source: src/mlx/gui/gui.py@ 1083:a5f219c25f2a

python3
Last change on this file since 1083:a5f219c25f2a was 1083:a5f219c25f2a, checked in by István Váradi <ivaradi@…>, 14 months ago

Updates for CEF 108 and the corresponding CEFPython.

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