source: src/mlx/gui/gui.py@ 1106:27adf311dd37

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

The flight contains if its departure gate is taxi-through

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