source: src/mlx/gui/gui.py@ 1128:3a549693a614

python3
Last change on this file since 1128:3a549693a614 was 1111:cb25010707a5, checked in by István Váradi <ivaradi@…>, 14 months ago

Yet another trick to make CEF work on Linux properly

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