source: src/mlx/gui/gui.py@ 1105:2d0d642d122d

python3
Last change on this file since 1105:2d0d642d122d was 1099:a2d5a4d1c6c1, checked in by István Váradi <ivaradi@…>, 16 months ago

Various minor bugfixes

File size: 73.3 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 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._mainWindow.set_resizable(self.config.mainWindowResizable)
1435 self._setupTimeSync()
1436 self._listenHotkeys()
1437
1438 def _clearCEFCache(self, menuItem):
1439 """Callback for clearing the CEF cache."""
1440 if askYesNo(xstr("clear_cef_cache_confirmation"),
1441 parent = self._mainWindow):
1442 self.restart(clearCEFCache = True)
1443
1444 def _reportBug(self, menuItem):
1445 """Callback for reporting a bug."""
1446 self._bugReportDialog.run()
1447
1448 def _setupTimeSync(self):
1449 """Enable or disable the simulator time synchronization based on the
1450 configuration."""
1451 simulator = self._simulator
1452 if simulator is not None:
1453 if self.config.syncFSTime:
1454 simulator.enableTimeSync()
1455 else:
1456 simulator.disableTimeSync()
1457
1458 def viewPIREP(self, pirep):
1459 """Display the PIREP viewer window with the given PIREP."""
1460 self._pirepViewer.setPIREP(pirep)
1461 self._pirepViewer.show_all()
1462 self._pirepViewer.run()
1463 self._pirepViewer.hide()
1464
1465 def viewMessagedPIREP(self, pirep):
1466 """Display the PIREP viewer window with the given PIREP containing
1467 messages as well."""
1468 self._messagedPIREPViewer.setPIREP(pirep)
1469 self._messagedPIREPViewer.show_all()
1470 self._messagedPIREPViewer.run()
1471 self._messagedPIREPViewer.hide()
1472
1473 def editPIREP(self, pirep):
1474 """Display the PIREP editor window and allow editing the PIREP."""
1475 self._pirepEditor.setPIREP(pirep)
1476 self._pirepEditor.show_all()
1477 if self._pirepEditor.run()==Gtk.ResponseType.OK:
1478 self.beginBusy(xstr("pirepEdit_save_busy"))
1479 self.webHandler.sendPIREP(self._pirepUpdatedCallback, pirep,
1480 update = True)
1481 else:
1482 self._pirepEditor.hide()
1483
1484 def _pirepUpdatedCallback(self, returned, result):
1485 """Callback for the PIREP updating result."""
1486 GObject.idle_add(self._handlePIREPUpdated, returned, result)
1487
1488 def _handlePIREPUpdated(self, returned, result):
1489 """Callback for the PIREP updating result."""
1490 self.endBusy()
1491 secondaryMarkup = None
1492 type = Gtk.MessageType.ERROR
1493 if returned:
1494 if result.success:
1495 type = None
1496 elif result.alreadyFlown:
1497 messageFormat = xstr("sendPIREP_already")
1498 secondaryMarkup = xstr("sendPIREP_already_sec")
1499 elif result.notAvailable:
1500 messageFormat = xstr("sendPIREP_notavail")
1501 else:
1502 messageFormat = xstr("sendPIREP_unknown")
1503 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1504 else:
1505 print("PIREP sending failed", result)
1506 messageFormat = xstr("sendPIREP_failed")
1507 secondaryMarkup = xstr("sendPIREP_failed_sec")
1508
1509 if type is not None:
1510 dialog = Gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1511 type = type, message_format = messageFormat)
1512 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1513 dialog.set_title(WINDOW_TITLE_BASE)
1514 if secondaryMarkup is not None:
1515 dialog.format_secondary_markup(secondaryMarkup)
1516
1517 dialog.run()
1518 dialog.hide()
1519
1520 self._pirepEditor.hide()
1521
1522 def _loadPIREP(self, menuItem):
1523 """Load a PIREP for sending."""
1524 dialog = self._getLoadPirepDialog()
1525
1526 if self._lastLoadedPIREP:
1527 dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP))
1528 else:
1529 pirepDirectory = self.config.pirepDirectory
1530 if pirepDirectory is not None:
1531 dialog.set_current_folder(pirepDirectory)
1532
1533 result = dialog.run()
1534 dialog.hide()
1535
1536 if result==Gtk.ResponseType.OK:
1537 self._lastLoadedPIREP = dialog.get_filename()
1538
1539 pirep = PIREP.load(self._lastLoadedPIREP)
1540 if pirep is None:
1541 dialog = Gtk.MessageDialog(parent = self._mainWindow,
1542 type = Gtk.MessageType.ERROR,
1543 message_format = xstr("loadPIREP_failed"))
1544 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1545 dialog.set_title(WINDOW_TITLE_BASE)
1546 dialog.format_secondary_markup(xstr("loadPIREP_failed_sec"))
1547 dialog.run()
1548 dialog.hide()
1549 else:
1550 dialog = self._getSendLoadedDialog(pirep)
1551 dialog.show_all()
1552 while True:
1553 result = dialog.run()
1554
1555 if result==Gtk.ResponseType.OK:
1556 self.sendPIREP(pirep)
1557 elif result==1:
1558 self.viewPIREP(pirep)
1559 else:
1560 break
1561
1562 dialog.hide()
1563
1564 def _getLoadPirepDialog(self):
1565 """Get the PIREP loading file chooser dialog.
1566
1567 If it is not created yet, it will be created."""
1568 if self._loadPIREPDialog is None:
1569 dialog = Gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
1570 xstr("loadPIREP_browser_title"),
1571 action = Gtk.FileChooserAction.OPEN,
1572 buttons = (Gtk.STOCK_CANCEL,
1573 Gtk.ResponseType.CANCEL,
1574 Gtk.STOCK_OK, Gtk.ResponseType.OK),
1575 parent = self._mainWindow)
1576 dialog.set_modal(True)
1577
1578
1579 filter = Gtk.FileFilter()
1580 filter.set_name(xstr("file_filter_pireps"))
1581 filter.add_pattern("*.pirep")
1582 dialog.add_filter(filter)
1583
1584 filter = Gtk.FileFilter()
1585 filter.set_name(xstr("file_filter_all"))
1586 filter.add_pattern("*.*")
1587 dialog.add_filter(filter)
1588
1589 self._loadPIREPDialog = dialog
1590
1591 return self._loadPIREPDialog
1592
1593 def _getSendLoadedDialog(self, pirep):
1594 """Get a dialog displaying the main information of the flight from the
1595 PIREP and providing Cancel and Send buttons."""
1596 dialog = Gtk.Dialog(title = WINDOW_TITLE_BASE + " - " +
1597 xstr("loadPIREP_send_title"),
1598 parent = self._mainWindow,
1599 flags = Gtk.DialogFlags.MODAL)
1600
1601 contentArea = dialog.get_content_area()
1602
1603 label = Gtk.Label(xstr("loadPIREP_send_help"))
1604 alignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
1605 xscale = 0.0, yscale = 0.0)
1606 alignment.set_padding(padding_top = 16, padding_bottom = 0,
1607 padding_left = 48, padding_right = 48)
1608 alignment.add(label)
1609 contentArea.pack_start(alignment, False, False, 8)
1610
1611 table = Gtk.Table(5, 2)
1612 tableAlignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
1613 xscale = 0.0, yscale = 0.0)
1614 tableAlignment.set_padding(padding_top = 0, padding_bottom = 32,
1615 padding_left = 48, padding_right = 48)
1616 table.set_row_spacings(4)
1617 table.set_col_spacings(16)
1618 tableAlignment.add(table)
1619 contentArea.pack_start(tableAlignment, True, True, 8)
1620
1621 bookedFlight = pirep.bookedFlight
1622
1623 label = Gtk.Label("<b>" + xstr("loadPIREP_send_flightno") + "</b>")
1624 label.set_use_markup(True)
1625 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1626 xscale = 0.0, yscale = 0.0)
1627 labelAlignment.add(label)
1628 table.attach(labelAlignment, 0, 1, 0, 1)
1629
1630 label = Gtk.Label(bookedFlight.callsign)
1631 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1632 xscale = 0.0, yscale = 0.0)
1633 labelAlignment.add(label)
1634 table.attach(labelAlignment, 1, 2, 0, 1)
1635
1636 label = Gtk.Label("<b>" + xstr("loadPIREP_send_date") + "</b>")
1637 label.set_use_markup(True)
1638 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1639 xscale = 0.0, yscale = 0.0)
1640 labelAlignment.add(label)
1641 table.attach(labelAlignment, 0, 1, 1, 2)
1642
1643 label = Gtk.Label(str(bookedFlight.departureTime.date()))
1644 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1645 xscale = 0.0, yscale = 0.0)
1646 labelAlignment.add(label)
1647 table.attach(labelAlignment, 1, 2, 1, 2)
1648
1649 label = Gtk.Label("<b>" + xstr("loadPIREP_send_from") + "</b>")
1650 label.set_use_markup(True)
1651 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1652 xscale = 0.0, yscale = 0.0)
1653 labelAlignment.add(label)
1654 table.attach(labelAlignment, 0, 1, 2, 3)
1655
1656 label = Gtk.Label(bookedFlight.departureICAO)
1657 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1658 xscale = 0.0, yscale = 0.0)
1659 labelAlignment.add(label)
1660 table.attach(labelAlignment, 1, 2, 2, 3)
1661
1662 label = Gtk.Label("<b>" + xstr("loadPIREP_send_to") + "</b>")
1663 label.set_use_markup(True)
1664 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1665 xscale = 0.0, yscale = 0.0)
1666 labelAlignment.add(label)
1667 table.attach(labelAlignment, 0, 1, 3, 4)
1668
1669 label = Gtk.Label(bookedFlight.arrivalICAO)
1670 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1671 xscale = 0.0, yscale = 0.0)
1672 labelAlignment.add(label)
1673 table.attach(labelAlignment, 1, 2, 3, 4)
1674
1675 label = Gtk.Label("<b>" + xstr("loadPIREP_send_rating") + "</b>")
1676 label.set_use_markup(True)
1677 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1678 xscale = 0.0, yscale = 0.0)
1679 labelAlignment.add(label)
1680 table.attach(labelAlignment, 0, 1, 4, 5)
1681
1682 rating = pirep.rating
1683 label = Gtk.Label()
1684 if rating<0:
1685 label.set_markup('<b><span foreground="red">NO GO</span></b>')
1686 else:
1687 label.set_text("%.1f %%" % (rating,))
1688
1689 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1690 xscale = 0.0, yscale = 0.0)
1691 labelAlignment.add(label)
1692 table.attach(labelAlignment, 1, 2, 4, 5)
1693
1694 dialog.add_button(xstr("button_cancel"), Gtk.ResponseType.REJECT)
1695 dialog.add_button(xstr("viewPIREP"), 1)
1696 dialog.add_button(xstr("sendPIREP"), Gtk.ResponseType.OK)
1697
1698 return dialog
1699
1700 def sendPIREP(self, pirep, callback = None):
1701 """Send the given PIREP."""
1702 self.beginBusy(xstr("sendPIREP_busy"))
1703 self._sendPIREPCallback = callback
1704 self.webHandler.sendPIREP(self._pirepSentCallback, pirep)
1705
1706 def _pirepSentCallback(self, returned, result):
1707 """Callback for the PIREP sending result."""
1708 GObject.idle_add(self._handlePIREPSent, returned, result)
1709
1710 def _handlePIREPSent(self, returned, result):
1711 """Callback for the PIREP sending result."""
1712 self.endBusy()
1713 secondaryMarkup = None
1714 type = Gtk.MessageType.ERROR
1715 if returned:
1716 if result.success:
1717 type = Gtk.MessageType.INFO
1718 messageFormat = xstr("sendPIREP_success")
1719 secondaryMarkup = xstr("sendPIREP_success_sec")
1720 elif result.alreadyFlown:
1721 messageFormat = xstr("sendPIREP_already")
1722 secondaryMarkup = xstr("sendPIREP_already_sec")
1723 elif result.notAvailable:
1724 messageFormat = xstr("sendPIREP_notavail")
1725 else:
1726 messageFormat = xstr("sendPIREP_unknown")
1727 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1728 else:
1729 print("PIREP sending failed", result)
1730 messageFormat = xstr("sendPIREP_failed")
1731 secondaryMarkup = xstr("sendPIREP_failed_sec")
1732
1733 dialog = Gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1734 type = type, message_format = messageFormat)
1735 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1736 dialog.set_title(WINDOW_TITLE_BASE)
1737 if secondaryMarkup is not None:
1738 dialog.format_secondary_markup(secondaryMarkup)
1739
1740 dialog.run()
1741 dialog.hide()
1742
1743 callback = self._sendPIREPCallback
1744 self._sendPIREPCallback = None
1745 if callback is not None:
1746 callback(returned, result)
1747
1748 def sendBugReport(self, summary, description, email, callback = None):
1749 """Send the bug report with the given data."""
1750 description += "\n\n" + ("=" * 40)
1751 description += "\n\nThe contents of the log:\n\n"
1752
1753 for (timestampString, text) in self._logger.lines:
1754 description += str(formatFlightLogLine(timestampString, text))
1755
1756 description += "\n\n" + ("=" * 40)
1757 description += "\n\nThe contents of the debug log:\n\n"
1758
1759 buffer = self._debugLogView.get_buffer()
1760 description += buffer.get_text(buffer.get_start_iter(),
1761 buffer.get_end_iter(), True)
1762
1763 self.beginBusy(xstr("sendBugReport_busy"))
1764 self._sendBugReportCallback = callback
1765 self.webHandler.sendBugReport(self._bugReportSentCallback,
1766 summary, description, email)
1767
1768 def _cefInitialized(self):
1769 """Called when CEF has been initialized."""
1770 self._acars.start()
1771 cef.initializeSimBrief()
1772
1773 def _bugReportSentCallback(self, returned, result):
1774 """Callback function for the bug report sending result."""
1775 GObject.idle_add(self._handleBugReportSent, returned, result)
1776
1777 def _handleBugReportSent(self, returned, result):
1778 """Callback for the bug report sending result."""
1779 self.endBusy()
1780 secondaryMarkup = None
1781 type = Gtk.MessageType.ERROR
1782 if returned:
1783 if result.success:
1784 type = Gtk.MessageType.INFO
1785 messageFormat = xstr("sendBugReport_success") % (result.ticketID,)
1786 secondaryMarkup = xstr("sendBugReport_success_sec")
1787 else:
1788 messageFormat = xstr("sendBugReport_error")
1789 secondaryMarkup = xstr("sendBugReport_siteerror_sec")
1790 else:
1791 messageFormat = xstr("sendBugReport_error")
1792 secondaryMarkup = xstr("sendBugReport_error_sec")
1793
1794 dialog = Gtk.MessageDialog(parent = self._wizard.gui._bugReportDialog,
1795 type = type, message_format = messageFormat)
1796 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1797 dialog.set_title(WINDOW_TITLE_BASE)
1798 if secondaryMarkup is not None:
1799 dialog.format_secondary_markup(secondaryMarkup)
1800
1801 dialog.run()
1802 dialog.hide()
1803
1804 callback = self._sendBugReportCallback
1805 self._sendBugReportCallback = None
1806 if callback is not None:
1807 callback(returned, result)
1808
1809 def _listenHotkeys(self):
1810 """Setup the hotkeys based on the configuration."""
1811 if self._hotkeySetID is None and self._simulator is not None:
1812 self._pilotHotkeyIndex = None
1813 self._checklistHotkeyIndex = None
1814
1815 hotkeys = []
1816
1817 config = self.config
1818 if config.enableSounds and config.pilotControlsSounds:
1819 self._pilotHotkeyIndex = len(hotkeys)
1820 hotkeys.append(config.pilotHotkey)
1821
1822 if config.enableChecklists:
1823 self._checklistHotkeyIndex = len(hotkeys)
1824 hotkeys.append(config.checklistHotkey)
1825
1826 if hotkeys:
1827 self._hotkeySetID = \
1828 self._simulator.listenHotkeys(hotkeys, self._handleHotkeys)
1829
1830 def _clearHotkeys(self):
1831 """Clear the hotkeys."""
1832 if self._hotkeySetID is not None:
1833 self._hotkeySetID=None
1834 self._simulator.clearHotkeys()
1835
1836 def _handleHotkeys(self, id, hotkeys):
1837 """Handle the hotkeys."""
1838 if id==self._hotkeySetID:
1839 for index in hotkeys:
1840 if index==self._pilotHotkeyIndex:
1841 print("gui.GUI._handleHotkeys: pilot hotkey pressed")
1842 self._flight.pilotHotkeyPressed()
1843 elif index==self._checklistHotkeyIndex:
1844 print("gui.GUI._handleHotkeys: checklist hotkey pressed")
1845 self._flight.checklistHotkeyPressed()
1846 else:
1847 print("gui.GUI._handleHotkeys: unhandled hotkey index:", index)
1848
1849 def _showManual(self, menuitem):
1850 """Show the user's manual."""
1851 webbrowser.open(url ="file://" +
1852 os.path.join(self._programDirectory, "doc", "manual",
1853 getLanguage(), "index.html"),
1854 new = 1)
1855
1856 def _showAbout(self, menuitem):
1857 """Show the about dialog."""
1858 dialog = self._getAboutDialog()
1859 dialog.show_all()
1860 dialog.run()
1861 dialog.hide()
1862
1863 def _getAboutDialog(self):
1864 """Get the about dialog.
1865
1866 If it does not exist yet, it will be created."""
1867 if self._aboutDialog is None:
1868 dialog = Gtk.AboutDialog()
1869 dialog.set_transient_for(self._mainWindow)
1870 dialog.set_modal(True)
1871
1872 logoPath = os.path.join(self._programDirectory, "logo.png")
1873 logo = GdkPixbuf.Pixbuf.new_from_file(logoPath)
1874 dialog.set_logo(logo)
1875
1876 dialog.set_program_name(PROGRAM_NAME)
1877 dialog.set_version(const.VERSION)
1878 dialog.set_copyright("(c) 2012 - 2023 by István Váradi")
1879 dialog.set_website("http://mlx.varadiistvan.hu")
1880 dialog.set_website_label(xstr("about_website"))
1881
1882 isHungarian = getLanguage()=="hu"
1883 authors = []
1884 for (familyName, firstName, role) in GUI._authors:
1885 author = "%s %s" % \
1886 (familyName if isHungarian else firstName,
1887 firstName if isHungarian else familyName)
1888 role = xstr("about_role_" + role)
1889 authors.append(author + " (" + role + ")")
1890 dialog.set_authors(authors)
1891
1892 dialog.set_license(xstr("about_license"))
1893
1894 self._aboutDialog = dialog
1895
1896 return self._aboutDialog
1897
1898 def _showAboutURL(self, dialog, link, user_data):
1899 """Show the about URL."""
1900 webbrowser.open(url = link, new = 1)
1901
1902 def _setTakeoffAntiIceOn(self, value):
1903 """Set the anti-ice on indicator."""
1904 self._wizard.takeoffAntiIceOn = value
1905
1906 def _setLandingAntiIceOn(self, value):
1907 """Set the anti-ice on indicator."""
1908 self._wizard.landingAntiIceOn = value
1909
1910 def _getCredentialsCallback(self):
1911 """Called when the web handler asks for the credentials."""
1912 # FIXME: this is almost the same as
1913 # SimBriefSetupPage._getCredentialsCallback
1914 with self._credentialsCondition:
1915 self._credentialsAvailable = False
1916
1917 GObject.idle_add(self._getCredentials)
1918
1919 while not self._credentialsAvailable:
1920 self._credentialsCondition.wait()
1921
1922 return (self._credentialsUserName, self._credentialsPassword)
1923
1924 def _getCredentials(self):
1925 """Get the credentials."""
1926 # FIXME: this is almost the same as
1927 # SimBriefSetupPage._getCredentials
1928 with self._credentialsCondition:
1929 config = self.config
1930
1931 dialog = CredentialsDialog(self, config.pilotID, config.password,
1932 xstr("login_title"),
1933 xstr("button_cancel"),
1934 xstr("button_ok"),
1935 xstr("label_pilotID"),
1936 xstr("login_pilotID_tooltip"),
1937 xstr("label_password"),
1938 xstr("login_password_tooltip"),
1939 xstr("login_info"),
1940 config.rememberPassword,
1941 xstr("remember_password"),
1942 xstr("login_remember_tooltip"))
1943 response = dialog.run()
1944
1945 if response==Gtk.ResponseType.OK:
1946 self._credentialsUserName = dialog.userName
1947 self._credentialsPassword = dialog.password
1948 rememberPassword = dialog.rememberPassword
1949
1950 config.pilotID = self._credentialsUserName
1951
1952 config.password = \
1953 self._credentialsPassword if rememberPassword else ""
1954 config.rememberPassword = rememberPassword
1955
1956 config.save()
1957 else:
1958 self._credentialsUserName = None
1959 self._credentialsPassword = None
1960
1961 self._credentialsAvailable = True
1962 self._credentialsCondition.notify()
1963
1964 def _updateDone(self):
1965 """Called when the update is done.
1966
1967 It checks if we already know the PID, and if not, asks the user whether
1968 to register."""
1969 cef.initialize(self._cefInitialized)
1970
1971 if not self.config.pilotID and not self.config.password:
1972 dialog = Gtk.MessageDialog(parent = self._mainWindow,
1973 type = Gtk.MessageType.QUESTION,
1974 message_format = xstr("register_ask"))
1975
1976 dialog.set_title(WINDOW_TITLE_BASE)
1977 dialog.format_secondary_markup(xstr("register_ask_sec"))
1978
1979 dialog.add_button(xstr("button_cancel"), 0)
1980 dialog.add_button(xstr("button_register"), 1)
1981 dialog.set_default_response(1)
1982
1983 result = dialog.run()
1984 dialog.hide()
1985 if result == 1:
1986 self._wizard.jumpPage("register")
Note: See TracBrowser for help on using the repository browser.