source: src/mlx/gui/gui.py@ 995:ad90726834d4

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

Using 'GObject' instead of 'gobject' (re #347)

File size: 70.8 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
[123]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)
[93]120 accelGroup = gtk.AccelGroup()
121 window.add_accel_group(accelGroup)
[650]122 window.realize()
[28]123
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
[92]139 self._notebook = gtk.Notebook()
[93]140 mainVBox.pack_start(self._notebook, True, True, 4)
[345]141
[46]142 self._wizard = Wizard(self)
[107]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)
[107]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)
156 label = gtk.Label(xstr("tab_weight_help"))
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())
[107]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)
169 label = gtk.Label(xstr("tab_gates"))
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)
175 label = gtk.Label("ACARS")
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
182 mainVBox.pack_start(gtk.HSeparator(), False, False, 0)
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
[994]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().\
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
[28]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
[105]503 dialog = gtk.MessageDialog(parent = self._mainWindow,
504 type = 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
535 dialog = gtk.MessageDialog(type = 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."""
593 dialog = gtk.MessageDialog(parent = self._mainWindow,
594 type = MESSAGETYPE_QUESTION,
595 message_format = xstr("cancelFlight_question"))
596
597 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
598 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
599
600 dialog.set_title(WINDOW_TITLE_BASE)
601 result = dialog.run()
602 dialog.hide()
[345]603
[208]604 if result==RESPONSETYPE_YES:
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"""
[994]741 iconified = gdk.WindowState.ICONIFIED
[246]742
743 if (event.changed_mask&WINDOW_STATE_WITHDRAWN)!=0:
744 if (event.new_window_state&WINDOW_STATE_WITHDRAWN)!=0:
745 self._statusIcon.mainWindowHidden()
746 else:
747 self._statusIcon.mainWindowShown()
748
[994]749 if (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
750 (event.new_window_state&WINDOW_STATE_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
[120]932 dialog = gtk.MessageDialog(parent = self.mainWindow,
[119]933 type = MESSAGETYPE_ERROR,
934 message_format = xstr("fleet_failed"))
[130]935 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
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:
977 dialog = gtk.MessageDialog(parent = self.mainWindow,
978 type = MESSAGETYPE_ERROR,
979 message_format = xstr("fleet_update_failed"))
[124]980 dialog.add_button(xstr("button_ok"), 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."""
1067 menuBar = gtk.MenuBar()
[345]1068
[110]1069 fileMenuItem = gtk.MenuItem(xstr("menu_file"))
[93]1070 fileMenu = gtk.Menu()
1071 fileMenuItem.set_submenu(fileMenu)
1072 menuBar.append(fileMenuItem)
1073
[151]1074 loadPIREPMenuItem = gtk.ImageMenuItem(gtk.STOCK_OPEN)
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")),
1079 CONTROL_MASK, ACCEL_VISIBLE)
1080 loadPIREPMenuItem.connect("activate", self._loadPIREP)
1081 fileMenu.append(loadPIREPMenuItem)
1082
1083 fileMenu.append(gtk.SeparatorMenuItem())
1084
[93]1085 quitMenuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
1086 quitMenuItem.set_use_stock(True)
[110]1087 quitMenuItem.set_label(xstr("menu_file_quit"))
[93]1088 quitMenuItem.add_accelerator("activate", accelGroup,
[110]1089 ord(xstr("menu_file_quit_key")),
1090 CONTROL_MASK, ACCEL_VISIBLE)
[93]1091 quitMenuItem.connect("activate", self._quit)
1092 fileMenu.append(quitMenuItem)
1093
[123]1094 toolsMenuItem = gtk.MenuItem(xstr("menu_tools"))
1095 toolsMenu = gtk.Menu()
1096 toolsMenuItem.set_submenu(toolsMenu)
1097 menuBar.append(toolsMenuItem)
1098
[858]1099 self._timetableMenuItem = timetableMenuItem = \
1100 gtk.ImageMenuItem(gtk.STOCK_INDENT)
1101 timetableMenuItem.set_use_stock(True)
1102 timetableMenuItem.set_label(xstr("menu_tools_timetable"))
1103 timetableMenuItem.add_accelerator("activate", accelGroup,
1104 ord(xstr("menu_tools_timetable_key")),
1105 CONTROL_MASK, ACCEL_VISIBLE)
1106 timetableMenuItem.connect("activate", self.showTimetable)
1107 self._timetableMenuItem.set_sensitive(False)
1108 toolsMenu.append(timetableMenuItem)
1109
[854]1110 self._flightsMenuItem = flightsMenuItem = \
1111 gtk.ImageMenuItem(gtk.STOCK_SPELL_CHECK)
1112 flightsMenuItem.set_use_stock(True)
1113 flightsMenuItem.set_label(xstr("menu_tools_flights"))
1114 flightsMenuItem.add_accelerator("activate", accelGroup,
1115 ord(xstr("menu_tools_flights_key")),
1116 CONTROL_MASK, ACCEL_VISIBLE)
1117 flightsMenuItem.connect("activate", self.showFlights)
1118 self._flightsMenuItem.set_sensitive(False)
1119 toolsMenu.append(flightsMenuItem)
1120
[204]1121 checklistMenuItem = gtk.ImageMenuItem(gtk.STOCK_APPLY)
[172]1122 checklistMenuItem.set_use_stock(True)
1123 checklistMenuItem.set_label(xstr("menu_tools_chklst"))
1124 checklistMenuItem.add_accelerator("activate", accelGroup,
1125 ord(xstr("menu_tools_chklst_key")),
1126 CONTROL_MASK, ACCEL_VISIBLE)
1127 checklistMenuItem.connect("activate", self._editChecklist)
1128 toolsMenu.append(checklistMenuItem)
1129
[264]1130 approachCalloutsMenuItem = gtk.ImageMenuItem(gtk.STOCK_EDIT)
1131 approachCalloutsMenuItem.set_use_stock(True)
1132 approachCalloutsMenuItem.set_label(xstr("menu_tools_callouts"))
1133 approachCalloutsMenuItem.add_accelerator("activate", accelGroup,
1134 ord(xstr("menu_tools_callouts_key")),
1135 CONTROL_MASK, ACCEL_VISIBLE)
1136 approachCalloutsMenuItem.connect("activate", self._editApproachCallouts)
1137 toolsMenu.append(approachCalloutsMenuItem)
1138
[204]1139 prefsMenuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
[123]1140 prefsMenuItem.set_use_stock(True)
1141 prefsMenuItem.set_label(xstr("menu_tools_prefs"))
1142 prefsMenuItem.add_accelerator("activate", accelGroup,
1143 ord(xstr("menu_tools_prefs_key")),
1144 CONTROL_MASK, ACCEL_VISIBLE)
1145 prefsMenuItem.connect("activate", self._editPreferences)
1146 toolsMenu.append(prefsMenuItem)
[93]1147
[482]1148 toolsMenu.append(gtk.SeparatorMenuItem())
1149
1150 bugReportMenuItem = gtk.ImageMenuItem(gtk.STOCK_PASTE)
1151 bugReportMenuItem.set_use_stock(True)
1152 bugReportMenuItem.set_label(xstr("menu_tools_bugreport"))
1153 bugReportMenuItem.add_accelerator("activate", accelGroup,
1154 ord(xstr("menu_tools_bugreport_key")),
1155 CONTROL_MASK, ACCEL_VISIBLE)
1156 bugReportMenuItem.connect("activate", self._reportBug)
1157 toolsMenu.append(bugReportMenuItem)
1158
[110]1159 viewMenuItem = gtk.MenuItem(xstr("menu_view"))
[93]1160 viewMenu = gtk.Menu()
1161 viewMenuItem.set_submenu(viewMenu)
1162 menuBar.append(viewMenuItem)
[28]1163
[93]1164 self._showMonitorMenuItem = gtk.CheckMenuItem()
[110]1165 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
[93]1166 self._showMonitorMenuItem.set_use_underline(True)
1167 self._showMonitorMenuItem.set_active(False)
1168 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
[110]1169 ord(xstr("menu_view_monitor_key")),
1170 CONTROL_MASK, ACCEL_VISIBLE)
[93]1171 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
1172 viewMenu.append(self._showMonitorMenuItem)
1173
1174 showDebugMenuItem = gtk.CheckMenuItem()
[110]1175 showDebugMenuItem.set_label(xstr("menu_view_debug"))
[93]1176 showDebugMenuItem.set_use_underline(True)
1177 showDebugMenuItem.set_active(False)
1178 showDebugMenuItem.add_accelerator("activate", accelGroup,
[110]1179 ord(xstr("menu_view_debug_key")),
1180 CONTROL_MASK, ACCEL_VISIBLE)
[93]1181 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
1182 viewMenu.append(showDebugMenuItem)
[28]1183
[227]1184 helpMenuItem = gtk.MenuItem(xstr("menu_help"))
1185 helpMenu = gtk.Menu()
1186 helpMenuItem.set_submenu(helpMenu)
1187 menuBar.append(helpMenuItem)
1188
1189 manualMenuItem = gtk.ImageMenuItem(gtk.STOCK_HELP)
1190 manualMenuItem.set_use_stock(True)
1191 manualMenuItem.set_label(xstr("menu_help_manual"))
1192 manualMenuItem.add_accelerator("activate", accelGroup,
1193 ord(xstr("menu_help_manual_key")),
1194 CONTROL_MASK, ACCEL_VISIBLE)
1195 manualMenuItem.connect("activate", self._showManual)
1196 helpMenu.append(manualMenuItem)
1197
1198 helpMenu.append(gtk.SeparatorMenuItem())
[345]1199
[227]1200 aboutMenuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
1201 aboutMenuItem.set_use_stock(True)
1202 aboutMenuItem.set_label(xstr("menu_help_about"))
1203 aboutMenuItem.add_accelerator("activate", accelGroup,
1204 ord(xstr("menu_help_about_key")),
1205 CONTROL_MASK, ACCEL_VISIBLE)
1206 aboutMenuItem.connect("activate", self._showAbout)
1207 helpMenu.append(aboutMenuItem)
1208
[93]1209 return menuBar
[28]1210
[93]1211 def _toggleDebugLog(self, menuItem):
1212 """Toggle the debug log."""
1213 if menuItem.get_active():
[107]1214 label = gtk.Label(xstr("tab_debug_log"))
[93]1215 label.set_use_underline(True)
[345]1216 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
[93]1217 self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
1218 self._notebook.set_current_page(self._debugLogPage)
1219 else:
1220 self._notebook.remove_page(self._debugLogPage)
1221
1222 def _buildLogWidget(self):
1223 """Build the widget for the log."""
1224 alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
1225
1226 alignment.set_padding(padding_top = 8, padding_bottom = 8,
1227 padding_left = 16, padding_right = 16)
[28]1228
1229 logScroller = gtk.ScrolledWindow()
[93]1230 # FIXME: these should be constants in common
[994]1231 logScroller.set_policy(gtk.PolicyType.AUTOMATIC,
1232 gtk.PolicyType.AUTOMATIC)
1233 logScroller.set_shadow_type(gtk.ShadowType.IN)
[93]1234 logView = gtk.TextView()
1235 logView.set_editable(False)
[171]1236 logView.set_cursor_visible(False)
[93]1237 logScroller.add(logView)
[28]1238
1239 logBox = gtk.VBox()
1240 logBox.pack_start(logScroller, True, True, 0)
1241 logBox.set_size_request(-1, 200)
1242
[93]1243 alignment.add(logBox)
[28]1244
[93]1245 return (alignment, logView)
[28]1246
[226]1247 def _writeLog(self, msg, logView, isFault = False):
[28]1248 """Write the given message to the log."""
[93]1249 buffer = logView.get_buffer()
[226]1250 appendTextBuffer(buffer, msg, isFault = isFault)
[93]1251 logView.scroll_mark_onscreen(buffer.get_insert())
[28]1252
[81]1253 def _quit(self, what = None, force = False):
[38]1254 """Quit from the application."""
[81]1255 if force:
1256 result=RESPONSETYPE_YES
1257 else:
[105]1258 dialog = gtk.MessageDialog(parent = self._mainWindow,
1259 type = MESSAGETYPE_QUESTION,
[110]1260 message_format = xstr("quit_question"))
1261
[124]1262 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
1263 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
1264
[105]1265 dialog.set_title(WINDOW_TITLE_BASE)
[81]1266 result = dialog.run()
1267 dialog.hide()
[345]1268
[76]1269 if result==RESPONSETYPE_YES:
1270 self._statusIcon.destroy()
1271 return gtk.main_quit()
[38]1272
[46]1273 def _notebookPageSwitch(self, notebook, page, page_num):
1274 """Called when the current page of the notebook has changed."""
1275 if page_num==0:
[995]1276 GObject.idle_add(self._wizard.grabDefault)
[46]1277 else:
1278 self._mainWindow.set_default(None)
[123]1279
[854]1280 def loginSuccessful(self):
1281 """Called when the login is successful."""
1282 self._flightsMenuItem.set_sensitive(True)
[858]1283 self._timetableMenuItem.set_sensitive(True)
[854]1284
[555]1285 def isWizardActive(self):
1286 """Determine if the flight wizard is active."""
1287 return self._notebook.get_current_page()==0
1288
[869]1289 def showTimetable(self, menuItem = None):
[858]1290 """Callback for showing the timetable."""
1291 if self._timetableWindow.hasFlightPairs:
1292 self._timetableWindow.show_all()
1293 else:
1294 date = datetime.date.today()
1295 self._timetableWindow.setTypes(self.loginResult.types)
1296 self._timetableWindow.setDate(date)
1297 self.updateTimeTable(date)
1298 self.beginBusy(xstr("timetable_query_busy"))
1299
1300 def updateTimeTable(self, date):
1301 """Update the time table for the given date."""
1302 self.beginBusy(xstr("timetable_query_busy"))
1303 self._timetableWindow.set_sensitive(False)
1304 window = self._timetableWindow.get_window()
1305 if window is not None:
1306 window.set_cursor(self._busyCursor)
1307 self.webHandler.getTimetable(self._timetableCallback, date,
1308 self.loginResult.types)
1309
1310 def _timetableCallback(self, returned, result):
1311 """Called when the timetable has been received."""
[995]1312 GObject.idle_add(self._handleTimetable, returned, result)
[858]1313
1314 def _handleTimetable(self, returned, result):
1315 """Handle the result of the query for the timetable."""
1316 self.endBusy()
1317 window = self._timetableWindow.get_window()
1318 if window is not None:
1319 window.set_cursor(None)
1320 self._timetableWindow.set_sensitive(True)
1321 if returned:
1322 self._timetableWindow.setFlightPairs(result.flightPairs)
1323 self._timetableWindow.show_all()
1324 else:
1325 dialog = gtk.MessageDialog(parent = self.mainWindow,
1326 type = MESSAGETYPE_ERROR,
1327 message_format = xstr("timetable_failed"))
1328 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1329 dialog.set_title(WINDOW_TITLE_BASE)
1330 dialog.run()
1331 dialog.hide()
1332 self._timetableWindow.clear()
1333
[854]1334 def showFlights(self, menuItem):
1335 """Callback for showing the flight list."""
1336 if self._flightsWindow.hasFlights:
1337 self._flightsWindow.show_all()
1338 else:
1339 self.beginBusy(xstr("acceptedflt_query_busy"))
1340 self.webHandler.getAcceptedFlights(self._acceptedFlightsCallback)
1341
1342 def _acceptedFlightsCallback(self, returned, result):
1343 """Called when the accepted flights have been received."""
[995]1344 GObject.idle_add(self._handleAcceptedFlights, returned, result)
[854]1345
1346 def _handleAcceptedFlights(self, returned, result):
1347 """Handle the result of the query for accepted flights."""
1348 self.endBusy()
1349 if returned:
1350 self._flightsWindow.clear()
1351 for flight in result.flights:
1352 self._flightsWindow.addFlight(flight)
1353 self._flightsWindow.show_all()
1354 else:
1355 dialog = gtk.MessageDialog(parent = self.mainWindow,
1356 type = MESSAGETYPE_ERROR,
1357 message_format = xstr("acceptedflt_failed"))
1358 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1359 dialog.set_title(WINDOW_TITLE_BASE)
1360 dialog.run()
1361 dialog.hide()
1362
[858]1363 def _hideTimetableWindow(self, window, event):
1364 """Hide the window of the timetable."""
1365 self._timetableWindow.hide()
1366 return True
1367
[854]1368 def _hideFlightsWindow(self, window, event):
1369 """Hide the window of the accepted flights."""
1370 self._flightsWindow.hide()
1371 return True
1372
[172]1373 def _editChecklist(self, menuItem):
1374 """Callback for editing the checklists."""
1375 self._checklistEditor.run()
[345]1376
[264]1377 def _editApproachCallouts(self, menuItem):
1378 """Callback for editing the approach callouts."""
1379 self._approachCalloutsEditor.run()
[345]1380
[123]1381 def _editPreferences(self, menuItem):
1382 """Callback for editing the preferences."""
[168]1383 self._clearHotkeys()
[123]1384 self._preferences.run(self.config)
[148]1385 self._setupTimeSync()
[168]1386 self._listenHotkeys()
[148]1387
[482]1388 def _reportBug(self, menuItem):
1389 """Callback for reporting a bug."""
[484]1390 self._bugReportDialog.run()
[482]1391
[148]1392 def _setupTimeSync(self):
1393 """Enable or disable the simulator time synchronization based on the
1394 configuration."""
1395 simulator = self._simulator
1396 if simulator is not None:
1397 if self.config.syncFSTime:
1398 simulator.enableTimeSync()
1399 else:
1400 simulator.disableTimeSync()
[151]1401
[830]1402 def viewPIREP(self, pirep):
1403 """Display the PIREP viewer window with the given PIREP."""
1404 self._pirepViewer.setPIREP(pirep)
1405 self._pirepViewer.show_all()
1406 self._pirepViewer.run()
1407 self._pirepViewer.hide()
1408
[855]1409 def viewMessagedPIREP(self, pirep):
1410 """Display the PIREP viewer window with the given PIREP containing
1411 messages as well."""
1412 self._messagedPIREPViewer.setPIREP(pirep)
1413 self._messagedPIREPViewer.show_all()
1414 self._messagedPIREPViewer.run()
1415 self._messagedPIREPViewer.hide()
1416
[830]1417 def editPIREP(self, pirep):
1418 """Display the PIREP editor window and allow editing the PIREP."""
[845]1419 self._pirepEditor.setPIREP(pirep)
1420 self._pirepEditor.show_all()
[853]1421 if self._pirepEditor.run()==RESPONSETYPE_OK:
1422 self.beginBusy(xstr("pirepEdit_save_busy"))
1423 self.webHandler.sendPIREP(self._pirepUpdatedCallback, pirep,
1424 update = True)
1425 else:
1426 self._pirepEditor.hide()
1427
1428 def _pirepUpdatedCallback(self, returned, result):
1429 """Callback for the PIREP updating result."""
[995]1430 GObject.idle_add(self._handlePIREPUpdated, returned, result)
[853]1431
1432 def _handlePIREPUpdated(self, returned, result):
1433 """Callback for the PIREP updating result."""
1434 self.endBusy()
1435 secondaryMarkup = None
1436 type = MESSAGETYPE_ERROR
1437 if returned:
1438 if result.success:
1439 type = None
1440 elif result.alreadyFlown:
1441 messageFormat = xstr("sendPIREP_already")
1442 secondaryMarkup = xstr("sendPIREP_already_sec")
1443 elif result.notAvailable:
1444 messageFormat = xstr("sendPIREP_notavail")
1445 else:
1446 messageFormat = xstr("sendPIREP_unknown")
1447 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1448 else:
[919]1449 print("PIREP sending failed", result)
[853]1450 messageFormat = xstr("sendPIREP_failed")
1451 secondaryMarkup = xstr("sendPIREP_failed_sec")
1452
1453 if type is not None:
1454 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1455 type = type, message_format = messageFormat)
1456 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1457 dialog.set_title(WINDOW_TITLE_BASE)
1458 if secondaryMarkup is not None:
1459 dialog.format_secondary_markup(secondaryMarkup)
1460
1461 dialog.run()
1462 dialog.hide()
1463
[845]1464 self._pirepEditor.hide()
[830]1465
[151]1466 def _loadPIREP(self, menuItem):
1467 """Load a PIREP for sending."""
1468 dialog = self._getLoadPirepDialog()
1469
1470 if self._lastLoadedPIREP:
1471 dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP))
1472 else:
1473 pirepDirectory = self.config.pirepDirectory
1474 if pirepDirectory is not None:
1475 dialog.set_current_folder(pirepDirectory)
[345]1476
[151]1477 result = dialog.run()
1478 dialog.hide()
1479
1480 if result==RESPONSETYPE_OK:
[954]1481 self._lastLoadedPIREP = dialog.get_filename()
[151]1482
1483 pirep = PIREP.load(self._lastLoadedPIREP)
1484 if pirep is None:
1485 dialog = gtk.MessageDialog(parent = self._mainWindow,
1486 type = MESSAGETYPE_ERROR,
1487 message_format = xstr("loadPIREP_failed"))
1488 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1489 dialog.set_title(WINDOW_TITLE_BASE)
1490 dialog.format_secondary_markup(xstr("loadPIREP_failed_sec"))
1491 dialog.run()
1492 dialog.hide()
1493 else:
1494 dialog = self._getSendLoadedDialog(pirep)
1495 dialog.show_all()
[239]1496 while True:
1497 result = dialog.run()
[151]1498
[239]1499 if result==RESPONSETYPE_OK:
1500 self.sendPIREP(pirep)
1501 elif result==1:
[832]1502 self.viewPIREP(pirep)
[239]1503 else:
1504 break
1505
1506 dialog.hide()
[151]1507
1508 def _getLoadPirepDialog(self):
1509 """Get the PIREP loading file chooser dialog.
1510
1511 If it is not created yet, it will be created."""
1512 if self._loadPIREPDialog is None:
1513 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
1514 xstr("loadPIREP_browser_title"),
1515 action = FILE_CHOOSER_ACTION_OPEN,
1516 buttons = (gtk.STOCK_CANCEL,
1517 RESPONSETYPE_CANCEL,
1518 gtk.STOCK_OK, RESPONSETYPE_OK),
1519 parent = self._mainWindow)
1520 dialog.set_modal(True)
[345]1521
[151]1522
1523 filter = gtk.FileFilter()
[184]1524 filter.set_name(xstr("file_filter_pireps"))
[151]1525 filter.add_pattern("*.pirep")
1526 dialog.add_filter(filter)
[345]1527
[151]1528 filter = gtk.FileFilter()
[184]1529 filter.set_name(xstr("file_filter_all"))
[151]1530 filter.add_pattern("*.*")
1531 dialog.add_filter(filter)
1532
1533 self._loadPIREPDialog = dialog
1534
1535 return self._loadPIREPDialog
1536
1537 def _getSendLoadedDialog(self, pirep):
1538 """Get a dialog displaying the main information of the flight from the
1539 PIREP and providing Cancel and Send buttons."""
1540 dialog = gtk.Dialog(title = WINDOW_TITLE_BASE + " - " +
1541 xstr("loadPIREP_send_title"),
1542 parent = self._mainWindow,
1543 flags = DIALOG_MODAL)
1544
1545 contentArea = dialog.get_content_area()
1546
1547 label = gtk.Label(xstr("loadPIREP_send_help"))
1548 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1549 xscale = 0.0, yscale = 0.0)
1550 alignment.set_padding(padding_top = 16, padding_bottom = 0,
1551 padding_left = 48, padding_right = 48)
1552 alignment.add(label)
1553 contentArea.pack_start(alignment, False, False, 8)
1554
1555 table = gtk.Table(5, 2)
1556 tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1557 xscale = 0.0, yscale = 0.0)
1558 tableAlignment.set_padding(padding_top = 0, padding_bottom = 32,
1559 padding_left = 48, padding_right = 48)
1560 table.set_row_spacings(4)
1561 table.set_col_spacings(16)
1562 tableAlignment.add(table)
1563 contentArea.pack_start(tableAlignment, True, True, 8)
1564
1565 bookedFlight = pirep.bookedFlight
1566
1567 label = gtk.Label("<b>" + xstr("loadPIREP_send_flightno") + "</b>")
1568 label.set_use_markup(True)
1569 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1570 xscale = 0.0, yscale = 0.0)
1571 labelAlignment.add(label)
1572 table.attach(labelAlignment, 0, 1, 0, 1)
[345]1573
[151]1574 label = gtk.Label(bookedFlight.callsign)
1575 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1576 xscale = 0.0, yscale = 0.0)
1577 labelAlignment.add(label)
1578 table.attach(labelAlignment, 1, 2, 0, 1)
1579
1580 label = gtk.Label("<b>" + xstr("loadPIREP_send_date") + "</b>")
1581 label.set_use_markup(True)
1582 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1583 xscale = 0.0, yscale = 0.0)
1584 labelAlignment.add(label)
1585 table.attach(labelAlignment, 0, 1, 1, 2)
[345]1586
[151]1587 label = gtk.Label(str(bookedFlight.departureTime.date()))
1588 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1589 xscale = 0.0, yscale = 0.0)
1590 labelAlignment.add(label)
1591 table.attach(labelAlignment, 1, 2, 1, 2)
1592
1593 label = gtk.Label("<b>" + xstr("loadPIREP_send_from") + "</b>")
1594 label.set_use_markup(True)
1595 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1596 xscale = 0.0, yscale = 0.0)
1597 labelAlignment.add(label)
1598 table.attach(labelAlignment, 0, 1, 2, 3)
[345]1599
[151]1600 label = gtk.Label(bookedFlight.departureICAO)
1601 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1602 xscale = 0.0, yscale = 0.0)
1603 labelAlignment.add(label)
1604 table.attach(labelAlignment, 1, 2, 2, 3)
1605
1606 label = gtk.Label("<b>" + xstr("loadPIREP_send_to") + "</b>")
1607 label.set_use_markup(True)
1608 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1609 xscale = 0.0, yscale = 0.0)
1610 labelAlignment.add(label)
1611 table.attach(labelAlignment, 0, 1, 3, 4)
[345]1612
[151]1613 label = gtk.Label(bookedFlight.arrivalICAO)
1614 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1615 xscale = 0.0, yscale = 0.0)
1616 labelAlignment.add(label)
1617 table.attach(labelAlignment, 1, 2, 3, 4)
1618
1619 label = gtk.Label("<b>" + xstr("loadPIREP_send_rating") + "</b>")
1620 label.set_use_markup(True)
1621 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1622 xscale = 0.0, yscale = 0.0)
1623 labelAlignment.add(label)
1624 table.attach(labelAlignment, 0, 1, 4, 5)
1625
1626 rating = pirep.rating
1627 label = gtk.Label()
1628 if rating<0:
1629 label.set_markup('<b><span foreground="red">NO GO</span></b>')
1630 else:
1631 label.set_text("%.1f %%" % (rating,))
[345]1632
[151]1633 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1634 xscale = 0.0, yscale = 0.0)
1635 labelAlignment.add(label)
1636 table.attach(labelAlignment, 1, 2, 4, 5)
1637
1638 dialog.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
[220]1639 dialog.add_button(xstr("viewPIREP"), 1)
[151]1640 dialog.add_button(xstr("sendPIREP"), RESPONSETYPE_OK)
[345]1641
[151]1642 return dialog
[345]1643
[151]1644 def sendPIREP(self, pirep, callback = None):
1645 """Send the given PIREP."""
1646 self.beginBusy(xstr("sendPIREP_busy"))
1647 self._sendPIREPCallback = callback
1648 self.webHandler.sendPIREP(self._pirepSentCallback, pirep)
1649
1650 def _pirepSentCallback(self, returned, result):
1651 """Callback for the PIREP sending result."""
[995]1652 GObject.idle_add(self._handlePIREPSent, returned, result)
[151]1653
1654 def _handlePIREPSent(self, returned, result):
1655 """Callback for the PIREP sending result."""
1656 self.endBusy()
1657 secondaryMarkup = None
1658 type = MESSAGETYPE_ERROR
1659 if returned:
1660 if result.success:
1661 type = MESSAGETYPE_INFO
1662 messageFormat = xstr("sendPIREP_success")
1663 secondaryMarkup = xstr("sendPIREP_success_sec")
1664 elif result.alreadyFlown:
1665 messageFormat = xstr("sendPIREP_already")
1666 secondaryMarkup = xstr("sendPIREP_already_sec")
1667 elif result.notAvailable:
1668 messageFormat = xstr("sendPIREP_notavail")
1669 else:
1670 messageFormat = xstr("sendPIREP_unknown")
1671 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1672 else:
[919]1673 print("PIREP sending failed", result)
[151]1674 messageFormat = xstr("sendPIREP_failed")
1675 secondaryMarkup = xstr("sendPIREP_failed_sec")
[345]1676
[151]1677 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1678 type = type, message_format = messageFormat)
1679 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1680 dialog.set_title(WINDOW_TITLE_BASE)
1681 if secondaryMarkup is not None:
1682 dialog.format_secondary_markup(secondaryMarkup)
1683
1684 dialog.run()
1685 dialog.hide()
1686
1687 callback = self._sendPIREPCallback
1688 self._sendPIREPCallback = None
1689 if callback is not None:
1690 callback(returned, result)
[168]1691
[484]1692 def sendBugReport(self, summary, description, email, callback = None):
1693 """Send the bug report with the given data."""
[490]1694 description += "\n\n" + ("=" * 40)
1695 description += "\n\nThe contents of the log:\n\n"
1696
1697 for (timestampString, text) in self._logger.lines:
[919]1698 description += str(formatFlightLogLine(timestampString, text))
[490]1699
1700 description += "\n\n" + ("=" * 40)
1701 description += "\n\nThe contents of the debug log:\n\n"
1702
1703 buffer = self._debugLogView.get_buffer()
1704 description += buffer.get_text(buffer.get_start_iter(),
1705 buffer.get_end_iter(), True)
1706
[484]1707 self.beginBusy(xstr("sendBugReport_busy"))
1708 self._sendBugReportCallback = callback
1709 self.webHandler.sendBugReport(self._bugReportSentCallback,
1710 summary, description, email)
1711
[682]1712 def _cefInitialized(self):
1713 """Called when CEF has been initialized."""
1714 self._acars.start()
[687]1715 cef.initializeSimBrief()
[682]1716
[484]1717 def _bugReportSentCallback(self, returned, result):
1718 """Callback function for the bug report sending result."""
[995]1719 GObject.idle_add(self._handleBugReportSent, returned, result)
[484]1720
1721 def _handleBugReportSent(self, returned, result):
1722 """Callback for the bug report sending result."""
1723 self.endBusy()
1724 secondaryMarkup = None
1725 type = MESSAGETYPE_ERROR
1726 if returned:
1727 if result.success:
1728 type = MESSAGETYPE_INFO
[491]1729 messageFormat = xstr("sendBugReport_success") % (result.ticketID,)
[484]1730 secondaryMarkup = xstr("sendBugReport_success_sec")
1731 else:
1732 messageFormat = xstr("sendBugReport_error")
1733 secondaryMarkup = xstr("sendBugReport_siteerror_sec")
1734 else:
1735 messageFormat = xstr("sendBugReport_error")
1736 secondaryMarkup = xstr("sendBugReport_error_sec")
1737
1738 dialog = gtk.MessageDialog(parent = self._wizard.gui._bugReportDialog,
1739 type = type, message_format = messageFormat)
1740 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1741 dialog.set_title(WINDOW_TITLE_BASE)
1742 if secondaryMarkup is not None:
1743 dialog.format_secondary_markup(secondaryMarkup)
1744
1745 dialog.run()
1746 dialog.hide()
1747
1748 callback = self._sendBugReportCallback
1749 self._sendBugReportCallback = None
1750 if callback is not None:
1751 callback(returned, result)
1752
[168]1753 def _listenHotkeys(self):
1754 """Setup the hotkeys based on the configuration."""
1755 if self._hotkeySetID is None and self._simulator is not None:
[178]1756 self._pilotHotkeyIndex = None
1757 self._checklistHotkeyIndex = None
1758
1759 hotkeys = []
1760
1761 config = self.config
1762 if config.enableSounds and config.pilotControlsSounds:
1763 self._pilotHotkeyIndex = len(hotkeys)
1764 hotkeys.append(config.pilotHotkey)
1765
1766 if config.enableChecklists:
1767 self._checklistHotkeyIndex = len(hotkeys)
1768 hotkeys.append(config.checklistHotkey)
1769
1770 if hotkeys:
1771 self._hotkeySetID = \
1772 self._simulator.listenHotkeys(hotkeys, self._handleHotkeys)
[168]1773
1774 def _clearHotkeys(self):
1775 """Clear the hotkeys."""
1776 if self._hotkeySetID is not None:
1777 self._hotkeySetID=None
1778 self._simulator.clearHotkeys()
1779
1780 def _handleHotkeys(self, id, hotkeys):
1781 """Handle the hotkeys."""
1782 if id==self._hotkeySetID:
[170]1783 for index in hotkeys:
[178]1784 if index==self._pilotHotkeyIndex:
[919]1785 print("gui.GUI._handleHotkeys: pilot hotkey pressed")
[170]1786 self._flight.pilotHotkeyPressed()
[178]1787 elif index==self._checklistHotkeyIndex:
[919]1788 print("gui.GUI._handleHotkeys: checklist hotkey pressed")
[178]1789 self._flight.checklistHotkeyPressed()
[170]1790 else:
[919]1791 print("gui.GUI._handleHotkeys: unhandled hotkey index:", index)
[227]1792
1793 def _showManual(self, menuitem):
1794 """Show the user's manual."""
1795 webbrowser.open(url ="file://" +
1796 os.path.join(self._programDirectory, "doc", "manual",
1797 getLanguage(), "index.html"),
1798 new = 1)
1799
1800 def _showAbout(self, menuitem):
1801 """Show the about dialog."""
1802 dialog = self._getAboutDialog()
1803 dialog.show_all()
1804 dialog.run()
1805 dialog.hide()
1806
1807 def _getAboutDialog(self):
1808 """Get the about dialog.
1809
1810 If it does not exist yet, it will be created."""
1811 if self._aboutDialog is None:
[276]1812 dialog = gtk.AboutDialog()
[227]1813 dialog.set_transient_for(self._mainWindow)
1814 dialog.set_modal(True)
[345]1815
[227]1816 logoPath = os.path.join(self._programDirectory, "logo.png")
1817 logo = pixbuf_new_from_file(logoPath)
1818 dialog.set_logo(logo)
[345]1819
[227]1820 dialog.set_program_name(PROGRAM_NAME)
1821 dialog.set_version(const.VERSION)
1822 dialog.set_copyright("(c) 2012 by István Váradi")
[237]1823 dialog.set_website("http://mlx.varadiistvan.hu")
1824 dialog.set_website_label(xstr("about_website"))
[227]1825
1826 isHungarian = getLanguage()=="hu"
1827 authors = []
1828 for (familyName, firstName, role) in GUI._authors:
[276]1829 author = "%s %s" % \
1830 (familyName if isHungarian else firstName,
1831 firstName if isHungarian else familyName)
1832 role = xstr("about_role_" + role)
1833 authors.append(author + " (" + role + ")")
[227]1834 dialog.set_authors(authors)
1835
1836 dialog.set_license(xstr("about_license"))
1837
[276]1838 self._aboutDialog = dialog
1839
[227]1840 return self._aboutDialog
[237]1841
1842 def _showAboutURL(self, dialog, link, user_data):
[345]1843 """Show the about URL."""
[237]1844 webbrowser.open(url = link, new = 1)
[391]1845
1846 def _setTakeoffAntiIceOn(self, value):
1847 """Set the anti-ice on indicator."""
1848 self._wizard.takeoffAntiIceOn = value
1849
1850 def _setLandingAntiIceOn(self, value):
1851 """Set the anti-ice on indicator."""
1852 self._wizard.landingAntiIceOn = value
[744]1853
1854 def _getCredentialsCallback(self):
1855 """Called when the web handler asks for the credentials."""
1856 # FIXME: this is almost the same as
1857 # SimBriefSetupPage._getCredentialsCallback
1858 with self._credentialsCondition:
1859 self._credentialsAvailable = False
1860
[995]1861 GObject.idle_add(self._getCredentials)
[744]1862
1863 while not self._credentialsAvailable:
1864 self._credentialsCondition.wait()
1865
1866 return (self._credentialsUserName, self._credentialsPassword)
1867
1868 def _getCredentials(self):
1869 """Get the credentials."""
1870 # FIXME: this is almost the same as
1871 # SimBriefSetupPage._getCredentials
1872 with self._credentialsCondition:
1873 config = self.config
1874
1875 dialog = CredentialsDialog(self, config.pilotID, config.password,
1876 xstr("login_title"),
1877 xstr("button_cancel"),
1878 xstr("button_ok"),
1879 xstr("label_pilotID"),
1880 xstr("login_pilotID_tooltip"),
1881 xstr("label_password"),
1882 xstr("login_password_tooltip"),
1883 xstr("login_info"),
1884 config.rememberPassword,
1885 xstr("remember_password"),
1886 xstr("login_remember_tooltip"))
1887 response = dialog.run()
1888
1889 if response==RESPONSETYPE_OK:
1890 self._credentialsUserName = dialog.userName
1891 self._credentialsPassword = dialog.password
1892 rememberPassword = dialog.rememberPassword
1893
1894 config.pilotID = self._credentialsUserName
1895
1896 config.password = \
1897 self._credentialsPassword if rememberPassword else ""
1898 config.rememberPassword = rememberPassword
1899
1900 config.save()
1901 else:
1902 self._credentialsUserName = None
1903 self._credentialsPassword = None
1904
1905 self._credentialsAvailable = True
1906 self._credentialsCondition.notify()
[755]1907
1908 def _updateDone(self):
1909 """Called when the update is done.
1910
1911 It checks if we already know the PID, and if not, asks the user whether
1912 to register."""
[946]1913 cef.initialize(self._cefInitialized)
1914
[755]1915 if not self.config.pilotID and not self.config.password:
1916 dialog = gtk.MessageDialog(parent = self._mainWindow,
1917 type = MESSAGETYPE_QUESTION,
1918 message_format = xstr("register_ask"))
1919
1920 dialog.set_title(WINDOW_TITLE_BASE)
1921 dialog.format_secondary_markup(xstr("register_ask_sec"))
1922
1923 dialog.add_button(xstr("button_cancel"), 0)
1924 dialog.add_button(xstr("button_register"), 1)
1925 dialog.set_default_response(1)
1926
1927 result = dialog.run()
1928 dialog.hide()
1929 if result == 1:
1930 self._wizard.jumpPage("register")
Note: See TracBrowser for help on using the repository browser.