source: src/mlx/gui/gui.py@ 1108:0d45f3dff91b

python3
Last change on this file since 1108:0d45f3dff91b was 1108:0d45f3dff91b, checked in by István Váradi <ivaradi@…>, 7 months ago

On Linux the CEF message loop is run

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