source: src/mlx/gui/gui.py@ 1097:f2ac841cffec

python3
Last change on this file since 1097:f2ac841cffec was 1097:f2ac841cffec, checked in by István Váradi <ivaradi@…>, 9 months ago

The browser cache can be cleared from the menu (re #368)

File size: 73.2 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(False)
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:
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 if self._simulator is None:
1063 self._simulator = fs.createSimulator(simulatorType, self)
1064 fs.setupMessageSending(self.config, self._simulator)
1065 self._setupTimeSync()
1066
1067 self._flight.simulator = self._simulator
1068
1069 self.beginBusy(xstr("connect_busy"))
1070 self._statusbar.updateConnection(self._connecting, self._connected)
1071
1072 self._connecting = True
1073 self._simulator.connect(self._flight.aircraft)
1074
1075 def startMonitoring(self):
1076 """Start monitoring."""
1077 if not self._monitoring:
1078 self.simulator.startMonitoring()
1079 self._monitoring = True
1080
1081 def stopMonitoring(self):
1082 """Stop monitoring."""
1083 if self._monitoring:
1084 self.simulator.stopMonitoring()
1085 self._monitoring = False
1086
1087 def cruiseLevelChanged(self):
1088 """Called when the cruise level is changed in the flight wizard."""
1089 if self._flight is not None:
1090 return self._flight.cruiseLevelChanged()
1091 else:
1092 return False
1093
1094 def _buildMenuBar(self, accelGroup):
1095 """Build the main menu bar."""
1096 menuBar = Gtk.MenuBar()
1097
1098 fileMenuItem = Gtk.MenuItem(xstr("menu_file"))
1099 fileMenu = Gtk.Menu()
1100 fileMenuItem.set_submenu(fileMenu)
1101 menuBar.append(fileMenuItem)
1102
1103 loadPIREPMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_OPEN)
1104 loadPIREPMenuItem.set_use_stock(True)
1105 loadPIREPMenuItem.set_label(xstr("menu_file_loadPIREP"))
1106 loadPIREPMenuItem.add_accelerator("activate", accelGroup,
1107 ord(xstr("menu_file_loadPIREP_key")),
1108 Gdk.ModifierType.CONTROL_MASK,
1109 Gtk.AccelFlags.VISIBLE)
1110 loadPIREPMenuItem.connect("activate", self._loadPIREP)
1111 fileMenu.append(loadPIREPMenuItem)
1112
1113 fileMenu.append(Gtk.SeparatorMenuItem())
1114
1115 quitMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_QUIT)
1116 quitMenuItem.set_use_stock(True)
1117 quitMenuItem.set_label(xstr("menu_file_quit"))
1118 quitMenuItem.add_accelerator("activate", accelGroup,
1119 ord(xstr("menu_file_quit_key")),
1120 Gdk.ModifierType.CONTROL_MASK,
1121 Gtk.AccelFlags.VISIBLE)
1122 quitMenuItem.connect("activate", self._quit)
1123 fileMenu.append(quitMenuItem)
1124
1125 toolsMenuItem = Gtk.MenuItem(xstr("menu_tools"))
1126 toolsMenu = Gtk.Menu()
1127 toolsMenuItem.set_submenu(toolsMenu)
1128 menuBar.append(toolsMenuItem)
1129
1130 self._timetableMenuItem = timetableMenuItem = \
1131 Gtk.ImageMenuItem(Gtk.STOCK_INDENT)
1132 timetableMenuItem.set_use_stock(True)
1133 timetableMenuItem.set_label(xstr("menu_tools_timetable"))
1134 timetableMenuItem.add_accelerator("activate", accelGroup,
1135 ord(xstr("menu_tools_timetable_key")),
1136 Gdk.ModifierType.CONTROL_MASK,
1137 Gtk.AccelFlags.VISIBLE)
1138 timetableMenuItem.connect("activate", self.showTimetable)
1139 self._timetableMenuItem.set_sensitive(False)
1140 toolsMenu.append(timetableMenuItem)
1141
1142 self._flightsMenuItem = flightsMenuItem = \
1143 Gtk.ImageMenuItem(Gtk.STOCK_SPELL_CHECK)
1144 flightsMenuItem.set_use_stock(True)
1145 flightsMenuItem.set_label(xstr("menu_tools_flights"))
1146 flightsMenuItem.add_accelerator("activate", accelGroup,
1147 ord(xstr("menu_tools_flights_key")),
1148 Gdk.ModifierType.CONTROL_MASK,
1149 Gtk.AccelFlags.VISIBLE)
1150 flightsMenuItem.connect("activate", self.showFlights)
1151 self._flightsMenuItem.set_sensitive(False)
1152 toolsMenu.append(flightsMenuItem)
1153
1154 checklistMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_APPLY)
1155 checklistMenuItem.set_use_stock(True)
1156 checklistMenuItem.set_label(xstr("menu_tools_chklst"))
1157 checklistMenuItem.add_accelerator("activate", accelGroup,
1158 ord(xstr("menu_tools_chklst_key")),
1159 Gdk.ModifierType.CONTROL_MASK,
1160 Gtk.AccelFlags.VISIBLE)
1161 checklistMenuItem.connect("activate", self._editChecklist)
1162 toolsMenu.append(checklistMenuItem)
1163
1164 approachCalloutsMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_EDIT)
1165 approachCalloutsMenuItem.set_use_stock(True)
1166 approachCalloutsMenuItem.set_label(xstr("menu_tools_callouts"))
1167 approachCalloutsMenuItem.add_accelerator("activate", accelGroup,
1168 ord(xstr("menu_tools_callouts_key")),
1169 Gdk.ModifierType.CONTROL_MASK,
1170 Gtk.AccelFlags.VISIBLE)
1171 approachCalloutsMenuItem.connect("activate", self._editApproachCallouts)
1172 toolsMenu.append(approachCalloutsMenuItem)
1173
1174 prefsMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_PREFERENCES)
1175 prefsMenuItem.set_use_stock(True)
1176 prefsMenuItem.set_label(xstr("menu_tools_prefs"))
1177 prefsMenuItem.add_accelerator("activate", accelGroup,
1178 ord(xstr("menu_tools_prefs_key")),
1179 Gdk.ModifierType.CONTROL_MASK,
1180 Gtk.AccelFlags.VISIBLE)
1181 prefsMenuItem.connect("activate", self._editPreferences)
1182 toolsMenu.append(prefsMenuItem)
1183
1184 toolsMenu.append(Gtk.SeparatorMenuItem())
1185
1186 clearCEFCacheMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_DISCARD)
1187 clearCEFCacheMenuItem.set_use_stock(True)
1188 clearCEFCacheMenuItem.set_label(xstr("menu_tools_clear_cef_cache"))
1189 clearCEFCacheMenuItem.connect("activate", self._clearCEFCache)
1190 toolsMenu.append(clearCEFCacheMenuItem)
1191
1192 toolsMenu.append(Gtk.SeparatorMenuItem())
1193
1194 bugReportMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_PASTE)
1195 bugReportMenuItem.set_use_stock(True)
1196 bugReportMenuItem.set_label(xstr("menu_tools_bugreport"))
1197 bugReportMenuItem.add_accelerator("activate", accelGroup,
1198 ord(xstr("menu_tools_bugreport_key")),
1199 Gdk.ModifierType.CONTROL_MASK,
1200 Gtk.AccelFlags.VISIBLE)
1201 bugReportMenuItem.connect("activate", self._reportBug)
1202 toolsMenu.append(bugReportMenuItem)
1203
1204 viewMenuItem = Gtk.MenuItem(xstr("menu_view"))
1205 viewMenu = Gtk.Menu()
1206 viewMenuItem.set_submenu(viewMenu)
1207 menuBar.append(viewMenuItem)
1208
1209 self._showMonitorMenuItem = Gtk.CheckMenuItem()
1210 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
1211 self._showMonitorMenuItem.set_use_underline(True)
1212 self._showMonitorMenuItem.set_active(False)
1213 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
1214 ord(xstr("menu_view_monitor_key")),
1215 Gdk.ModifierType.CONTROL_MASK,
1216 Gtk.AccelFlags.VISIBLE)
1217 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
1218 viewMenu.append(self._showMonitorMenuItem)
1219
1220 showDebugMenuItem = Gtk.CheckMenuItem()
1221 showDebugMenuItem.set_label(xstr("menu_view_debug"))
1222 showDebugMenuItem.set_use_underline(True)
1223 showDebugMenuItem.set_active(False)
1224 showDebugMenuItem.add_accelerator("activate", accelGroup,
1225 ord(xstr("menu_view_debug_key")),
1226 Gdk.ModifierType.CONTROL_MASK,
1227 Gtk.AccelFlags.VISIBLE)
1228 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
1229 viewMenu.append(showDebugMenuItem)
1230
1231 helpMenuItem = Gtk.MenuItem(xstr("menu_help"))
1232 helpMenu = Gtk.Menu()
1233 helpMenuItem.set_submenu(helpMenu)
1234 menuBar.append(helpMenuItem)
1235
1236 manualMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_HELP)
1237 manualMenuItem.set_use_stock(True)
1238 manualMenuItem.set_label(xstr("menu_help_manual"))
1239 manualMenuItem.add_accelerator("activate", accelGroup,
1240 ord(xstr("menu_help_manual_key")),
1241 Gdk.ModifierType.CONTROL_MASK,
1242 Gtk.AccelFlags.VISIBLE)
1243 manualMenuItem.connect("activate", self._showManual)
1244 helpMenu.append(manualMenuItem)
1245
1246 helpMenu.append(Gtk.SeparatorMenuItem())
1247
1248 aboutMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_ABOUT)
1249 aboutMenuItem.set_use_stock(True)
1250 aboutMenuItem.set_label(xstr("menu_help_about"))
1251 aboutMenuItem.add_accelerator("activate", accelGroup,
1252 ord(xstr("menu_help_about_key")),
1253 Gdk.ModifierType.CONTROL_MASK,
1254 Gtk.AccelFlags.VISIBLE)
1255 aboutMenuItem.connect("activate", self._showAbout)
1256 helpMenu.append(aboutMenuItem)
1257
1258 return menuBar
1259
1260 def _toggleDebugLog(self, menuItem):
1261 """Toggle the debug log."""
1262 if menuItem.get_active():
1263 label = Gtk.Label(xstr("tab_debug_log"))
1264 label.set_use_underline(True)
1265 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
1266 self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
1267 self._notebook.set_current_page(self._debugLogPage)
1268 else:
1269 self._notebook.remove_page(self._debugLogPage)
1270
1271 def _buildLogWidget(self):
1272 """Build the widget for the log."""
1273 alignment = Gtk.Alignment(xscale = 1.0, yscale = 1.0)
1274
1275 alignment.set_padding(padding_top = 8, padding_bottom = 8,
1276 padding_left = 16, padding_right = 16)
1277
1278 logScroller = Gtk.ScrolledWindow()
1279 # FIXME: these should be constants in common
1280 logScroller.set_policy(Gtk.PolicyType.AUTOMATIC,
1281 Gtk.PolicyType.AUTOMATIC)
1282 logScroller.set_shadow_type(Gtk.ShadowType.IN)
1283 logView = Gtk.TextView()
1284 logView.set_editable(False)
1285 logView.set_cursor_visible(False)
1286 logScroller.add(logView)
1287
1288 logBox = Gtk.VBox()
1289 logBox.pack_start(logScroller, True, True, 0)
1290 logBox.set_size_request(-1, 200)
1291
1292 alignment.add(logBox)
1293
1294 return (alignment, logView)
1295
1296 def _writeLog(self, msg, logView, isFault = False):
1297 """Write the given message to the log."""
1298 buffer = logView.get_buffer()
1299 appendTextBuffer(buffer, msg, isFault = isFault)
1300 logView.scroll_mark_onscreen(buffer.get_insert())
1301
1302 def _quit(self, what = None, force = False):
1303 """Quit from the application."""
1304 if force:
1305 result=Gtk.ResponseType.YES
1306 else:
1307 dialog = Gtk.MessageDialog(parent = self._mainWindow,
1308 type = Gtk.MessageType.QUESTION,
1309 message_format = xstr("quit_question"))
1310
1311 dialog.add_button(xstr("button_no"), Gtk.ResponseType.NO)
1312 dialog.add_button(xstr("button_yes"), Gtk.ResponseType.YES)
1313
1314 dialog.set_title(WINDOW_TITLE_BASE)
1315 result = dialog.run()
1316 dialog.hide()
1317
1318 if result==Gtk.ResponseType.YES:
1319 self._statusIcon.destroy()
1320 return Gtk.main_quit()
1321
1322 def _notebookPageSwitch(self, notebook, page, page_num):
1323 """Called when the current page of the notebook has changed."""
1324 if page_num==0:
1325 GObject.idle_add(self._wizard.grabDefault)
1326 else:
1327 self._mainWindow.set_default(None)
1328
1329 def loginSuccessful(self):
1330 """Called when the login is successful."""
1331 self._flightsMenuItem.set_sensitive(True)
1332 self._timetableMenuItem.set_sensitive(True)
1333
1334 def isWizardActive(self):
1335 """Determine if the flight wizard is active."""
1336 return self._notebook.get_current_page()==0
1337
1338 def showTimetable(self, menuItem = None):
1339 """Callback for showing the timetable."""
1340 if self._timetableWindow.hasFlightPairs:
1341 self._timetableWindow.show_all()
1342 else:
1343 date = datetime.date.today()
1344 self._timetableWindow.setTypes(self.loginResult.types)
1345 self._timetableWindow.setDate(date)
1346 self.updateTimeTable(date)
1347 self.beginBusy(xstr("timetable_query_busy"))
1348
1349 def updateTimeTable(self, date):
1350 """Update the time table for the given date."""
1351 self.beginBusy(xstr("timetable_query_busy"))
1352 self._timetableWindow.set_sensitive(False)
1353 window = self._timetableWindow.get_window()
1354 if window is not None:
1355 window.set_cursor(self._busyCursor)
1356 self.webHandler.getTimetable(self._timetableCallback, date,
1357 self.loginResult.types)
1358
1359 def _timetableCallback(self, returned, result):
1360 """Called when the timetable has been received."""
1361 GObject.idle_add(self._handleTimetable, returned, result)
1362
1363 def _handleTimetable(self, returned, result):
1364 """Handle the result of the query for the timetable."""
1365 self.endBusy()
1366 window = self._timetableWindow.get_window()
1367 if window is not None:
1368 window.set_cursor(None)
1369 self._timetableWindow.set_sensitive(True)
1370 if returned:
1371 self._timetableWindow.setFlightPairs(result.flightPairs)
1372 self._timetableWindow.show_all()
1373 else:
1374 dialog = Gtk.MessageDialog(parent = self.mainWindow,
1375 type = Gtk.MessageType.ERROR,
1376 message_format = xstr("timetable_failed"))
1377 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1378 dialog.set_title(WINDOW_TITLE_BASE)
1379 dialog.run()
1380 dialog.hide()
1381 self._timetableWindow.clear()
1382
1383 def showFlights(self, menuItem):
1384 """Callback for showing the flight list."""
1385 if self._flightsWindow.hasFlights:
1386 self._flightsWindow.show_all()
1387 else:
1388 self.beginBusy(xstr("acceptedflt_query_busy"))
1389 self.webHandler.getAcceptedFlights(self._acceptedFlightsCallback)
1390
1391 def _acceptedFlightsCallback(self, returned, result):
1392 """Called when the accepted flights have been received."""
1393 GObject.idle_add(self._handleAcceptedFlights, returned, result)
1394
1395 def _handleAcceptedFlights(self, returned, result):
1396 """Handle the result of the query for accepted flights."""
1397 self.endBusy()
1398 if returned:
1399 self._flightsWindow.clear()
1400 for flight in result.flights:
1401 self._flightsWindow.addFlight(flight)
1402 self._flightsWindow.show_all()
1403 else:
1404 dialog = Gtk.MessageDialog(parent = self.mainWindow,
1405 type = Gtk.MessageType.ERROR,
1406 message_format = xstr("acceptedflt_failed"))
1407 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1408 dialog.set_title(WINDOW_TITLE_BASE)
1409 dialog.run()
1410 dialog.hide()
1411
1412 def _hideTimetableWindow(self, window, event):
1413 """Hide the window of the timetable."""
1414 self._timetableWindow.hide()
1415 return True
1416
1417 def _hideFlightsWindow(self, window, event):
1418 """Hide the window of the accepted flights."""
1419 self._flightsWindow.hide()
1420 return True
1421
1422 def _editChecklist(self, menuItem):
1423 """Callback for editing the checklists."""
1424 self._checklistEditor.run()
1425
1426 def _editApproachCallouts(self, menuItem):
1427 """Callback for editing the approach callouts."""
1428 self._approachCalloutsEditor.run()
1429
1430 def _editPreferences(self, menuItem):
1431 """Callback for editing the preferences."""
1432 self._clearHotkeys()
1433 self._preferences.run(self.config)
1434 self._setupTimeSync()
1435 self._listenHotkeys()
1436
1437 def _clearCEFCache(self, menuItem):
1438 """Callback for clearing the CEF cache."""
1439 if askYesNo(xstr("clear_cef_cache_confirmation"),
1440 parent = self._mainWindow):
1441 self.restart(clearCEFCache = True)
1442
1443 def _reportBug(self, menuItem):
1444 """Callback for reporting a bug."""
1445 self._bugReportDialog.run()
1446
1447 def _setupTimeSync(self):
1448 """Enable or disable the simulator time synchronization based on the
1449 configuration."""
1450 simulator = self._simulator
1451 if simulator is not None:
1452 if self.config.syncFSTime:
1453 simulator.enableTimeSync()
1454 else:
1455 simulator.disableTimeSync()
1456
1457 def viewPIREP(self, pirep):
1458 """Display the PIREP viewer window with the given PIREP."""
1459 self._pirepViewer.setPIREP(pirep)
1460 self._pirepViewer.show_all()
1461 self._pirepViewer.run()
1462 self._pirepViewer.hide()
1463
1464 def viewMessagedPIREP(self, pirep):
1465 """Display the PIREP viewer window with the given PIREP containing
1466 messages as well."""
1467 self._messagedPIREPViewer.setPIREP(pirep)
1468 self._messagedPIREPViewer.show_all()
1469 self._messagedPIREPViewer.run()
1470 self._messagedPIREPViewer.hide()
1471
1472 def editPIREP(self, pirep):
1473 """Display the PIREP editor window and allow editing the PIREP."""
1474 self._pirepEditor.setPIREP(pirep)
1475 self._pirepEditor.show_all()
1476 if self._pirepEditor.run()==Gtk.ResponseType.OK:
1477 self.beginBusy(xstr("pirepEdit_save_busy"))
1478 self.webHandler.sendPIREP(self._pirepUpdatedCallback, pirep,
1479 update = True)
1480 else:
1481 self._pirepEditor.hide()
1482
1483 def _pirepUpdatedCallback(self, returned, result):
1484 """Callback for the PIREP updating result."""
1485 GObject.idle_add(self._handlePIREPUpdated, returned, result)
1486
1487 def _handlePIREPUpdated(self, returned, result):
1488 """Callback for the PIREP updating result."""
1489 self.endBusy()
1490 secondaryMarkup = None
1491 type = Gtk.MessageType.ERROR
1492 if returned:
1493 if result.success:
1494 type = None
1495 elif result.alreadyFlown:
1496 messageFormat = xstr("sendPIREP_already")
1497 secondaryMarkup = xstr("sendPIREP_already_sec")
1498 elif result.notAvailable:
1499 messageFormat = xstr("sendPIREP_notavail")
1500 else:
1501 messageFormat = xstr("sendPIREP_unknown")
1502 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1503 else:
1504 print("PIREP sending failed", result)
1505 messageFormat = xstr("sendPIREP_failed")
1506 secondaryMarkup = xstr("sendPIREP_failed_sec")
1507
1508 if type is not None:
1509 dialog = Gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1510 type = type, message_format = messageFormat)
1511 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1512 dialog.set_title(WINDOW_TITLE_BASE)
1513 if secondaryMarkup is not None:
1514 dialog.format_secondary_markup(secondaryMarkup)
1515
1516 dialog.run()
1517 dialog.hide()
1518
1519 self._pirepEditor.hide()
1520
1521 def _loadPIREP(self, menuItem):
1522 """Load a PIREP for sending."""
1523 dialog = self._getLoadPirepDialog()
1524
1525 if self._lastLoadedPIREP:
1526 dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP))
1527 else:
1528 pirepDirectory = self.config.pirepDirectory
1529 if pirepDirectory is not None:
1530 dialog.set_current_folder(pirepDirectory)
1531
1532 result = dialog.run()
1533 dialog.hide()
1534
1535 if result==Gtk.ResponseType.OK:
1536 self._lastLoadedPIREP = dialog.get_filename()
1537
1538 pirep = PIREP.load(self._lastLoadedPIREP)
1539 if pirep is None:
1540 dialog = Gtk.MessageDialog(parent = self._mainWindow,
1541 type = Gtk.MessageType.ERROR,
1542 message_format = xstr("loadPIREP_failed"))
1543 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1544 dialog.set_title(WINDOW_TITLE_BASE)
1545 dialog.format_secondary_markup(xstr("loadPIREP_failed_sec"))
1546 dialog.run()
1547 dialog.hide()
1548 else:
1549 dialog = self._getSendLoadedDialog(pirep)
1550 dialog.show_all()
1551 while True:
1552 result = dialog.run()
1553
1554 if result==Gtk.ResponseType.OK:
1555 self.sendPIREP(pirep)
1556 elif result==1:
1557 self.viewPIREP(pirep)
1558 else:
1559 break
1560
1561 dialog.hide()
1562
1563 def _getLoadPirepDialog(self):
1564 """Get the PIREP loading file chooser dialog.
1565
1566 If it is not created yet, it will be created."""
1567 if self._loadPIREPDialog is None:
1568 dialog = Gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
1569 xstr("loadPIREP_browser_title"),
1570 action = Gtk.FileChooserAction.OPEN,
1571 buttons = (Gtk.STOCK_CANCEL,
1572 Gtk.ResponseType.CANCEL,
1573 Gtk.STOCK_OK, Gtk.ResponseType.OK),
1574 parent = self._mainWindow)
1575 dialog.set_modal(True)
1576
1577
1578 filter = Gtk.FileFilter()
1579 filter.set_name(xstr("file_filter_pireps"))
1580 filter.add_pattern("*.pirep")
1581 dialog.add_filter(filter)
1582
1583 filter = Gtk.FileFilter()
1584 filter.set_name(xstr("file_filter_all"))
1585 filter.add_pattern("*.*")
1586 dialog.add_filter(filter)
1587
1588 self._loadPIREPDialog = dialog
1589
1590 return self._loadPIREPDialog
1591
1592 def _getSendLoadedDialog(self, pirep):
1593 """Get a dialog displaying the main information of the flight from the
1594 PIREP and providing Cancel and Send buttons."""
1595 dialog = Gtk.Dialog(title = WINDOW_TITLE_BASE + " - " +
1596 xstr("loadPIREP_send_title"),
1597 parent = self._mainWindow,
1598 flags = Gtk.DialogFlags.MODAL)
1599
1600 contentArea = dialog.get_content_area()
1601
1602 label = Gtk.Label(xstr("loadPIREP_send_help"))
1603 alignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
1604 xscale = 0.0, yscale = 0.0)
1605 alignment.set_padding(padding_top = 16, padding_bottom = 0,
1606 padding_left = 48, padding_right = 48)
1607 alignment.add(label)
1608 contentArea.pack_start(alignment, False, False, 8)
1609
1610 table = Gtk.Table(5, 2)
1611 tableAlignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
1612 xscale = 0.0, yscale = 0.0)
1613 tableAlignment.set_padding(padding_top = 0, padding_bottom = 32,
1614 padding_left = 48, padding_right = 48)
1615 table.set_row_spacings(4)
1616 table.set_col_spacings(16)
1617 tableAlignment.add(table)
1618 contentArea.pack_start(tableAlignment, True, True, 8)
1619
1620 bookedFlight = pirep.bookedFlight
1621
1622 label = Gtk.Label("<b>" + xstr("loadPIREP_send_flightno") + "</b>")
1623 label.set_use_markup(True)
1624 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1625 xscale = 0.0, yscale = 0.0)
1626 labelAlignment.add(label)
1627 table.attach(labelAlignment, 0, 1, 0, 1)
1628
1629 label = Gtk.Label(bookedFlight.callsign)
1630 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1631 xscale = 0.0, yscale = 0.0)
1632 labelAlignment.add(label)
1633 table.attach(labelAlignment, 1, 2, 0, 1)
1634
1635 label = Gtk.Label("<b>" + xstr("loadPIREP_send_date") + "</b>")
1636 label.set_use_markup(True)
1637 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1638 xscale = 0.0, yscale = 0.0)
1639 labelAlignment.add(label)
1640 table.attach(labelAlignment, 0, 1, 1, 2)
1641
1642 label = Gtk.Label(str(bookedFlight.departureTime.date()))
1643 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1644 xscale = 0.0, yscale = 0.0)
1645 labelAlignment.add(label)
1646 table.attach(labelAlignment, 1, 2, 1, 2)
1647
1648 label = Gtk.Label("<b>" + xstr("loadPIREP_send_from") + "</b>")
1649 label.set_use_markup(True)
1650 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1651 xscale = 0.0, yscale = 0.0)
1652 labelAlignment.add(label)
1653 table.attach(labelAlignment, 0, 1, 2, 3)
1654
1655 label = Gtk.Label(bookedFlight.departureICAO)
1656 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1657 xscale = 0.0, yscale = 0.0)
1658 labelAlignment.add(label)
1659 table.attach(labelAlignment, 1, 2, 2, 3)
1660
1661 label = Gtk.Label("<b>" + xstr("loadPIREP_send_to") + "</b>")
1662 label.set_use_markup(True)
1663 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1664 xscale = 0.0, yscale = 0.0)
1665 labelAlignment.add(label)
1666 table.attach(labelAlignment, 0, 1, 3, 4)
1667
1668 label = Gtk.Label(bookedFlight.arrivalICAO)
1669 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1670 xscale = 0.0, yscale = 0.0)
1671 labelAlignment.add(label)
1672 table.attach(labelAlignment, 1, 2, 3, 4)
1673
1674 label = Gtk.Label("<b>" + xstr("loadPIREP_send_rating") + "</b>")
1675 label.set_use_markup(True)
1676 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1677 xscale = 0.0, yscale = 0.0)
1678 labelAlignment.add(label)
1679 table.attach(labelAlignment, 0, 1, 4, 5)
1680
1681 rating = pirep.rating
1682 label = Gtk.Label()
1683 if rating<0:
1684 label.set_markup('<b><span foreground="red">NO GO</span></b>')
1685 else:
1686 label.set_text("%.1f %%" % (rating,))
1687
1688 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1689 xscale = 0.0, yscale = 0.0)
1690 labelAlignment.add(label)
1691 table.attach(labelAlignment, 1, 2, 4, 5)
1692
1693 dialog.add_button(xstr("button_cancel"), Gtk.ResponseType.REJECT)
1694 dialog.add_button(xstr("viewPIREP"), 1)
1695 dialog.add_button(xstr("sendPIREP"), Gtk.ResponseType.OK)
1696
1697 return dialog
1698
1699 def sendPIREP(self, pirep, callback = None):
1700 """Send the given PIREP."""
1701 self.beginBusy(xstr("sendPIREP_busy"))
1702 self._sendPIREPCallback = callback
1703 self.webHandler.sendPIREP(self._pirepSentCallback, pirep)
1704
1705 def _pirepSentCallback(self, returned, result):
1706 """Callback for the PIREP sending result."""
1707 GObject.idle_add(self._handlePIREPSent, returned, result)
1708
1709 def _handlePIREPSent(self, returned, result):
1710 """Callback for the PIREP sending result."""
1711 self.endBusy()
1712 secondaryMarkup = None
1713 type = Gtk.MessageType.ERROR
1714 if returned:
1715 if result.success:
1716 type = Gtk.MessageType.INFO
1717 messageFormat = xstr("sendPIREP_success")
1718 secondaryMarkup = xstr("sendPIREP_success_sec")
1719 elif result.alreadyFlown:
1720 messageFormat = xstr("sendPIREP_already")
1721 secondaryMarkup = xstr("sendPIREP_already_sec")
1722 elif result.notAvailable:
1723 messageFormat = xstr("sendPIREP_notavail")
1724 else:
1725 messageFormat = xstr("sendPIREP_unknown")
1726 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1727 else:
1728 print("PIREP sending failed", result)
1729 messageFormat = xstr("sendPIREP_failed")
1730 secondaryMarkup = xstr("sendPIREP_failed_sec")
1731
1732 dialog = Gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1733 type = type, message_format = messageFormat)
1734 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1735 dialog.set_title(WINDOW_TITLE_BASE)
1736 if secondaryMarkup is not None:
1737 dialog.format_secondary_markup(secondaryMarkup)
1738
1739 dialog.run()
1740 dialog.hide()
1741
1742 callback = self._sendPIREPCallback
1743 self._sendPIREPCallback = None
1744 if callback is not None:
1745 callback(returned, result)
1746
1747 def sendBugReport(self, summary, description, email, callback = None):
1748 """Send the bug report with the given data."""
1749 description += "\n\n" + ("=" * 40)
1750 description += "\n\nThe contents of the log:\n\n"
1751
1752 for (timestampString, text) in self._logger.lines:
1753 description += str(formatFlightLogLine(timestampString, text))
1754
1755 description += "\n\n" + ("=" * 40)
1756 description += "\n\nThe contents of the debug log:\n\n"
1757
1758 buffer = self._debugLogView.get_buffer()
1759 description += buffer.get_text(buffer.get_start_iter(),
1760 buffer.get_end_iter(), True)
1761
1762 self.beginBusy(xstr("sendBugReport_busy"))
1763 self._sendBugReportCallback = callback
1764 self.webHandler.sendBugReport(self._bugReportSentCallback,
1765 summary, description, email)
1766
1767 def _cefInitialized(self):
1768 """Called when CEF has been initialized."""
1769 self._acars.start()
1770 cef.initializeSimBrief()
1771
1772 def _bugReportSentCallback(self, returned, result):
1773 """Callback function for the bug report sending result."""
1774 GObject.idle_add(self._handleBugReportSent, returned, result)
1775
1776 def _handleBugReportSent(self, returned, result):
1777 """Callback for the bug report sending result."""
1778 self.endBusy()
1779 secondaryMarkup = None
1780 type = Gtk.MessageType.ERROR
1781 if returned:
1782 if result.success:
1783 type = Gtk.MessageType.INFO
1784 messageFormat = xstr("sendBugReport_success") % (result.ticketID,)
1785 secondaryMarkup = xstr("sendBugReport_success_sec")
1786 else:
1787 messageFormat = xstr("sendBugReport_error")
1788 secondaryMarkup = xstr("sendBugReport_siteerror_sec")
1789 else:
1790 messageFormat = xstr("sendBugReport_error")
1791 secondaryMarkup = xstr("sendBugReport_error_sec")
1792
1793 dialog = Gtk.MessageDialog(parent = self._wizard.gui._bugReportDialog,
1794 type = type, message_format = messageFormat)
1795 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1796 dialog.set_title(WINDOW_TITLE_BASE)
1797 if secondaryMarkup is not None:
1798 dialog.format_secondary_markup(secondaryMarkup)
1799
1800 dialog.run()
1801 dialog.hide()
1802
1803 callback = self._sendBugReportCallback
1804 self._sendBugReportCallback = None
1805 if callback is not None:
1806 callback(returned, result)
1807
1808 def _listenHotkeys(self):
1809 """Setup the hotkeys based on the configuration."""
1810 if self._hotkeySetID is None and self._simulator is not None:
1811 self._pilotHotkeyIndex = None
1812 self._checklistHotkeyIndex = None
1813
1814 hotkeys = []
1815
1816 config = self.config
1817 if config.enableSounds and config.pilotControlsSounds:
1818 self._pilotHotkeyIndex = len(hotkeys)
1819 hotkeys.append(config.pilotHotkey)
1820
1821 if config.enableChecklists:
1822 self._checklistHotkeyIndex = len(hotkeys)
1823 hotkeys.append(config.checklistHotkey)
1824
1825 if hotkeys:
1826 self._hotkeySetID = \
1827 self._simulator.listenHotkeys(hotkeys, self._handleHotkeys)
1828
1829 def _clearHotkeys(self):
1830 """Clear the hotkeys."""
1831 if self._hotkeySetID is not None:
1832 self._hotkeySetID=None
1833 self._simulator.clearHotkeys()
1834
1835 def _handleHotkeys(self, id, hotkeys):
1836 """Handle the hotkeys."""
1837 if id==self._hotkeySetID:
1838 for index in hotkeys:
1839 if index==self._pilotHotkeyIndex:
1840 print("gui.GUI._handleHotkeys: pilot hotkey pressed")
1841 self._flight.pilotHotkeyPressed()
1842 elif index==self._checklistHotkeyIndex:
1843 print("gui.GUI._handleHotkeys: checklist hotkey pressed")
1844 self._flight.checklistHotkeyPressed()
1845 else:
1846 print("gui.GUI._handleHotkeys: unhandled hotkey index:", index)
1847
1848 def _showManual(self, menuitem):
1849 """Show the user's manual."""
1850 webbrowser.open(url ="file://" +
1851 os.path.join(self._programDirectory, "doc", "manual",
1852 getLanguage(), "index.html"),
1853 new = 1)
1854
1855 def _showAbout(self, menuitem):
1856 """Show the about dialog."""
1857 dialog = self._getAboutDialog()
1858 dialog.show_all()
1859 dialog.run()
1860 dialog.hide()
1861
1862 def _getAboutDialog(self):
1863 """Get the about dialog.
1864
1865 If it does not exist yet, it will be created."""
1866 if self._aboutDialog is None:
1867 dialog = Gtk.AboutDialog()
1868 dialog.set_transient_for(self._mainWindow)
1869 dialog.set_modal(True)
1870
1871 logoPath = os.path.join(self._programDirectory, "logo.png")
1872 logo = GdkPixbuf.Pixbuf.new_from_file(logoPath)
1873 dialog.set_logo(logo)
1874
1875 dialog.set_program_name(PROGRAM_NAME)
1876 dialog.set_version(const.VERSION)
1877 dialog.set_copyright("(c) 2012 - 2023 by István Váradi")
1878 dialog.set_website("http://mlx.varadiistvan.hu")
1879 dialog.set_website_label(xstr("about_website"))
1880
1881 isHungarian = getLanguage()=="hu"
1882 authors = []
1883 for (familyName, firstName, role) in GUI._authors:
1884 author = "%s %s" % \
1885 (familyName if isHungarian else firstName,
1886 firstName if isHungarian else familyName)
1887 role = xstr("about_role_" + role)
1888 authors.append(author + " (" + role + ")")
1889 dialog.set_authors(authors)
1890
1891 dialog.set_license(xstr("about_license"))
1892
1893 self._aboutDialog = dialog
1894
1895 return self._aboutDialog
1896
1897 def _showAboutURL(self, dialog, link, user_data):
1898 """Show the about URL."""
1899 webbrowser.open(url = link, new = 1)
1900
1901 def _setTakeoffAntiIceOn(self, value):
1902 """Set the anti-ice on indicator."""
1903 self._wizard.takeoffAntiIceOn = value
1904
1905 def _setLandingAntiIceOn(self, value):
1906 """Set the anti-ice on indicator."""
1907 self._wizard.landingAntiIceOn = value
1908
1909 def _getCredentialsCallback(self):
1910 """Called when the web handler asks for the credentials."""
1911 # FIXME: this is almost the same as
1912 # SimBriefSetupPage._getCredentialsCallback
1913 with self._credentialsCondition:
1914 self._credentialsAvailable = False
1915
1916 GObject.idle_add(self._getCredentials)
1917
1918 while not self._credentialsAvailable:
1919 self._credentialsCondition.wait()
1920
1921 return (self._credentialsUserName, self._credentialsPassword)
1922
1923 def _getCredentials(self):
1924 """Get the credentials."""
1925 # FIXME: this is almost the same as
1926 # SimBriefSetupPage._getCredentials
1927 with self._credentialsCondition:
1928 config = self.config
1929
1930 dialog = CredentialsDialog(self, config.pilotID, config.password,
1931 xstr("login_title"),
1932 xstr("button_cancel"),
1933 xstr("button_ok"),
1934 xstr("label_pilotID"),
1935 xstr("login_pilotID_tooltip"),
1936 xstr("label_password"),
1937 xstr("login_password_tooltip"),
1938 xstr("login_info"),
1939 config.rememberPassword,
1940 xstr("remember_password"),
1941 xstr("login_remember_tooltip"))
1942 response = dialog.run()
1943
1944 if response==Gtk.ResponseType.OK:
1945 self._credentialsUserName = dialog.userName
1946 self._credentialsPassword = dialog.password
1947 rememberPassword = dialog.rememberPassword
1948
1949 config.pilotID = self._credentialsUserName
1950
1951 config.password = \
1952 self._credentialsPassword if rememberPassword else ""
1953 config.rememberPassword = rememberPassword
1954
1955 config.save()
1956 else:
1957 self._credentialsUserName = None
1958 self._credentialsPassword = None
1959
1960 self._credentialsAvailable = True
1961 self._credentialsCondition.notify()
1962
1963 def _updateDone(self):
1964 """Called when the update is done.
1965
1966 It checks if we already know the PID, and if not, asks the user whether
1967 to register."""
1968 cef.initialize(self._cefInitialized)
1969
1970 if not self.config.pilotID and not self.config.password:
1971 dialog = Gtk.MessageDialog(parent = self._mainWindow,
1972 type = Gtk.MessageType.QUESTION,
1973 message_format = xstr("register_ask"))
1974
1975 dialog.set_title(WINDOW_TITLE_BASE)
1976 dialog.format_secondary_markup(xstr("register_ask_sec"))
1977
1978 dialog.add_button(xstr("button_cancel"), 0)
1979 dialog.add_button(xstr("button_register"), 1)
1980 dialog.set_default_response(1)
1981
1982 result = dialog.run()
1983 dialog.hide()
1984 if result == 1:
1985 self._wizard.jumpPage("register")
Note: See TracBrowser for help on using the repository browser.