source: src/mlx/gui/gui.py@ 1010:e2b76a41d9ca

python3
Last change on this file since 1010:e2b76a41d9ca was 1010:e2b76a41d9ca, checked in by István Váradi <ivaradi@…>, 5 years ago

Updated copyright end years

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