source: src/mlx/gui/gui.py@ 832:3d075b6a0e08

Last change on this file since 832:3d075b6a0e08 was 832:3d075b6a0e08, checked in by István Váradi <ivaradi@…>, 7 years ago

Eliminated code duplication.

File size: 62.5 KB
RevLine 
[227]1# -*- coding: utf-8 -*-
[28]2
[29]3from statusicon import StatusIcon
[32]4from statusbar import Statusbar
[90]5from info import FlightInfo
[36]6from update import Updater
[29]7from mlx.gui.common import *
[42]8from mlx.gui.flight import Wizard
[77]9from mlx.gui.monitor import MonitorWindow
[117]10from mlx.gui.weighthelp import WeightHelp
[118]11from mlx.gui.gates import FleetGateStatus
[123]12from mlx.gui.prefs import Preferences
[172]13from mlx.gui.checklist import ChecklistEditor
[264]14from mlx.gui.callouts import ApproachCalloutsEditor
[220]15from mlx.gui.pirep import PIREPViewer
[483]16from mlx.gui.bugreport import BugReportDialog
[650]17from mlx.gui.acars import ACARS
18import cef
[28]19
20import mlx.const as const
21import mlx.fs as fs
22import mlx.flight as flight
23import mlx.logger as logger
24import mlx.acft as acft
[41]25import mlx.web as web
[182]26import mlx.singleton as singleton
[261]27import mlx.airports as airports
[227]28from mlx.i18n import xstr, getLanguage
[151]29from mlx.pirep import PIREP
[28]30
31import time
[38]32import threading
33import sys
[203]34import datetime
[227]35import webbrowser
[28]36
[77]37#------------------------------------------------------------------------------
38
[300]39## @package mlx.gui.gui
40#
41# The main GUI class.
42#
43# The \ref GUI class is the main class of the GUI. It is a connection listener,
44# and aggregates all the windows, the menu, etc. It maintains the connection to
45# the simulator as well as the flight object.
46
47#------------------------------------------------------------------------------
48
[28]49class GUI(fs.ConnectionListener):
50 """The main GUI class."""
[276]51 _authors = [ (u"Váradi", u"István", "prog_test"),
52 (u"Galyassy", u"Tamás", "negotiation"),
53 (u"Kurják", u"Ákos", "test"),
54 (u"Nagy", u"Dániel", "test"),
[393]55 (u"Radó", u"Iván", "test"),
56 (u"Petrovszki", u"Gábor", "test"),
57 (u"Serfőző", u"Tamás", "test"),
58 (u"Szebenyi", u"Bálint", "test"),
59 (u"Zsebényi-Loksa", u"Gergely", "test") ]
[227]60
[36]61 def __init__(self, programDirectory, config):
[28]62 """Construct the GUI."""
63 gobject.threads_init()
64
[36]65 self._programDirectory = programDirectory
[42]66 self.config = config
[28]67 self._connecting = False
[59]68 self._reconnecting = False
[28]69 self._connected = False
[96]70 self._logger = logger.Logger(self)
[28]71 self._flight = None
72 self._simulator = None
[430]73 self._fsType = None
[59]74 self._monitoring = False
[130]75
[119]76 self._fleet = None
[130]77
[119]78 self._fleetCallback = None
[38]79
[130]80 self._updatePlaneCallback = None
81 self._updatePlaneTailNumber = None
82 self._updatePlaneStatus = None
83 self._updatePlaneGateNumber = None
84
[38]85 self._stdioLock = threading.Lock()
86 self._stdioText = ""
[203]87 self._stdioStartingLine = True
[28]88
[151]89 self._sendPIREPCallback = None
[484]90 self._sendBugReportCallback = None
[151]91
[744]92 self._credentialsCondition = threading.Condition()
93 self._credentialsAvailable = False
94 self._credentialsUserName = None
95 self._credentialsPassword = None
96
[746]97 self.webHandler = web.Handler(config, self._getCredentialsCallback)
[41]98 self.webHandler.start()
99
[38]100 self.toRestart = False
101
[29]102 def build(self, iconDirectory):
[28]103 """Build the GUI."""
[345]104
[123]105 self._mainWindow = window = gtk.Window()
[105]106 window.set_title(WINDOW_TITLE_BASE)
[36]107 window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
[202]108 window.set_resizable(False)
[249]109 window.connect("delete-event", self.deleteMainWindow)
[36]110 window.connect("window-state-event", self._handleMainWindowState)
[650]111 if os.name=="nt":
112 window.connect("leave-notify-event", self._handleLeaveNotify)
[93]113 accelGroup = gtk.AccelGroup()
114 window.add_accel_group(accelGroup)
[650]115 window.realize()
[28]116
117 mainVBox = gtk.VBox()
[36]118 window.add(mainVBox)
[28]119
[123]120 self._preferences = Preferences(self)
[172]121 self._checklistEditor = ChecklistEditor(self)
[264]122 self._approachCalloutsEditor = ApproachCalloutsEditor(self)
[484]123 self._bugReportDialog = BugReportDialog(self)
[123]124
[93]125 menuBar = self._buildMenuBar(accelGroup)
126 mainVBox.pack_start(menuBar, False, False, 0)
127
[92]128 self._notebook = gtk.Notebook()
[93]129 mainVBox.pack_start(self._notebook, True, True, 4)
[345]130
[46]131 self._wizard = Wizard(self)
[107]132 label = gtk.Label(xstr("tab_flight"))
[46]133 label.set_use_underline(True)
[108]134 label.set_tooltip_text(xstr("tab_flight_tooltip"))
[92]135 self._notebook.append_page(self._wizard, label)
[42]136
[90]137 self._flightInfo = FlightInfo(self)
[107]138 label = gtk.Label(xstr("tab_flight_info"))
[90]139 label.set_use_underline(True)
[108]140 label.set_tooltip_text(xstr("tab_flight_info_tooltip"))
[92]141 self._notebook.append_page(self._flightInfo, label)
[93]142 self._flightInfo.disable()
[90]143
[117]144 self._weightHelp = WeightHelp(self)
145 label = gtk.Label(xstr("tab_weight_help"))
146 label.set_use_underline(True)
147 label.set_tooltip_text(xstr("tab_weight_help_tooltip"))
148 self._notebook.append_page(self._weightHelp, label)
[345]149
[93]150 (logWidget, self._logView) = self._buildLogWidget()
[345]151 addFaultTag(self._logView.get_buffer())
[107]152 label = gtk.Label(xstr("tab_log"))
[46]153 label.set_use_underline(True)
[108]154 label.set_tooltip_text(xstr("tab_log_tooltip"))
[93]155 self._notebook.append_page(logWidget, label)
156
[118]157 self._fleetGateStatus = FleetGateStatus(self)
158 label = gtk.Label(xstr("tab_gates"))
159 label.set_use_underline(True)
160 label.set_tooltip_text(xstr("tab_gates_tooltip"))
161 self._notebook.append_page(self._fleetGateStatus, label)
[345]162
[650]163 self._acars = ACARS(self)
164 label = gtk.Label("ACARS")
165 label.set_use_underline(True)
166 self._notebook.append_page(self._acars, label)
167
[93]168 (self._debugLogWidget, self._debugLogView) = self._buildLogWidget()
169 self._debugLogWidget.show_all()
[32]170
171 mainVBox.pack_start(gtk.HSeparator(), False, False, 0)
172
[281]173 self._statusbar = Statusbar(iconDirectory)
[32]174 mainVBox.pack_start(self._statusbar, False, False, 0)
175
[92]176 self._notebook.connect("switch-page", self._notebookPageSwitch)
[46]177
[77]178 self._monitorWindow = MonitorWindow(self, iconDirectory)
[93]179 self._monitorWindow.add_accel_group(accelGroup)
[77]180 self._monitorWindowX = None
181 self._monitorWindowY = None
[93]182 self._selfToggling = False
[77]183
[220]184 self._pirepViewer = PIREPViewer(self)
185
[46]186 window.show_all()
[650]187 if os.name=="nt":
188 window.get_window().focus()
[620]189
[46]190 self._wizard.grabDefault()
[117]191 self._weightHelp.reset()
192 self._weightHelp.disable()
[28]193
[29]194 self._statusIcon = StatusIcon(iconDirectory, self)
[28]195
[49]196 self._busyCursor = gdk.Cursor(gdk.CursorType.WATCH if pygobject
197 else gdk.WATCH)
198
[151]199 self._loadPIREPDialog = None
200 self._lastLoadedPIREP = None
201
[168]202 self._hotkeySetID = None
[178]203 self._pilotHotkeyIndex = None
204 self._checklistHotkeyIndex = None
[168]205
[227]206 self._aboutDialog = None
207
[59]208 @property
[105]209 def mainWindow(self):
210 """Get the main window of the GUI."""
211 return self._mainWindow
[345]212
[105]213 @property
[97]214 def logger(self):
215 """Get the logger used by us."""
216 return self._logger
[345]217
[97]218 @property
[59]219 def simulator(self):
220 """Get the simulator used by us."""
221 return self._simulator
[345]222
[71]223 @property
224 def flight(self):
225 """Get the flight being performed."""
226 return self._flight
[84]227
228 @property
[430]229 def fsType(self):
230 """Get the flight simulator type."""
231 return self._fsType
232
233 @property
[184]234 def entranceExam(self):
235 """Get whether an entrance exam is about to be taken."""
236 return self._wizard.entranceExam
[215]237
238 @property
239 def loggedIn(self):
240 """Indicate if the user has logged in properly."""
241 return self._wizard.loggedIn
[345]242
[184]243 @property
[139]244 def loginResult(self):
245 """Get the result of the login."""
246 return self._wizard.loginResult
247
248 @property
[97]249 def bookedFlight(self):
250 """Get the booked flight selected, if any."""
251 return self._wizard.bookedFlight
252
253 @property
[303]254 def numCrew(self):
255 """Get the number of crew members."""
256 return self._wizard.numCrew
257
258 @property
259 def numPassengers(self):
260 """Get the number of passengers."""
261 return self._wizard.numPassengers
262
263 @property
264 def bagWeight(self):
265 """Get the bag weight."""
266 return self._wizard.bagWeight
267
268 @property
[97]269 def cargoWeight(self):
270 """Get the cargo weight."""
271 return self._wizard.cargoWeight
272
273 @property
[303]274 def mailWeight(self):
275 """Get the mail weight."""
276 return self._wizard.mailWeight
277
278 @property
[84]279 def zfw(self):
280 """Get Zero-Fuel Weight calculated for the current flight."""
281 return self._wizard.zfw
[345]282
[84]283 @property
[97]284 def filedCruiseAltitude(self):
285 """Get cruise altitude filed for the current flight."""
286 return self._wizard.filedCruiseAltitude
[345]287
[97]288 @property
[84]289 def cruiseAltitude(self):
[97]290 """Get cruise altitude set for the current flight."""
[84]291 return self._wizard.cruiseAltitude
[97]292
293 @property
[383]294 def loggableCruiseAltitude(self):
295 """Get the cruise altitude that can be logged."""
296 return self._wizard.loggableCruiseAltitude
297
298 @property
[97]299 def route(self):
300 """Get the flight route."""
301 return self._wizard.route
302
303 @property
304 def departureMETAR(self):
305 """Get the METAR of the deprature airport."""
306 return self._wizard.departureMETAR
[345]307
[84]308 @property
[97]309 def arrivalMETAR(self):
310 """Get the METAR of the deprature airport."""
311 return self._wizard.arrivalMETAR
312
313 @property
314 def departureRunway(self):
315 """Get the name of the departure runway."""
316 return self._wizard.departureRunway
[345]317
[97]318 @property
319 def sid(self):
320 """Get the SID."""
321 return self._wizard.sid
322
323 @property
[84]324 def v1(self):
325 """Get the V1 speed calculated for the flight."""
326 return self._wizard.v1
[345]327
[84]328 @property
329 def vr(self):
330 """Get the Vr speed calculated for the flight."""
331 return self._wizard.vr
[345]332
[84]333 @property
334 def v2(self):
335 """Get the V2 speed calculated for the flight."""
336 return self._wizard.v2
[345]337
[86]338 @property
[384]339 def derate(self):
340 """Get the derate value calculated for the flight."""
341 return self._wizard.derate
342
343 @property
[391]344 def takeoffAntiIceOn(self):
345 """Get whether the anti-ice system was on during take-off."""
346 return self._wizard.takeoffAntiIceOn
347
348 @takeoffAntiIceOn.setter
349 def takeoffAntiIceOn(self, value):
350 """Set the anti-ice on indicator."""
351 gobject.idle_add(self._setTakeoffAntiIceOn, value)
352
353 @property
[349]354 def rtoIndicated(self):
355 """Get whether the pilot has indicated than an RTO has occured."""
356 return self._wizard.rtoIndicated
357
358 @property
[97]359 def arrivalRunway(self):
360 """Get the arrival runway."""
361 return self._wizard.arrivalRunway
362
363 @property
364 def star(self):
365 """Get the STAR."""
366 return self._wizard.star
367
368 @property
369 def transition(self):
370 """Get the transition."""
371 return self._wizard.transition
372
373 @property
374 def approachType(self):
375 """Get the approach type."""
376 return self._wizard.approachType
377
378 @property
[86]379 def vref(self):
380 """Get the Vref speed calculated for the flight."""
381 return self._wizard.vref
[345]382
[97]383 @property
[391]384 def landingAntiIceOn(self):
385 """Get whether the anti-ice system was on during landing."""
386 return self._wizard.landingAntiIceOn
387
388 @landingAntiIceOn.setter
389 def landingAntiIceOn(self, value):
390 """Set the anti-ice on indicator."""
391 gobject.idle_add(self._setLandingAntiIceOn, value)
392
393 @property
[97]394 def flightType(self):
395 """Get the flight type."""
396 return self._wizard.flightType
397
398 @property
399 def online(self):
400 """Get whether the flight was online or not."""
401 return self._wizard.online
402
403 @property
404 def comments(self):
405 """Get the comments."""
406 return self._flightInfo.comments
407
408 @property
[349]409 def hasComments(self):
410 """Indicate whether there is a comment."""
411 return self._flightInfo.hasComments
412
413 @property
[97]414 def flightDefects(self):
415 """Get the flight defects."""
[604]416 return self._flightInfo.faultsAndExplanations
[97]417
[99]418 @property
419 def delayCodes(self):
420 """Get the delay codes."""
421 return self._flightInfo.delayCodes
422
[555]423 @property
424 def hasDelayCode(self):
425 """Determine if there is at least one delay code selected."""
426 return self._flightInfo.hasDelayCode
427
[605]428 @property
429 def faultsFullyExplained(self):
430 """Determine if all the faults have been fully explained by the
431 user."""
432 return self._flightInfo.faultsFullyExplained
433
[620]434 if pygobject:
435 @property
436 def backgroundColour(self):
437 """Get the background colour of the main window."""
438 return self._mainWindow.get_style_context().\
439 get_background_color(gtk.StateFlags.NORMAL)
440
[28]441 def run(self):
442 """Run the GUI."""
[42]443 if self.config.autoUpdate:
[38]444 self._updater = Updater(self,
445 self._programDirectory,
[733]446 self.config.updateURL,
[36]447 self._mainWindow)
448 self._updater.start()
[755]449 else:
450 self.updateDone()
[345]451
[805]452 cef.initialize(self._cefInitialized)
[650]453
[182]454 singleton.raiseCallback = self.raiseCallback
[28]455 gtk.main()
[182]456 singleton.raiseCallback = None
[36]457
[650]458 cef.finalize()
459
[91]460 self._disconnect()
[28]461
[755]462 def updateDone(self):
463 """Called when the update is done (and there is no need to restart)."""
464 gobject.idle_add(self._updateDone)
465
[28]466 def connected(self, fsType, descriptor):
467 """Called when we have connected to the simulator."""
468 self._connected = True
[241]469 self._logger.untimedMessage("MLX %s connected to the simulator %s" % \
470 (const.VERSION, descriptor))
[133]471 fs.sendMessage(const.MESSAGETYPE_INFORMATION,
472 "Welcome to MAVA Logger X " + const.VERSION)
[59]473 gobject.idle_add(self._handleConnected, fsType, descriptor)
474
475 def _handleConnected(self, fsType, descriptor):
476 """Called when the connection to the simulator has succeeded."""
477 self._statusbar.updateConnection(self._connecting, self._connected)
478 self.endBusy()
479 if not self._reconnecting:
480 self._wizard.connected(fsType, descriptor)
481 self._reconnecting = False
[430]482 self._fsType = fsType
[168]483 self._listenHotkeys()
[59]484
485 def connectionFailed(self):
486 """Called when the connection failed."""
487 self._logger.untimedMessage("Connection to the simulator failed")
488 gobject.idle_add(self._connectionFailed)
489
490 def _connectionFailed(self):
491 """Called when the connection failed."""
492 self.endBusy()
493 self._statusbar.updateConnection(self._connecting, self._connected)
494
[105]495 dialog = gtk.MessageDialog(parent = self._mainWindow,
496 type = MESSAGETYPE_ERROR,
[108]497 message_format = xstr("conn_failed"))
[345]498
[105]499 dialog.set_title(WINDOW_TITLE_BASE)
[108]500 dialog.format_secondary_markup(xstr("conn_failed_sec"))
[345]501
[108]502 dialog.add_button(xstr("button_cancel"), 0)
503 dialog.add_button(xstr("button_tryagain"), 1)
[59]504 dialog.set_default_response(1)
[345]505
[59]506 result = dialog.run()
507 dialog.hide()
508 if result == 1:
[107]509 self.beginBusy(xstr("connect_busy"))
[59]510 self._simulator.reconnect()
511 else:
[91]512 self.reset()
[345]513
[28]514 def disconnected(self):
515 """Called when we have disconnected from the simulator."""
516 self._connected = False
517 self._logger.untimedMessage("Disconnected from the simulator")
[609]518 if self._flight is not None:
519 self._flight.disconnected()
[59]520
521 gobject.idle_add(self._disconnected)
522
523 def _disconnected(self):
[345]524 """Called when we have disconnected from the simulator unexpectedly."""
[59]525 self._statusbar.updateConnection(self._connecting, self._connected)
526
527 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
[108]528 message_format = xstr("conn_broken"),
[59]529 parent = self._mainWindow)
[105]530 dialog.set_title(WINDOW_TITLE_BASE)
[108]531 dialog.format_secondary_markup(xstr("conn_broken_sec"))
[59]532
[108]533 dialog.add_button(xstr("button_cancel"), 0)
534 dialog.add_button(xstr("button_reconnect"), 1)
[59]535 dialog.set_default_response(1)
536
537 result = dialog.run()
538 dialog.hide()
539 if result == 1:
[108]540 self.beginBusy(xstr("connect_busy"))
[59]541 self._reconnecting = True
542 self._simulator.reconnect()
543 else:
[91]544 self.reset()
545
[436]546 def enableFlightInfo(self, aircraftType):
[93]547 """Enable the flight info tab."""
[436]548 self._flightInfo.enable(aircraftType)
[93]549
[208]550 def cancelFlight(self):
551 """Cancel the current file, if the user confirms it."""
552 dialog = gtk.MessageDialog(parent = self._mainWindow,
553 type = MESSAGETYPE_QUESTION,
554 message_format = xstr("cancelFlight_question"))
555
556 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
557 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
558
559 dialog.set_title(WINDOW_TITLE_BASE)
560 result = dialog.run()
561 dialog.hide()
[345]562
[208]563 if result==RESPONSETYPE_YES:
564 self.reset()
565
[91]566 def reset(self):
567 """Reset the GUI."""
568 self._disconnect()
[92]569
[501]570 self._simulator = None
571
[91]572 self._flightInfo.reset()
[93]573 self._flightInfo.disable()
[91]574 self.resetFlightStatus()
[28]575
[117]576 self._weightHelp.reset()
577 self._weightHelp.disable()
[92]578 self._notebook.set_current_page(0)
579
580 self._logView.get_buffer().set_text("")
581
[215]582 if self.loggedIn:
[769]583 self._wizard.cancelFlight(self._handleReloadResult)
[215]584 else:
585 self._wizard.reset(None)
[208]586
587 def _handleReloadResult(self, returned, result):
588 """Handle the result of the reloading of the flights."""
589 self._wizard.reset(result if returned and result.loggedIn else None)
590
[152]591 def _disconnect(self, closingMessage = None, duration = 3):
[91]592 """Disconnect from the simulator if connected."""
[92]593 self.stopMonitoring()
[168]594 self._clearHotkeys()
[92]595
[91]596 if self._connected:
[152]597 if closingMessage is None:
598 self._flight.simulator.disconnect()
599 else:
600 fs.sendMessage(const.MESSAGETYPE_ENVIRONMENT,
601 closingMessage, duration,
602 disconnect = True)
[91]603 self._connected = False
604
605 self._connecting = False
606 self._reconnecting = False
607 self._statusbar.updateConnection(False, False)
[128]608 self._weightHelp.disable()
[134]609
610 return True
[345]611
612 def insertFlightLogLine(self, index, timestampString, text, isFault):
613 """Insert the flight log line with the given data."""
614 gobject.idle_add(self._insertFlightLogLine, index,
615 formatFlightLogLine(timestampString, text),
616 isFault)
617
618 def _insertFlightLogLine(self, index, line, isFault):
619 """Perform the real insertion.
[96]620
[345]621 To be called from the event loop."""
622 buffer = self._logView.get_buffer()
623 lineIter = buffer.get_iter_at_line(index)
624 insertTextBuffer(buffer, lineIter, line, isFault = isFault)
625 self._logView.scroll_mark_onscreen(buffer.get_insert())
[96]626
[345]627 def removeFlightLogLine(self, index):
628 """Remove the flight log line with the given index."""
629 gobject.idle_add(self._removeFlightLogLine, index)
630
[604]631 def addFault(self, id, timestampString, text):
632 """Add a fault to the list of faults."""
633 faultText = formatFlightLogLine(timestampString, text).strip()
[606]634 gobject.idle_add(self._flightInfo.addFault, id, faultText)
[604]635
636 def updateFault(self, id, timestampString, text):
637 """Update a fault in the list of faults."""
638 faultText = formatFlightLogLine(timestampString, text).strip()
[606]639 gobject.idle_add(self._flightInfo.updateFault, id, faultText)
[604]640
641 def clearFault(self, id):
642 """Clear a fault in the list of faults."""
[606]643 gobject.idle_add(self._flightInfo.clearFault, id)
[604]644
[345]645 def _removeFlightLogLine(self, index):
646 """Perform the real removal."""
[96]647 buffer = self._logView.get_buffer()
648 startIter = buffer.get_iter_at_line(index)
[345]649 endIter = buffer.get_iter_at_line(index+1)
[96]650 buffer.delete(startIter, endIter)
651 self._logView.scroll_mark_onscreen(buffer.get_insert())
652
[28]653 def check(self, flight, aircraft, logger, oldState, state):
654 """Update the data."""
[77]655 gobject.idle_add(self._monitorWindow.setData, state)
[80]656 gobject.idle_add(self._statusbar.updateTime, state.timestamp)
[28]657
[31]658 def resetFlightStatus(self):
659 """Reset the status of the flight."""
[32]660 self._statusbar.resetFlightStatus()
[80]661 self._statusbar.updateTime()
[31]662 self._statusIcon.resetFlightStatus()
663
664 def setStage(self, stage):
665 """Set the stage of the flight."""
666 gobject.idle_add(self._setStage, stage)
667
668 def _setStage(self, stage):
669 """Set the stage of the flight."""
[32]670 self._statusbar.setStage(stage)
[31]671 self._statusIcon.setStage(stage)
[84]672 self._wizard.setStage(stage)
[88]673 if stage==const.STAGE_END:
[261]674 welcomeMessage = \
675 airports.getWelcomeMessage(self.bookedFlight.arrivalICAO)
[152]676 self._disconnect(closingMessage =
[261]677 "Flight plan closed. " + welcomeMessage,
[152]678 duration = 5)
[31]679
680 def setRating(self, rating):
681 """Set the rating of the flight."""
682 gobject.idle_add(self._setRating, rating)
683
684 def _setRating(self, rating):
685 """Set the rating of the flight."""
[32]686 self._statusbar.setRating(rating)
[31]687 self._statusIcon.setRating(rating)
688
689 def setNoGo(self, reason):
690 """Set the rating of the flight to No-Go with the given reason."""
691 gobject.idle_add(self._setNoGo, reason)
692
693 def _setNoGo(self, reason):
694 """Set the rating of the flight."""
[32]695 self._statusbar.setNoGo(reason)
[31]696 self._statusIcon.setNoGo(reason)
697
[29]698 def _handleMainWindowState(self, window, event):
699 """Hande a change in the state of the window"""
700 iconified = gdk.WindowState.ICONIFIED if pygobject \
701 else gdk.WINDOW_STATE_ICONIFIED
[246]702
703 if (event.changed_mask&WINDOW_STATE_WITHDRAWN)!=0:
704 if (event.new_window_state&WINDOW_STATE_WITHDRAWN)!=0:
705 self._statusIcon.mainWindowHidden()
706 else:
707 self._statusIcon.mainWindowShown()
708
709 if self.config.hideMinimizedWindow and not pygobject and \
710 (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
711 (event.new_window_state&WINDOW_STATE_ICONIFIED)!=0:
[35]712 self.hideMainWindow(savePosition = False)
[246]713 elif (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
714 (event.new_window_state&WINDOW_STATE_ICONIFIED)==0:
715 self._mainWindow.present()
[345]716
[650]717 def _handleLeaveNotify(self, widget, event):
718 """Handle the leave-notify event.
719
720 Here we reset the focus to the main window as CEF might have acquired
721 it earlier."""
722 self._mainWindow.get_window().focus()
723
[182]724 def raiseCallback(self):
725 """Callback for the singleton handling code."""
726 gobject.idle_add(self.raiseMainWindow)
727
728 def raiseMainWindow(self):
[246]729 """Show the main window if invisible, and raise it."""
[182]730 if not self._mainWindow.get_visible():
731 self.showMainWindow()
732 self._mainWindow.present()
733
[249]734 def deleteMainWindow(self, window, event):
735 """Handle the delete event for the main window."""
736 if self.config.quitOnClose:
737 self._quit()
738 else:
739 self.hideMainWindow()
740 return True
741
[35]742 def hideMainWindow(self, savePosition = True):
[29]743 """Hide the main window and save its position."""
[35]744 if savePosition:
745 (self._mainWindowX, self._mainWindowY) = \
746 self._mainWindow.get_window().get_root_origin()
747 else:
748 self._mainWindowX = self._mainWindowY = None
[29]749 self._mainWindow.hide()
750 return True
751
752 def showMainWindow(self):
753 """Show the main window at its former position."""
[35]754 if self._mainWindowX is not None and self._mainWindowY is not None:
755 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
756
[246]757 if pygobject:
758 self._mainWindow.show()
759 else:
760 self._mainWindow.present()
[29]761 self._mainWindow.deiconify()
[345]762
[29]763 def toggleMainWindow(self):
764 """Toggle the main window."""
765 if self._mainWindow.get_visible():
766 self.hideMainWindow()
767 else:
768 self.showMainWindow()
[36]769
[77]770 def hideMonitorWindow(self, savePosition = True):
771 """Hide the monitor window."""
772 if savePosition:
773 (self._monitorWindowX, self._monitorWindowY) = \
774 self._monitorWindow.get_window().get_root_origin()
775 else:
776 self._monitorWindowX = self._monitorWindowY = None
777 self._monitorWindow.hide()
778 self._statusIcon.monitorWindowHidden()
[93]779 if self._showMonitorMenuItem.get_active():
780 self._selfToggling = True
781 self._showMonitorMenuItem.set_active(False)
[77]782 return True
783
784 def showMonitorWindow(self):
785 """Show the monitor window."""
786 if self._monitorWindowX is not None and self._monitorWindowY is not None:
787 self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
788 self._monitorWindow.show_all()
789 self._statusIcon.monitorWindowShown()
[93]790 if not self._showMonitorMenuItem.get_active():
791 self._selfToggling = True
792 self._showMonitorMenuItem.set_active(True)
793
794 def _toggleMonitorWindow(self, menuItem):
795 if self._selfToggling:
796 self._selfToggling = False
797 elif self._monitorWindow.get_visible():
798 self.hideMonitorWindow()
799 else:
800 self.showMonitorWindow()
[77]801
[38]802 def restart(self):
803 """Quit and restart the application."""
804 self.toRestart = True
[81]805 self._quit(force = True)
[38]806
807 def flushStdIO(self):
808 """Flush any text to the standard error that could not be logged."""
809 if self._stdioText:
810 sys.__stderr__.write(self._stdioText)
[345]811
[36]812 def writeStdIO(self, text):
813 """Write the given text into standard I/O log."""
[38]814 with self._stdioLock:
815 self._stdioText += text
816
817 gobject.idle_add(self._writeStdIO)
[36]818
[49]819 def beginBusy(self, message):
820 """Begin a period of background processing."""
[93]821 self._wizard.set_sensitive(False)
[117]822 self._weightHelp.set_sensitive(False)
[49]823 self._mainWindow.get_window().set_cursor(self._busyCursor)
824 self._statusbar.updateBusyState(message)
825
[686]826 def updateBusyState(self, message):
827 """Update the busy state."""
828 self._statusbar.updateBusyState(message)
829
[49]830 def endBusy(self):
831 """End a period of background processing."""
832 self._mainWindow.get_window().set_cursor(None)
[117]833 self._weightHelp.set_sensitive(True)
[93]834 self._wizard.set_sensitive(True)
[49]835 self._statusbar.updateBusyState(None)
836
[117]837 def initializeWeightHelp(self):
838 """Initialize the weight help tab."""
839 self._weightHelp.reset()
840 self._weightHelp.enable()
841
[134]842 def getFleetAsync(self, callback = None, force = None):
843 """Get the fleet asynchronously."""
844 gobject.idle_add(self.getFleet, callback, force)
845
[119]846 def getFleet(self, callback = None, force = False):
847 """Get the fleet.
848
849 If force is False, and we already have a fleet retrieved,
850 that one will be used."""
851 if self._fleet is None or force:
852 self._fleetCallback = callback
853 self.beginBusy(xstr("fleet_busy"))
854 self.webHandler.getFleet(self._fleetResultCallback)
855 else:
856 callback(self._fleet)
857
[555]858 def commentsChanged(self):
859 """Indicate that the comments have changed."""
860 self._wizard.commentsChanged()
861
862 def delayCodesChanged(self):
863 """Called when the delay codes have changed."""
864 self._wizard.delayCodesChanged()
865
[605]866 def faultExplanationsChanged(self):
867 """Called when the status of the explanations of the faults have
868 changed."""
869 self._wizard.faultExplanationsChanged()
870
[349]871 def updateRTO(self, inLoop = False):
872 """Indicate that the RTO state should be updated."""
873 if inLoop:
874 self._wizard.updateRTO()
875 else:
876 gobject.idle_add(self.updateRTO, True)
877
878 def rtoToggled(self, indicated):
879 """Called when the user has toggled the RTO checkbox."""
880 self._flight.rtoToggled(indicated)
881
[119]882 def _fleetResultCallback(self, returned, result):
883 """Called when the fleet has been queried."""
884 gobject.idle_add(self._handleFleetResult, returned, result)
885
886 def _handleFleetResult(self, returned, result):
887 """Handle the fleet result."""
888 self.endBusy()
889 if returned:
890 self._fleet = result.fleet
891 else:
892 self._fleet = None
893
[120]894 dialog = gtk.MessageDialog(parent = self.mainWindow,
[119]895 type = MESSAGETYPE_ERROR,
896 message_format = xstr("fleet_failed"))
[130]897 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
898 dialog.set_title(WINDOW_TITLE_BASE)
899 dialog.run()
900 dialog.hide()
901
902 callback = self._fleetCallback
903 self._fleetCallback = None
904 if callback is not None:
905 callback(self._fleet)
906 self._fleetGateStatus.handleFleet(self._fleet)
907
908 def updatePlane(self, tailNumber, status,
909 gateNumber = None, callback = None):
910 """Update the status of the given plane."""
911 self.beginBusy(xstr("fleet_update_busy"))
912
913 self._updatePlaneCallback = callback
914
915 self._updatePlaneTailNumber = tailNumber
916 self._updatePlaneStatus = status
917 self._updatePlaneGateNumber = gateNumber
[345]918
[130]919 self.webHandler.updatePlane(self._updatePlaneResultCallback,
920 tailNumber, status, gateNumber)
921
922 def _updatePlaneResultCallback(self, returned, result):
923 """Called when the status of a plane has been updated."""
924 gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
925
926 def _handleUpdatePlaneResult(self, returned, result):
927 """Handle the plane update result."""
928 self.endBusy()
929 if returned:
930 success = result.success
931 if success:
932 if self._fleet is not None:
933 self._fleet.updatePlane(self._updatePlaneTailNumber,
934 self._updatePlaneStatus,
935 self._updatePlaneGateNumber)
936 self._fleetGateStatus.handleFleet(self._fleet)
937 else:
938 dialog = gtk.MessageDialog(parent = self.mainWindow,
939 type = MESSAGETYPE_ERROR,
940 message_format = xstr("fleet_update_failed"))
[124]941 dialog.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
[119]942 dialog.set_title(WINDOW_TITLE_BASE)
943 dialog.run()
944 dialog.hide()
945
[130]946 success = None
947
948 callback = self._updatePlaneCallback
949 self._updatePlaneCallback = None
950 if callback is not None:
951 callback(success)
[119]952
[38]953 def _writeStdIO(self):
[36]954 """Perform the real writing."""
[38]955 with self._stdioLock:
956 text = self._stdioText
957 self._stdioText = ""
958 if not text: return
[345]959
[36]960 lines = text.splitlines()
961 if text[-1]=="\n":
962 text = ""
963 else:
964 text = lines[-1]
965 lines = lines[:-1]
[203]966
967 now = datetime.datetime.now()
968 timeStr = "%02d:%02d:%02d: " % (now.hour, now.minute, now.second)
[345]969
[36]970 for line in lines:
[96]971 #print >> sys.__stdout__, line
[203]972 if self._stdioStartingLine:
973 self._writeLog(timeStr, self._debugLogView)
[93]974 self._writeLog(line + "\n", self._debugLogView)
[203]975 self._stdioStartingLine = True
[36]976
977 if text:
[96]978 #print >> sys.__stdout__, text,
[203]979 if self._stdioStartingLine:
980 self._writeLog(timeStr, self._debugLogView)
[93]981 self._writeLog(text, self._debugLogView)
[203]982 self._stdioStartingLine = False
[51]983
[798]984 def connectSimulator(self, bookedFlight, simulatorType):
[59]985 """Connect to the simulator for the first time."""
986 self._logger.reset()
987
988 self._flight = flight.Flight(self._logger, self)
[131]989 self._flight.flareTimeFromFS = self.config.flareTimeFromFS
[798]990 self._flight.aircraftType = bookedFlight.aircraftType
991 self._flight.aircraft = acft.Aircraft.create(self._flight, bookedFlight)
[59]992 self._flight.aircraft._checkers.append(self)
[345]993
[59]994 if self._simulator is None:
[501]995 self._simulator = fs.createSimulator(simulatorType, self)
[133]996 fs.setupMessageSending(self.config, self._simulator)
[148]997 self._setupTimeSync()
[345]998
[59]999 self._flight.simulator = self._simulator
1000
[107]1001 self.beginBusy(xstr("connect_busy"))
[59]1002 self._statusbar.updateConnection(self._connecting, self._connected)
1003
1004 self._connecting = True
[345]1005 self._simulator.connect(self._flight.aircraft)
[59]1006
[70]1007 def startMonitoring(self):
1008 """Start monitoring."""
[88]1009 if not self._monitoring:
1010 self.simulator.startMonitoring()
1011 self._monitoring = True
[70]1012
1013 def stopMonitoring(self):
1014 """Stop monitoring."""
[88]1015 if self._monitoring:
1016 self.simulator.stopMonitoring()
1017 self._monitoring = False
[70]1018
[304]1019 def cruiseLevelChanged(self):
1020 """Called when the cruise level is changed in the flight wizard."""
1021 if self._flight is not None:
[383]1022 return self._flight.cruiseLevelChanged()
1023 else:
1024 return False
[304]1025
[93]1026 def _buildMenuBar(self, accelGroup):
1027 """Build the main menu bar."""
1028 menuBar = gtk.MenuBar()
[345]1029
[110]1030 fileMenuItem = gtk.MenuItem(xstr("menu_file"))
[93]1031 fileMenu = gtk.Menu()
1032 fileMenuItem.set_submenu(fileMenu)
1033 menuBar.append(fileMenuItem)
1034
[151]1035 loadPIREPMenuItem = gtk.ImageMenuItem(gtk.STOCK_OPEN)
1036 loadPIREPMenuItem.set_use_stock(True)
1037 loadPIREPMenuItem.set_label(xstr("menu_file_loadPIREP"))
1038 loadPIREPMenuItem.add_accelerator("activate", accelGroup,
1039 ord(xstr("menu_file_loadPIREP_key")),
1040 CONTROL_MASK, ACCEL_VISIBLE)
1041 loadPIREPMenuItem.connect("activate", self._loadPIREP)
1042 fileMenu.append(loadPIREPMenuItem)
1043
1044 fileMenu.append(gtk.SeparatorMenuItem())
1045
[93]1046 quitMenuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
1047 quitMenuItem.set_use_stock(True)
[110]1048 quitMenuItem.set_label(xstr("menu_file_quit"))
[93]1049 quitMenuItem.add_accelerator("activate", accelGroup,
[110]1050 ord(xstr("menu_file_quit_key")),
1051 CONTROL_MASK, ACCEL_VISIBLE)
[93]1052 quitMenuItem.connect("activate", self._quit)
1053 fileMenu.append(quitMenuItem)
1054
[123]1055 toolsMenuItem = gtk.MenuItem(xstr("menu_tools"))
1056 toolsMenu = gtk.Menu()
1057 toolsMenuItem.set_submenu(toolsMenu)
1058 menuBar.append(toolsMenuItem)
1059
[204]1060 checklistMenuItem = gtk.ImageMenuItem(gtk.STOCK_APPLY)
[172]1061 checklistMenuItem.set_use_stock(True)
1062 checklistMenuItem.set_label(xstr("menu_tools_chklst"))
1063 checklistMenuItem.add_accelerator("activate", accelGroup,
1064 ord(xstr("menu_tools_chklst_key")),
1065 CONTROL_MASK, ACCEL_VISIBLE)
1066 checklistMenuItem.connect("activate", self._editChecklist)
1067 toolsMenu.append(checklistMenuItem)
1068
[264]1069 approachCalloutsMenuItem = gtk.ImageMenuItem(gtk.STOCK_EDIT)
1070 approachCalloutsMenuItem.set_use_stock(True)
1071 approachCalloutsMenuItem.set_label(xstr("menu_tools_callouts"))
1072 approachCalloutsMenuItem.add_accelerator("activate", accelGroup,
1073 ord(xstr("menu_tools_callouts_key")),
1074 CONTROL_MASK, ACCEL_VISIBLE)
1075 approachCalloutsMenuItem.connect("activate", self._editApproachCallouts)
1076 toolsMenu.append(approachCalloutsMenuItem)
1077
[204]1078 prefsMenuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
[123]1079 prefsMenuItem.set_use_stock(True)
1080 prefsMenuItem.set_label(xstr("menu_tools_prefs"))
1081 prefsMenuItem.add_accelerator("activate", accelGroup,
1082 ord(xstr("menu_tools_prefs_key")),
1083 CONTROL_MASK, ACCEL_VISIBLE)
1084 prefsMenuItem.connect("activate", self._editPreferences)
1085 toolsMenu.append(prefsMenuItem)
[93]1086
[482]1087 toolsMenu.append(gtk.SeparatorMenuItem())
1088
1089 bugReportMenuItem = gtk.ImageMenuItem(gtk.STOCK_PASTE)
1090 bugReportMenuItem.set_use_stock(True)
1091 bugReportMenuItem.set_label(xstr("menu_tools_bugreport"))
1092 bugReportMenuItem.add_accelerator("activate", accelGroup,
1093 ord(xstr("menu_tools_bugreport_key")),
1094 CONTROL_MASK, ACCEL_VISIBLE)
1095 bugReportMenuItem.connect("activate", self._reportBug)
1096 toolsMenu.append(bugReportMenuItem)
1097
[110]1098 viewMenuItem = gtk.MenuItem(xstr("menu_view"))
[93]1099 viewMenu = gtk.Menu()
1100 viewMenuItem.set_submenu(viewMenu)
1101 menuBar.append(viewMenuItem)
[28]1102
[93]1103 self._showMonitorMenuItem = gtk.CheckMenuItem()
[110]1104 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
[93]1105 self._showMonitorMenuItem.set_use_underline(True)
1106 self._showMonitorMenuItem.set_active(False)
1107 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
[110]1108 ord(xstr("menu_view_monitor_key")),
1109 CONTROL_MASK, ACCEL_VISIBLE)
[93]1110 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
1111 viewMenu.append(self._showMonitorMenuItem)
1112
1113 showDebugMenuItem = gtk.CheckMenuItem()
[110]1114 showDebugMenuItem.set_label(xstr("menu_view_debug"))
[93]1115 showDebugMenuItem.set_use_underline(True)
1116 showDebugMenuItem.set_active(False)
1117 showDebugMenuItem.add_accelerator("activate", accelGroup,
[110]1118 ord(xstr("menu_view_debug_key")),
1119 CONTROL_MASK, ACCEL_VISIBLE)
[93]1120 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
1121 viewMenu.append(showDebugMenuItem)
[28]1122
[227]1123 helpMenuItem = gtk.MenuItem(xstr("menu_help"))
1124 helpMenu = gtk.Menu()
1125 helpMenuItem.set_submenu(helpMenu)
1126 menuBar.append(helpMenuItem)
1127
1128 manualMenuItem = gtk.ImageMenuItem(gtk.STOCK_HELP)
1129 manualMenuItem.set_use_stock(True)
1130 manualMenuItem.set_label(xstr("menu_help_manual"))
1131 manualMenuItem.add_accelerator("activate", accelGroup,
1132 ord(xstr("menu_help_manual_key")),
1133 CONTROL_MASK, ACCEL_VISIBLE)
1134 manualMenuItem.connect("activate", self._showManual)
1135 helpMenu.append(manualMenuItem)
1136
1137 helpMenu.append(gtk.SeparatorMenuItem())
[345]1138
[227]1139 aboutMenuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
1140 aboutMenuItem.set_use_stock(True)
1141 aboutMenuItem.set_label(xstr("menu_help_about"))
1142 aboutMenuItem.add_accelerator("activate", accelGroup,
1143 ord(xstr("menu_help_about_key")),
1144 CONTROL_MASK, ACCEL_VISIBLE)
1145 aboutMenuItem.connect("activate", self._showAbout)
1146 helpMenu.append(aboutMenuItem)
1147
[93]1148 return menuBar
[28]1149
[93]1150 def _toggleDebugLog(self, menuItem):
1151 """Toggle the debug log."""
1152 if menuItem.get_active():
[107]1153 label = gtk.Label(xstr("tab_debug_log"))
[93]1154 label.set_use_underline(True)
[345]1155 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
[93]1156 self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
1157 self._notebook.set_current_page(self._debugLogPage)
1158 else:
1159 self._notebook.remove_page(self._debugLogPage)
1160
1161 def _buildLogWidget(self):
1162 """Build the widget for the log."""
1163 alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
1164
1165 alignment.set_padding(padding_top = 8, padding_bottom = 8,
1166 padding_left = 16, padding_right = 16)
[28]1167
1168 logScroller = gtk.ScrolledWindow()
[93]1169 # FIXME: these should be constants in common
1170 logScroller.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1171 else gtk.POLICY_AUTOMATIC,
1172 gtk.PolicyType.AUTOMATIC if pygobject
1173 else gtk.POLICY_AUTOMATIC)
1174 logScroller.set_shadow_type(gtk.ShadowType.IN if pygobject
1175 else gtk.SHADOW_IN)
1176 logView = gtk.TextView()
1177 logView.set_editable(False)
[171]1178 logView.set_cursor_visible(False)
[93]1179 logScroller.add(logView)
[28]1180
1181 logBox = gtk.VBox()
1182 logBox.pack_start(logScroller, True, True, 0)
1183 logBox.set_size_request(-1, 200)
1184
[93]1185 alignment.add(logBox)
[28]1186
[93]1187 return (alignment, logView)
[28]1188
[226]1189 def _writeLog(self, msg, logView, isFault = False):
[28]1190 """Write the given message to the log."""
[93]1191 buffer = logView.get_buffer()
[226]1192 appendTextBuffer(buffer, msg, isFault = isFault)
[93]1193 logView.scroll_mark_onscreen(buffer.get_insert())
[28]1194
[81]1195 def _quit(self, what = None, force = False):
[38]1196 """Quit from the application."""
[81]1197 if force:
1198 result=RESPONSETYPE_YES
1199 else:
[105]1200 dialog = gtk.MessageDialog(parent = self._mainWindow,
1201 type = MESSAGETYPE_QUESTION,
[110]1202 message_format = xstr("quit_question"))
1203
[124]1204 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
1205 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
1206
[105]1207 dialog.set_title(WINDOW_TITLE_BASE)
[81]1208 result = dialog.run()
1209 dialog.hide()
[345]1210
[76]1211 if result==RESPONSETYPE_YES:
1212 self._statusIcon.destroy()
1213 return gtk.main_quit()
[38]1214
[46]1215 def _notebookPageSwitch(self, notebook, page, page_num):
1216 """Called when the current page of the notebook has changed."""
1217 if page_num==0:
[48]1218 gobject.idle_add(self._wizard.grabDefault)
[46]1219 else:
1220 self._mainWindow.set_default(None)
[123]1221
[555]1222 def isWizardActive(self):
1223 """Determine if the flight wizard is active."""
1224 return self._notebook.get_current_page()==0
1225
[172]1226 def _editChecklist(self, menuItem):
1227 """Callback for editing the checklists."""
1228 self._checklistEditor.run()
[345]1229
[264]1230 def _editApproachCallouts(self, menuItem):
1231 """Callback for editing the approach callouts."""
1232 self._approachCalloutsEditor.run()
[345]1233
[123]1234 def _editPreferences(self, menuItem):
1235 """Callback for editing the preferences."""
[168]1236 self._clearHotkeys()
[123]1237 self._preferences.run(self.config)
[148]1238 self._setupTimeSync()
[168]1239 self._listenHotkeys()
[148]1240
[482]1241 def _reportBug(self, menuItem):
1242 """Callback for reporting a bug."""
[484]1243 self._bugReportDialog.run()
[482]1244
[148]1245 def _setupTimeSync(self):
1246 """Enable or disable the simulator time synchronization based on the
1247 configuration."""
1248 simulator = self._simulator
1249 if simulator is not None:
1250 if self.config.syncFSTime:
1251 simulator.enableTimeSync()
1252 else:
1253 simulator.disableTimeSync()
[151]1254
[830]1255 def viewPIREP(self, pirep):
1256 """Display the PIREP viewer window with the given PIREP."""
1257 self._pirepViewer.setPIREP(pirep)
1258 self._pirepViewer.show_all()
1259 self._pirepViewer.run()
1260 self._pirepViewer.hide()
1261
1262 def editPIREP(self, pirep):
1263 """Display the PIREP editor window and allow editing the PIREP."""
1264 self.viewPIREP(pirep)
1265
[151]1266 def _loadPIREP(self, menuItem):
1267 """Load a PIREP for sending."""
1268 dialog = self._getLoadPirepDialog()
1269
1270 if self._lastLoadedPIREP:
1271 dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP))
1272 else:
1273 pirepDirectory = self.config.pirepDirectory
1274 if pirepDirectory is not None:
1275 dialog.set_current_folder(pirepDirectory)
[345]1276
[151]1277 result = dialog.run()
1278 dialog.hide()
1279
1280 if result==RESPONSETYPE_OK:
[164]1281 self._lastLoadedPIREP = text2unicode(dialog.get_filename())
[151]1282
1283 pirep = PIREP.load(self._lastLoadedPIREP)
1284 if pirep is None:
1285 dialog = gtk.MessageDialog(parent = self._mainWindow,
1286 type = MESSAGETYPE_ERROR,
1287 message_format = xstr("loadPIREP_failed"))
1288 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1289 dialog.set_title(WINDOW_TITLE_BASE)
1290 dialog.format_secondary_markup(xstr("loadPIREP_failed_sec"))
1291 dialog.run()
1292 dialog.hide()
1293 else:
1294 dialog = self._getSendLoadedDialog(pirep)
1295 dialog.show_all()
[239]1296 while True:
1297 result = dialog.run()
[151]1298
[239]1299 if result==RESPONSETYPE_OK:
1300 self.sendPIREP(pirep)
1301 elif result==1:
[832]1302 self.viewPIREP(pirep)
[239]1303 else:
1304 break
1305
1306 dialog.hide()
[151]1307
1308 def _getLoadPirepDialog(self):
1309 """Get the PIREP loading file chooser dialog.
1310
1311 If it is not created yet, it will be created."""
1312 if self._loadPIREPDialog is None:
1313 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
1314 xstr("loadPIREP_browser_title"),
1315 action = FILE_CHOOSER_ACTION_OPEN,
1316 buttons = (gtk.STOCK_CANCEL,
1317 RESPONSETYPE_CANCEL,
1318 gtk.STOCK_OK, RESPONSETYPE_OK),
1319 parent = self._mainWindow)
1320 dialog.set_modal(True)
[345]1321
[151]1322
1323 filter = gtk.FileFilter()
[184]1324 filter.set_name(xstr("file_filter_pireps"))
[151]1325 filter.add_pattern("*.pirep")
1326 dialog.add_filter(filter)
[345]1327
[151]1328 filter = gtk.FileFilter()
[184]1329 filter.set_name(xstr("file_filter_all"))
[151]1330 filter.add_pattern("*.*")
1331 dialog.add_filter(filter)
1332
1333 self._loadPIREPDialog = dialog
1334
1335 return self._loadPIREPDialog
1336
1337 def _getSendLoadedDialog(self, pirep):
1338 """Get a dialog displaying the main information of the flight from the
1339 PIREP and providing Cancel and Send buttons."""
1340 dialog = gtk.Dialog(title = WINDOW_TITLE_BASE + " - " +
1341 xstr("loadPIREP_send_title"),
1342 parent = self._mainWindow,
1343 flags = DIALOG_MODAL)
1344
1345 contentArea = dialog.get_content_area()
1346
1347 label = gtk.Label(xstr("loadPIREP_send_help"))
1348 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1349 xscale = 0.0, yscale = 0.0)
1350 alignment.set_padding(padding_top = 16, padding_bottom = 0,
1351 padding_left = 48, padding_right = 48)
1352 alignment.add(label)
1353 contentArea.pack_start(alignment, False, False, 8)
1354
1355 table = gtk.Table(5, 2)
1356 tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1357 xscale = 0.0, yscale = 0.0)
1358 tableAlignment.set_padding(padding_top = 0, padding_bottom = 32,
1359 padding_left = 48, padding_right = 48)
1360 table.set_row_spacings(4)
1361 table.set_col_spacings(16)
1362 tableAlignment.add(table)
1363 contentArea.pack_start(tableAlignment, True, True, 8)
1364
1365 bookedFlight = pirep.bookedFlight
1366
1367 label = gtk.Label("<b>" + xstr("loadPIREP_send_flightno") + "</b>")
1368 label.set_use_markup(True)
1369 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1370 xscale = 0.0, yscale = 0.0)
1371 labelAlignment.add(label)
1372 table.attach(labelAlignment, 0, 1, 0, 1)
[345]1373
[151]1374 label = gtk.Label(bookedFlight.callsign)
1375 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1376 xscale = 0.0, yscale = 0.0)
1377 labelAlignment.add(label)
1378 table.attach(labelAlignment, 1, 2, 0, 1)
1379
1380 label = gtk.Label("<b>" + xstr("loadPIREP_send_date") + "</b>")
1381 label.set_use_markup(True)
1382 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1383 xscale = 0.0, yscale = 0.0)
1384 labelAlignment.add(label)
1385 table.attach(labelAlignment, 0, 1, 1, 2)
[345]1386
[151]1387 label = gtk.Label(str(bookedFlight.departureTime.date()))
1388 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1389 xscale = 0.0, yscale = 0.0)
1390 labelAlignment.add(label)
1391 table.attach(labelAlignment, 1, 2, 1, 2)
1392
1393 label = gtk.Label("<b>" + xstr("loadPIREP_send_from") + "</b>")
1394 label.set_use_markup(True)
1395 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1396 xscale = 0.0, yscale = 0.0)
1397 labelAlignment.add(label)
1398 table.attach(labelAlignment, 0, 1, 2, 3)
[345]1399
[151]1400 label = gtk.Label(bookedFlight.departureICAO)
1401 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1402 xscale = 0.0, yscale = 0.0)
1403 labelAlignment.add(label)
1404 table.attach(labelAlignment, 1, 2, 2, 3)
1405
1406 label = gtk.Label("<b>" + xstr("loadPIREP_send_to") + "</b>")
1407 label.set_use_markup(True)
1408 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1409 xscale = 0.0, yscale = 0.0)
1410 labelAlignment.add(label)
1411 table.attach(labelAlignment, 0, 1, 3, 4)
[345]1412
[151]1413 label = gtk.Label(bookedFlight.arrivalICAO)
1414 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1415 xscale = 0.0, yscale = 0.0)
1416 labelAlignment.add(label)
1417 table.attach(labelAlignment, 1, 2, 3, 4)
1418
1419 label = gtk.Label("<b>" + xstr("loadPIREP_send_rating") + "</b>")
1420 label.set_use_markup(True)
1421 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1422 xscale = 0.0, yscale = 0.0)
1423 labelAlignment.add(label)
1424 table.attach(labelAlignment, 0, 1, 4, 5)
1425
1426 rating = pirep.rating
1427 label = gtk.Label()
1428 if rating<0:
1429 label.set_markup('<b><span foreground="red">NO GO</span></b>')
1430 else:
1431 label.set_text("%.1f %%" % (rating,))
[345]1432
[151]1433 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1434 xscale = 0.0, yscale = 0.0)
1435 labelAlignment.add(label)
1436 table.attach(labelAlignment, 1, 2, 4, 5)
1437
1438 dialog.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
[220]1439 dialog.add_button(xstr("viewPIREP"), 1)
[151]1440 dialog.add_button(xstr("sendPIREP"), RESPONSETYPE_OK)
[345]1441
[151]1442 return dialog
[345]1443
[151]1444 def sendPIREP(self, pirep, callback = None):
1445 """Send the given PIREP."""
1446 self.beginBusy(xstr("sendPIREP_busy"))
1447 self._sendPIREPCallback = callback
1448 self.webHandler.sendPIREP(self._pirepSentCallback, pirep)
1449
1450 def _pirepSentCallback(self, returned, result):
1451 """Callback for the PIREP sending result."""
1452 gobject.idle_add(self._handlePIREPSent, returned, result)
1453
1454 def _handlePIREPSent(self, returned, result):
1455 """Callback for the PIREP sending result."""
1456 self.endBusy()
1457 secondaryMarkup = None
1458 type = MESSAGETYPE_ERROR
1459 if returned:
1460 if result.success:
1461 type = MESSAGETYPE_INFO
1462 messageFormat = xstr("sendPIREP_success")
1463 secondaryMarkup = xstr("sendPIREP_success_sec")
1464 elif result.alreadyFlown:
1465 messageFormat = xstr("sendPIREP_already")
1466 secondaryMarkup = xstr("sendPIREP_already_sec")
1467 elif result.notAvailable:
1468 messageFormat = xstr("sendPIREP_notavail")
1469 else:
1470 messageFormat = xstr("sendPIREP_unknown")
1471 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1472 else:
1473 print "PIREP sending failed", result
1474 messageFormat = xstr("sendPIREP_failed")
1475 secondaryMarkup = xstr("sendPIREP_failed_sec")
[345]1476
[151]1477 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1478 type = type, message_format = messageFormat)
1479 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1480 dialog.set_title(WINDOW_TITLE_BASE)
1481 if secondaryMarkup is not None:
1482 dialog.format_secondary_markup(secondaryMarkup)
1483
1484 dialog.run()
1485 dialog.hide()
1486
1487 callback = self._sendPIREPCallback
1488 self._sendPIREPCallback = None
1489 if callback is not None:
1490 callback(returned, result)
[168]1491
[484]1492 def sendBugReport(self, summary, description, email, callback = None):
1493 """Send the bug report with the given data."""
[490]1494 description += "\n\n" + ("=" * 40)
1495 description += "\n\nThe contents of the log:\n\n"
1496
1497 for (timestampString, text) in self._logger.lines:
1498 description += unicode(formatFlightLogLine(timestampString, text))
1499
1500 description += "\n\n" + ("=" * 40)
1501 description += "\n\nThe contents of the debug log:\n\n"
1502
1503 buffer = self._debugLogView.get_buffer()
1504 description += buffer.get_text(buffer.get_start_iter(),
1505 buffer.get_end_iter(), True)
1506
[484]1507 self.beginBusy(xstr("sendBugReport_busy"))
1508 self._sendBugReportCallback = callback
1509 self.webHandler.sendBugReport(self._bugReportSentCallback,
1510 summary, description, email)
1511
[682]1512 def _cefInitialized(self):
1513 """Called when CEF has been initialized."""
1514 self._acars.start()
[687]1515 cef.initializeSimBrief()
[682]1516
[484]1517 def _bugReportSentCallback(self, returned, result):
1518 """Callback function for the bug report sending result."""
1519 gobject.idle_add(self._handleBugReportSent, returned, result)
1520
1521 def _handleBugReportSent(self, returned, result):
1522 """Callback for the bug report sending result."""
1523 self.endBusy()
1524 secondaryMarkup = None
1525 type = MESSAGETYPE_ERROR
1526 if returned:
1527 if result.success:
1528 type = MESSAGETYPE_INFO
[491]1529 messageFormat = xstr("sendBugReport_success") % (result.ticketID,)
[484]1530 secondaryMarkup = xstr("sendBugReport_success_sec")
1531 else:
1532 messageFormat = xstr("sendBugReport_error")
1533 secondaryMarkup = xstr("sendBugReport_siteerror_sec")
1534 else:
1535 messageFormat = xstr("sendBugReport_error")
1536 secondaryMarkup = xstr("sendBugReport_error_sec")
1537
1538 dialog = gtk.MessageDialog(parent = self._wizard.gui._bugReportDialog,
1539 type = type, message_format = messageFormat)
1540 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1541 dialog.set_title(WINDOW_TITLE_BASE)
1542 if secondaryMarkup is not None:
1543 dialog.format_secondary_markup(secondaryMarkup)
1544
1545 dialog.run()
1546 dialog.hide()
1547
1548 callback = self._sendBugReportCallback
1549 self._sendBugReportCallback = None
1550 if callback is not None:
1551 callback(returned, result)
1552
[168]1553 def _listenHotkeys(self):
1554 """Setup the hotkeys based on the configuration."""
1555 if self._hotkeySetID is None and self._simulator is not None:
[178]1556 self._pilotHotkeyIndex = None
1557 self._checklistHotkeyIndex = None
1558
1559 hotkeys = []
1560
1561 config = self.config
1562 if config.enableSounds and config.pilotControlsSounds:
1563 self._pilotHotkeyIndex = len(hotkeys)
1564 hotkeys.append(config.pilotHotkey)
1565
1566 if config.enableChecklists:
1567 self._checklistHotkeyIndex = len(hotkeys)
1568 hotkeys.append(config.checklistHotkey)
1569
1570 if hotkeys:
1571 self._hotkeySetID = \
1572 self._simulator.listenHotkeys(hotkeys, self._handleHotkeys)
[168]1573
1574 def _clearHotkeys(self):
1575 """Clear the hotkeys."""
1576 if self._hotkeySetID is not None:
1577 self._hotkeySetID=None
1578 self._simulator.clearHotkeys()
1579
1580 def _handleHotkeys(self, id, hotkeys):
1581 """Handle the hotkeys."""
1582 if id==self._hotkeySetID:
[170]1583 for index in hotkeys:
[178]1584 if index==self._pilotHotkeyIndex:
1585 print "gui.GUI._handleHotkeys: pilot hotkey pressed"
[170]1586 self._flight.pilotHotkeyPressed()
[178]1587 elif index==self._checklistHotkeyIndex:
1588 print "gui.GUI._handleHotkeys: checklist hotkey pressed"
1589 self._flight.checklistHotkeyPressed()
[170]1590 else:
[178]1591 print "gui.GUI._handleHotkeys: unhandled hotkey index:", index
[227]1592
1593 def _showManual(self, menuitem):
1594 """Show the user's manual."""
1595 webbrowser.open(url ="file://" +
1596 os.path.join(self._programDirectory, "doc", "manual",
1597 getLanguage(), "index.html"),
1598 new = 1)
1599
1600 def _showAbout(self, menuitem):
1601 """Show the about dialog."""
1602 dialog = self._getAboutDialog()
1603 dialog.show_all()
1604 dialog.run()
1605 dialog.hide()
1606
1607 def _getAboutDialog(self):
1608 """Get the about dialog.
1609
1610 If it does not exist yet, it will be created."""
1611 if self._aboutDialog is None:
[276]1612 dialog = gtk.AboutDialog()
[227]1613 dialog.set_transient_for(self._mainWindow)
1614 dialog.set_modal(True)
[345]1615
[227]1616 logoPath = os.path.join(self._programDirectory, "logo.png")
1617 logo = pixbuf_new_from_file(logoPath)
1618 dialog.set_logo(logo)
[345]1619
[227]1620 dialog.set_program_name(PROGRAM_NAME)
1621 dialog.set_version(const.VERSION)
1622 dialog.set_copyright("(c) 2012 by István Váradi")
[237]1623 dialog.set_website("http://mlx.varadiistvan.hu")
1624 dialog.set_website_label(xstr("about_website"))
[227]1625
1626 isHungarian = getLanguage()=="hu"
1627 authors = []
1628 for (familyName, firstName, role) in GUI._authors:
[276]1629 author = "%s %s" % \
1630 (familyName if isHungarian else firstName,
1631 firstName if isHungarian else familyName)
1632 role = xstr("about_role_" + role)
1633 authors.append(author + " (" + role + ")")
[227]1634 dialog.set_authors(authors)
1635
1636 dialog.set_license(xstr("about_license"))
1637
[237]1638 if not pygobject:
1639 gtk.about_dialog_set_url_hook(self._showAboutURL, None)
1640
[276]1641 self._aboutDialog = dialog
1642
[227]1643 return self._aboutDialog
[237]1644
1645 def _showAboutURL(self, dialog, link, user_data):
[345]1646 """Show the about URL."""
[237]1647 webbrowser.open(url = link, new = 1)
[391]1648
1649 def _setTakeoffAntiIceOn(self, value):
1650 """Set the anti-ice on indicator."""
1651 self._wizard.takeoffAntiIceOn = value
1652
1653 def _setLandingAntiIceOn(self, value):
1654 """Set the anti-ice on indicator."""
1655 self._wizard.landingAntiIceOn = value
[744]1656
1657 def _getCredentialsCallback(self):
1658 """Called when the web handler asks for the credentials."""
1659 # FIXME: this is almost the same as
1660 # SimBriefSetupPage._getCredentialsCallback
1661 with self._credentialsCondition:
1662 self._credentialsAvailable = False
1663
1664 gobject.idle_add(self._getCredentials)
1665
1666 while not self._credentialsAvailable:
1667 self._credentialsCondition.wait()
1668
1669 return (self._credentialsUserName, self._credentialsPassword)
1670
1671 def _getCredentials(self):
1672 """Get the credentials."""
1673 # FIXME: this is almost the same as
1674 # SimBriefSetupPage._getCredentials
1675 with self._credentialsCondition:
1676 config = self.config
1677
1678 dialog = CredentialsDialog(self, config.pilotID, config.password,
1679 xstr("login_title"),
1680 xstr("button_cancel"),
1681 xstr("button_ok"),
1682 xstr("label_pilotID"),
1683 xstr("login_pilotID_tooltip"),
1684 xstr("label_password"),
1685 xstr("login_password_tooltip"),
1686 xstr("login_info"),
1687 config.rememberPassword,
1688 xstr("remember_password"),
1689 xstr("login_remember_tooltip"))
1690 response = dialog.run()
1691
1692 if response==RESPONSETYPE_OK:
1693 self._credentialsUserName = dialog.userName
1694 self._credentialsPassword = dialog.password
1695 rememberPassword = dialog.rememberPassword
1696
1697 config.pilotID = self._credentialsUserName
1698
1699 config.password = \
1700 self._credentialsPassword if rememberPassword else ""
1701 config.rememberPassword = rememberPassword
1702
1703 config.save()
1704 else:
1705 self._credentialsUserName = None
1706 self._credentialsPassword = None
1707
1708 self._credentialsAvailable = True
1709 self._credentialsCondition.notify()
[755]1710
1711 def _updateDone(self):
1712 """Called when the update is done.
1713
1714 It checks if we already know the PID, and if not, asks the user whether
1715 to register."""
1716 if not self.config.pilotID and not self.config.password:
1717 dialog = gtk.MessageDialog(parent = self._mainWindow,
1718 type = MESSAGETYPE_QUESTION,
1719 message_format = xstr("register_ask"))
1720
1721 dialog.set_title(WINDOW_TITLE_BASE)
1722 dialog.format_secondary_markup(xstr("register_ask_sec"))
1723
1724 dialog.add_button(xstr("button_cancel"), 0)
1725 dialog.add_button(xstr("button_register"), 1)
1726 dialog.set_default_response(1)
1727
1728 result = dialog.run()
1729 dialog.hide()
1730 if result == 1:
1731 self._wizard.jumpPage("register")
Note: See TracBrowser for help on using the repository browser.