source: src/mlx/gui/gui.py@ 1037:687ff2fb8cab

python3
Last change on this file since 1037:687ff2fb8cab was 1037:687ff2fb8cab, checked in by István Váradi <ivaradi@…>, 2 years ago

Stepped version and copyright date (re #357)

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