source: src/mlx/gui/gui.py@ 1068:928cd01e06c6

python3
Last change on this file since 1068:928cd01e06c6 was 1046:12bbf8604117, checked in by István Váradi <ivaradi@…>, 3 years ago

New style briefing (re #357)

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