source: src/mlx/gui/gui.py@ 1181:52dda2b6d0eb

python3 tip
Last change on this file since 1181:52dda2b6d0eb was 1181:52dda2b6d0eb, checked in by István Váradi <ivaradi@…>, 6 days ago

The Trac server can be accessed with password authentication only

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