source: src/mlx/gui/gui.py@ 1178:cd1b4199e2e2

python3 tip
Last change on this file since 1178:cd1b4199e2e2 was 1178:cd1b4199e2e2, checked in by István Váradi <ivaradi@…>, 2 days ago

The PIREP editor allows sending a PIREP only if the user is logged in (re #391)

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