source: src/mlx/gui/gui.py@ 1045:110ed385e042

python3
Last change on this file since 1045:110ed385e042 was 1037:687ff2fb8cab, checked in by István Váradi <ivaradi@…>, 2 years ago

Stepped version and copyright date (re #357)

File size: 72.2 KB
Line 
1# -*- coding: utf-8 -*-
2
3from .statusicon import StatusIcon
4from .statusbar import Statusbar
5from .info import FlightInfo
6from .update import Updater
7from mlx.gui.common import *
8from mlx.gui.flight import Wizard
9from mlx.gui.monitor import MonitorWindow
10from mlx.gui.weighthelp import WeightHelp
11from mlx.gui.gates import FleetGateStatus
12from mlx.gui.prefs import Preferences
13from mlx.gui.checklist import ChecklistEditor
14from mlx.gui.callouts import ApproachCalloutsEditor
15from mlx.gui.flightlist import AcceptedFlightsWindow
16from mlx.gui.pirep import PIREPViewer, PIREPEditor
17from mlx.gui.bugreport import BugReportDialog
18from mlx.gui.acars import ACARS
19from mlx.gui.timetable import TimetableWindow
20from . import cef
21
22import mlx.const as const
23import mlx.fs as fs
24import mlx.flight as flight
25import mlx.logger as logger
26import mlx.acft as acft
27import mlx.web as web
28import mlx.singleton as singleton
29import mlx.airports as airports
30from mlx.i18n import xstr, getLanguage
31from mlx.pirep import PIREP
32
33import time
34import threading
35import sys
36import datetime
37import webbrowser
38
39#------------------------------------------------------------------------------
40
41## @package mlx.gui.gui
42#
43# The main GUI class.
44#
45# The \ref GUI class is the main class of the GUI. It is a connection listener,
46# and aggregates all the windows, the menu, etc. It maintains the connection to
47# the simulator as well as the flight object.
48
49#------------------------------------------------------------------------------
50
51class GUI(fs.ConnectionListener):
52 """The main GUI class."""
53 _authors = [ ("Váradi", "István", "prog_test"),
54 ("Galyassy", "Tamás", "negotiation"),
55 ("Kurják", "Ákos", "test"),
56 ("Nagy", "Dániel", "test"),
57 ("Radó", "Iván", "test"),
58 ("Petrovszki", "Gábor", "test"),
59 ("Serfőző", "Tamás", "test"),
60 ("Szebenyi", "Bálint", "test"),
61 ("Zsebényi-Loksa", "Gergely", "test") ]
62
63 def __init__(self, programDirectory, config):
64 """Construct the GUI."""
65 GObject.threads_init()
66
67 self._programDirectory = programDirectory
68 self.config = config
69 self._connecting = False
70 self._reconnecting = False
71 self._connected = False
72 self._logger = logger.Logger(self)
73 self._flight = None
74 self._simulator = None
75 self._fsType = None
76 self._monitoring = False
77
78 self._fleet = None
79
80 self._fleetCallback = None
81
82 self._updatePlaneCallback = None
83 self._updatePlaneTailNumber = None
84 self._updatePlaneStatus = None
85 self._updatePlaneGateNumber = None
86
87 self._stdioLock = threading.Lock()
88 self._stdioText = ""
89 self._stdioStartingLine = True
90
91 self._sendPIREPCallback = None
92 self._sendBugReportCallback = None
93
94 self._credentialsCondition = threading.Condition()
95 self._credentialsAvailable = False
96 self._credentialsUserName = None
97 self._credentialsPassword = None
98
99 self._bookFlightsUserCallback = None
100 self._bookFlightsBusyCallback = None
101
102 self.webHandler = web.Handler(config, self._getCredentialsCallback)
103 self.webHandler.start()
104
105 self.toRestart = False
106
107 def build(self, iconDirectory):
108 """Build the GUI."""
109
110 self._mainWindow = window = Gtk.Window()
111 if os.name!="nt":
112 window.set_visual(window.get_screen().lookup_visual(0x21))
113 window.set_title(WINDOW_TITLE_BASE)
114 window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
115 window.set_resizable(False)
116 window.connect("delete-event", self.deleteMainWindow)
117 window.connect("window-state-event", self._handleMainWindowState)
118 if os.name=="nt":
119 window.connect("leave-notify-event", self._handleLeaveNotify)
120 accelGroup = Gtk.AccelGroup()
121 window.add_accel_group(accelGroup)
122 window.realize()
123
124 mainVBox = Gtk.VBox()
125 window.add(mainVBox)
126
127 self._preferences = Preferences(self)
128 self._timetableWindow = TimetableWindow(self)
129 self._timetableWindow.connect("delete-event", self._hideTimetableWindow)
130 self._flightsWindow = AcceptedFlightsWindow(self)
131 self._flightsWindow.connect("delete-event", self._hideFlightsWindow)
132 self._checklistEditor = ChecklistEditor(self)
133 self._approachCalloutsEditor = ApproachCalloutsEditor(self)
134 self._bugReportDialog = BugReportDialog(self)
135
136 menuBar = self._buildMenuBar(accelGroup)
137 mainVBox.pack_start(menuBar, False, False, 0)
138
139 self._notebook = Gtk.Notebook()
140 mainVBox.pack_start(self._notebook, True, True, 4)
141
142 self._wizard = Wizard(self)
143 label = Gtk.Label(xstr("tab_flight"))
144 label.set_use_underline(True)
145 label.set_tooltip_text(xstr("tab_flight_tooltip"))
146 self._notebook.append_page(self._wizard, label)
147
148 self._flightInfo = FlightInfo(self)
149 label = Gtk.Label(xstr("tab_flight_info"))
150 label.set_use_underline(True)
151 label.set_tooltip_text(xstr("tab_flight_info_tooltip"))
152 self._notebook.append_page(self._flightInfo, label)
153 self._flightInfo.disable()
154
155 self._weightHelp = WeightHelp(self)
156 label = Gtk.Label(xstr("tab_weight_help"))
157 label.set_use_underline(True)
158 label.set_tooltip_text(xstr("tab_weight_help_tooltip"))
159 self._notebook.append_page(self._weightHelp, label)
160
161 (logWidget, self._logView) = self._buildLogWidget()
162 addFaultTag(self._logView.get_buffer())
163 label = Gtk.Label(xstr("tab_log"))
164 label.set_use_underline(True)
165 label.set_tooltip_text(xstr("tab_log_tooltip"))
166 self._notebook.append_page(logWidget, label)
167
168 self._fleetGateStatus = FleetGateStatus(self)
169 label = Gtk.Label(xstr("tab_gates"))
170 label.set_use_underline(True)
171 label.set_tooltip_text(xstr("tab_gates_tooltip"))
172 self._notebook.append_page(self._fleetGateStatus, label)
173
174 self._acars = ACARS(self)
175 label = Gtk.Label("ACARS")
176 label.set_use_underline(True)
177 self._notebook.append_page(self._acars, label)
178
179 (self._debugLogWidget, self._debugLogView) = self._buildLogWidget()
180 self._debugLogWidget.show_all()
181
182 mainVBox.pack_start(Gtk.HSeparator(), False, False, 0)
183
184 self._statusbar = Statusbar(iconDirectory)
185 mainVBox.pack_start(self._statusbar, False, False, 0)
186
187 self._notebook.connect("switch-page", self._notebookPageSwitch)
188
189 self._monitorWindow = MonitorWindow(self, iconDirectory)
190 self._monitorWindow.add_accel_group(accelGroup)
191 self._monitorWindowX = None
192 self._monitorWindowY = None
193 self._selfToggling = False
194
195 self._pirepViewer = PIREPViewer(self)
196 self._messagedPIREPViewer = PIREPViewer(self, showMessages = True)
197
198 self._pirepEditor = PIREPEditor(self)
199
200 window.show_all()
201
202 self._wizard.grabDefault()
203 self._weightHelp.reset()
204 self._weightHelp.disable()
205
206 self._statusIcon = StatusIcon(iconDirectory, self)
207
208 self._busyCursor = Gdk.Cursor(Gdk.CursorType.WATCH)
209
210 self._loadPIREPDialog = None
211 self._lastLoadedPIREP = None
212
213 self._hotkeySetID = None
214 self._pilotHotkeyIndex = None
215 self._checklistHotkeyIndex = None
216
217 self._aboutDialog = None
218
219 @property
220 def mainWindow(self):
221 """Get the main window of the GUI."""
222 return self._mainWindow
223
224 @property
225 def logger(self):
226 """Get the logger used by us."""
227 return self._logger
228
229 @property
230 def simulator(self):
231 """Get the simulator used by us."""
232 return self._simulator
233
234 @property
235 def flight(self):
236 """Get the flight being performed."""
237 return self._flight
238
239 @property
240 def fsType(self):
241 """Get the flight simulator type."""
242 return self._fsType
243
244 @property
245 def entranceExam(self):
246 """Get whether an entrance exam is about to be taken."""
247 return self._wizard.entranceExam
248
249 @property
250 def loggedIn(self):
251 """Indicate if the user has logged in properly."""
252 return self._wizard.loggedIn
253
254 @property
255 def loginResult(self):
256 """Get the result of the login."""
257 return self._wizard.loginResult
258
259 @property
260 def bookedFlight(self):
261 """Get the booked flight selected, if any."""
262 return self._wizard.bookedFlight
263
264 @property
265 def numCockpitCrew(self):
266 """Get the number of cockpit crew members."""
267 return self._wizard.numCockpitCrew
268
269 @property
270 def numCabinCrew(self):
271 """Get the number of cabin crew members."""
272 return self._wizard.numCabinCrew
273
274 @property
275 def numPassengers(self):
276 """Get the number of passengers."""
277 return self._wizard.numPassengers
278
279 @property
280 def numChildren(self):
281 """Get the number of child passengers."""
282 return self._wizard.numChildren
283
284 @property
285 def numInfants(self):
286 """Get the number of infant passengers."""
287 return self._wizard.numInfants
288
289 @property
290 def bagWeight(self):
291 """Get the bag weight."""
292 return self._wizard.bagWeight
293
294 @property
295 def cargoWeight(self):
296 """Get the cargo weight."""
297 return self._wizard.cargoWeight
298
299 @property
300 def mailWeight(self):
301 """Get the mail weight."""
302 return self._wizard.mailWeight
303
304 @property
305 def zfw(self):
306 """Get Zero-Fuel Weight calculated for the current flight."""
307 return self._wizard.zfw
308
309 @property
310 def filedCruiseAltitude(self):
311 """Get cruise altitude filed for the current flight."""
312 return self._wizard.filedCruiseAltitude
313
314 @property
315 def cruiseAltitude(self):
316 """Get cruise altitude set for the current flight."""
317 return self._wizard.cruiseAltitude
318
319 @property
320 def loggableCruiseAltitude(self):
321 """Get the cruise altitude that can be logged."""
322 return self._wizard.loggableCruiseAltitude
323
324 @property
325 def route(self):
326 """Get the flight route."""
327 return self._wizard.route
328
329 @property
330 def departureMETAR(self):
331 """Get the METAR of the deprature airport."""
332 return self._wizard.departureMETAR
333
334 @property
335 def arrivalMETAR(self):
336 """Get the METAR of the deprature airport."""
337 return self._wizard.arrivalMETAR
338
339 @property
340 def departureRunway(self):
341 """Get the name of the departure runway."""
342 return self._wizard.departureRunway
343
344 @property
345 def sid(self):
346 """Get the SID."""
347 return self._wizard.sid
348
349 @property
350 def v1(self):
351 """Get the V1 speed calculated for the flight."""
352 return self._wizard.v1
353
354 @property
355 def vr(self):
356 """Get the Vr speed calculated for the flight."""
357 return self._wizard.vr
358
359 @property
360 def v2(self):
361 """Get the V2 speed calculated for the flight."""
362 return self._wizard.v2
363
364 @property
365 def derate(self):
366 """Get the derate value calculated for the flight."""
367 return self._wizard.derate
368
369 @property
370 def takeoffAntiIceOn(self):
371 """Get whether the anti-ice system was on during take-off."""
372 return self._wizard.takeoffAntiIceOn
373
374 @takeoffAntiIceOn.setter
375 def takeoffAntiIceOn(self, value):
376 """Set the anti-ice on indicator."""
377 GObject.idle_add(self._setTakeoffAntiIceOn, value)
378
379 @property
380 def rtoIndicated(self):
381 """Get whether the pilot has indicated than an RTO has occured."""
382 return self._wizard.rtoIndicated
383
384 @property
385 def arrivalRunway(self):
386 """Get the arrival runway."""
387 return self._wizard.arrivalRunway
388
389 @property
390 def star(self):
391 """Get the STAR."""
392 return self._wizard.star
393
394 @property
395 def transition(self):
396 """Get the transition."""
397 return self._wizard.transition
398
399 @property
400 def approachType(self):
401 """Get the approach type."""
402 return self._wizard.approachType
403
404 @property
405 def vref(self):
406 """Get the Vref speed calculated for the flight."""
407 return self._wizard.vref
408
409 @property
410 def landingAntiIceOn(self):
411 """Get whether the anti-ice system was on during landing."""
412 return self._wizard.landingAntiIceOn
413
414 @landingAntiIceOn.setter
415 def landingAntiIceOn(self, value):
416 """Set the anti-ice on indicator."""
417 GObject.idle_add(self._setLandingAntiIceOn, value)
418
419 @property
420 def flightType(self):
421 """Get the flight type."""
422 return self._wizard.flightType
423
424 @property
425 def online(self):
426 """Get whether the flight was online or not."""
427 return self._wizard.online
428
429 @property
430 def comments(self):
431 """Get the comments."""
432 return self._flightInfo.comments
433
434 @property
435 def hasComments(self):
436 """Indicate whether there is a comment."""
437 return self._flightInfo.hasComments
438
439 @property
440 def flightDefects(self):
441 """Get the flight defects."""
442 return self._flightInfo.faultsAndExplanations
443
444 @property
445 def delayCodes(self):
446 """Get the delay codes."""
447 return self._flightInfo.delayCodes
448
449 @property
450 def hasDelayCode(self):
451 """Determine if there is at least one delay code selected."""
452 return self._flightInfo.hasDelayCode
453
454 @property
455 def faultsFullyExplained(self):
456 """Determine if all the faults have been fully explained by the
457 user."""
458 return self._flightInfo.faultsFullyExplained
459
460 @property
461 def backgroundColour(self):
462 """Get the background colour of the main window."""
463 return self._mainWindow.get_style_context().\
464 get_background_color(Gtk.StateFlags.NORMAL)
465
466 def run(self):
467 """Run the GUI."""
468 if self.config.autoUpdate:
469 self._updater = Updater(self,
470 self._programDirectory,
471 self.config.updateURL,
472 self._mainWindow)
473 self._updater.start()
474 else:
475 self.updateDone()
476
477 singleton.raiseCallback = self.raiseCallback
478 Gtk.main()
479 singleton.raiseCallback = None
480
481 cef.finalize()
482
483 self._disconnect()
484
485 def updateDone(self):
486 """Called when the update is done (and there is no need to restart)."""
487 GObject.idle_add(self._updateDone)
488
489 def connected(self, fsType, descriptor):
490 """Called when we have connected to the simulator."""
491 self._connected = True
492 self._logger.untimedMessage("MLX %s connected to the simulator %s" % \
493 (const.VERSION, descriptor))
494 fs.sendMessage(const.MESSAGETYPE_INFORMATION,
495 "Welcome to MAVA Logger X " + const.VERSION)
496 GObject.idle_add(self._handleConnected, fsType, descriptor)
497
498 def _handleConnected(self, fsType, descriptor):
499 """Called when the connection to the simulator has succeeded."""
500 self._statusbar.updateConnection(self._connecting, self._connected)
501 self.endBusy()
502 if not self._reconnecting:
503 self._wizard.connected(fsType, descriptor)
504 self._reconnecting = False
505 self._fsType = fsType
506 self._listenHotkeys()
507
508 def connectionFailed(self):
509 """Called when the connection failed."""
510 self._logger.untimedMessage("Connection to the simulator failed")
511 GObject.idle_add(self._connectionFailed)
512
513 def _connectionFailed(self):
514 """Called when the connection failed."""
515 self.endBusy()
516 self._statusbar.updateConnection(self._connecting, self._connected)
517
518 dialog = Gtk.MessageDialog(parent = self._mainWindow,
519 type = Gtk.MessageType.ERROR,
520 message_format = xstr("conn_failed"))
521
522 dialog.set_title(WINDOW_TITLE_BASE)
523 dialog.format_secondary_markup(xstr("conn_failed_sec"))
524
525 dialog.add_button(xstr("button_cancel"), 0)
526 dialog.add_button(xstr("button_tryagain"), 1)
527 dialog.set_default_response(1)
528
529 result = dialog.run()
530 dialog.hide()
531 if result == 1:
532 self.beginBusy(xstr("connect_busy"))
533 self._simulator.reconnect()
534 else:
535 self.reset()
536
537 def disconnected(self):
538 """Called when we have disconnected from the simulator."""
539 self._connected = False
540 self._logger.untimedMessage("Disconnected from the simulator")
541 if self._flight is not None:
542 self._flight.disconnected()
543
544 GObject.idle_add(self._disconnected)
545
546 def _disconnected(self):
547 """Called when we have disconnected from the simulator unexpectedly."""
548 self._statusbar.updateConnection(self._connecting, self._connected)
549
550 dialog = Gtk.MessageDialog(type = Gtk.MessageType.ERROR,
551 message_format = xstr("conn_broken"),
552 parent = self._mainWindow)
553 dialog.set_title(WINDOW_TITLE_BASE)
554 dialog.format_secondary_markup(xstr("conn_broken_sec"))
555
556 dialog.add_button(xstr("button_cancel"), 0)
557 dialog.add_button(xstr("button_reconnect"), 1)
558 dialog.set_default_response(1)
559
560 result = dialog.run()
561 dialog.hide()
562 if result == 1:
563 self.beginBusy(xstr("connect_busy"))
564 self._reconnecting = True
565 self._simulator.reconnect()
566 else:
567 self.reset()
568
569 def enableFlightInfo(self, aircraftType):
570 """Enable the flight info tab."""
571 self._flightInfo.enable(aircraftType)
572
573 def bookFlights(self, callback, flightIDs, date, tailNumber,
574 busyCallback = None):
575 """Initiate the booking of flights with the given timetable IDs and
576 other data"""
577 self._bookFlightsUserCallback = callback
578 self._bookFlightsBusyCallback = busyCallback
579
580 self.beginBusy(xstr("bookflights_busy"))
581 if busyCallback is not None:
582 busyCallback(True)
583
584 self.webHandler.bookFlights(self._bookFlightsCallback,
585 flightIDs, date, tailNumber)
586
587 def _bookFlightsCallback(self, returned, result):
588 """Called when the booking of flights has finished."""
589 GObject.idle_add(self._handleBookFlightsResult, returned, result)
590
591 def _handleBookFlightsResult(self, returned, result):
592 """Called when the booking of flights is done.
593
594 If it was successful, the booked flights are added to the list of the
595 flight selector."""
596 if self._bookFlightsBusyCallback is not None:
597 self._bookFlightsBusyCallback(False)
598 self.endBusy()
599
600 if returned:
601 for bookedFlight in result.bookedFlights:
602 self._wizard.addFlight(bookedFlight)
603
604 self._bookFlightsUserCallback(returned, result)
605
606 def cancelFlight(self):
607 """Cancel the current file, if the user confirms it."""
608 dialog = Gtk.MessageDialog(parent = self._mainWindow,
609 type = Gtk.MessageType.QUESTION,
610 message_format = xstr("cancelFlight_question"))
611
612 dialog.add_button(xstr("button_no"), Gtk.ResponseType.NO)
613 dialog.add_button(xstr("button_yes"), Gtk.ResponseType.YES)
614
615 dialog.set_title(WINDOW_TITLE_BASE)
616 result = dialog.run()
617 dialog.hide()
618
619 if result==Gtk.ResponseType.YES:
620 self.reset()
621
622 def reset(self):
623 """Reset the GUI."""
624 self._disconnect()
625
626 self._simulator = None
627
628 self._flightInfo.reset()
629 self._flightInfo.disable()
630 self.resetFlightStatus()
631
632 self._weightHelp.reset()
633 self._weightHelp.disable()
634 self._notebook.set_current_page(0)
635
636 self._logView.get_buffer().set_text("")
637
638 if self.loggedIn:
639 self._wizard.cancelFlight(self._handleReloadResult)
640 else:
641 self._wizard.reset(None)
642
643 def _handleReloadResult(self, returned, result):
644 """Handle the result of the reloading of the flights."""
645 self._wizard.reset(result if returned and result.loggedIn else None)
646
647 def _disconnect(self, closingMessage = None, duration = 3):
648 """Disconnect from the simulator if connected."""
649 self.stopMonitoring()
650 self._clearHotkeys()
651
652 if self._connected:
653 if closingMessage is None:
654 self._flight.simulator.disconnect()
655 else:
656 fs.sendMessage(const.MESSAGETYPE_ENVIRONMENT,
657 closingMessage, duration,
658 disconnect = True)
659 self._connected = False
660
661 self._connecting = False
662 self._reconnecting = False
663 self._statusbar.updateConnection(False, False)
664 self._weightHelp.disable()
665
666 return True
667
668 def insertFlightLogLine(self, index, timestampString, text, isFault):
669 """Insert the flight log line with the given data."""
670 GObject.idle_add(self._insertFlightLogLine, index,
671 formatFlightLogLine(timestampString, text),
672 isFault)
673
674 def _insertFlightLogLine(self, index, line, isFault):
675 """Perform the real insertion.
676
677 To be called from the event loop."""
678 buffer = self._logView.get_buffer()
679 lineIter = buffer.get_iter_at_line(index)
680 insertTextBuffer(buffer, lineIter, line, isFault = isFault)
681 self._logView.scroll_mark_onscreen(buffer.get_insert())
682
683 def removeFlightLogLine(self, index):
684 """Remove the flight log line with the given index."""
685 GObject.idle_add(self._removeFlightLogLine, index)
686
687 def addFault(self, id, timestampString, text):
688 """Add a fault to the list of faults."""
689 faultText = formatFlightLogLine(timestampString, text).strip()
690 GObject.idle_add(self._flightInfo.addFault, id, faultText)
691
692 def updateFault(self, id, timestampString, text):
693 """Update a fault in the list of faults."""
694 faultText = formatFlightLogLine(timestampString, text).strip()
695 GObject.idle_add(self._flightInfo.updateFault, id, faultText)
696
697 def clearFault(self, id):
698 """Clear a fault in the list of faults."""
699 GObject.idle_add(self._flightInfo.clearFault, id)
700
701 def _removeFlightLogLine(self, index):
702 """Perform the real removal."""
703 buffer = self._logView.get_buffer()
704 startIter = buffer.get_iter_at_line(index)
705 endIter = buffer.get_iter_at_line(index+1)
706 buffer.delete(startIter, endIter)
707 self._logView.scroll_mark_onscreen(buffer.get_insert())
708
709 def check(self, flight, aircraft, logger, oldState, state):
710 """Update the data."""
711 GObject.idle_add(self._monitorWindow.setData, state)
712 GObject.idle_add(self._statusbar.updateTime, state.timestamp)
713
714 def resetFlightStatus(self):
715 """Reset the status of the flight."""
716 self._statusbar.resetFlightStatus()
717 self._statusbar.updateTime()
718 self._statusIcon.resetFlightStatus()
719
720 def setStage(self, stage):
721 """Set the stage of the flight."""
722 GObject.idle_add(self._setStage, stage)
723
724 def _setStage(self, stage):
725 """Set the stage of the flight."""
726 self._statusbar.setStage(stage)
727 self._statusIcon.setStage(stage)
728 self._wizard.setStage(stage)
729 if stage==const.STAGE_END:
730 welcomeMessage = \
731 airports.getWelcomeMessage(self.bookedFlight.arrivalICAO)
732 self._disconnect(closingMessage =
733 "Flight plan closed. " + welcomeMessage,
734 duration = 5)
735
736 def setRating(self, rating):
737 """Set the rating of the flight."""
738 GObject.idle_add(self._setRating, rating)
739
740 def _setRating(self, rating):
741 """Set the rating of the flight."""
742 self._statusbar.setRating(rating)
743 self._statusIcon.setRating(rating)
744
745 def setNoGo(self, reason):
746 """Set the rating of the flight to No-Go with the given reason."""
747 GObject.idle_add(self._setNoGo, reason)
748
749 def _setNoGo(self, reason):
750 """Set the rating of the flight."""
751 self._statusbar.setNoGo(reason)
752 self._statusIcon.setNoGo(reason)
753
754 def _handleMainWindowState(self, window, event):
755 """Hande a change in the state of the window"""
756 iconified = Gdk.WindowState.ICONIFIED
757
758 if (event.changed_mask&Gdk.WindowState.WITHDRAWN)!=0:
759 if (event.new_window_state&Gdk.WindowState.WITHDRAWN)!=0:
760 self._statusIcon.mainWindowHidden()
761 else:
762 self._statusIcon.mainWindowShown()
763
764 if (event.changed_mask&Gdk.WindowState.ICONIFIED)!=0 and \
765 (event.new_window_state&Gdk.WindowState.ICONIFIED)==0:
766 self._mainWindow.present()
767
768 def _handleLeaveNotify(self, widget, event):
769 """Handle the leave-notify event.
770
771 Here we reset the focus to the main window as CEF might have acquired
772 it earlier."""
773 self._mainWindow.get_window().focus(0)
774
775 def raiseCallback(self):
776 """Callback for the singleton handling code."""
777 GObject.idle_add(self.raiseMainWindow)
778
779 def raiseMainWindow(self):
780 """Show the main window if invisible, and raise it."""
781 if not self._mainWindow.get_visible():
782 self.showMainWindow()
783 self._mainWindow.present()
784
785 def deleteMainWindow(self, window, event):
786 """Handle the delete event for the main window."""
787 if self.config.quitOnClose:
788 self._quit()
789 else:
790 self.hideMainWindow()
791 return True
792
793 def hideMainWindow(self, savePosition = True):
794 """Hide the main window and save its position."""
795 if savePosition:
796 (self._mainWindowX, self._mainWindowY) = \
797 self._mainWindow.get_window().get_root_origin()
798 else:
799 self._mainWindowX = self._mainWindowY = None
800 self._mainWindow.hide()
801 return True
802
803 def showMainWindow(self):
804 """Show the main window at its former position."""
805 if self._mainWindowX is not None and self._mainWindowY is not None:
806 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
807
808 self._mainWindow.show()
809 self._mainWindow.deiconify()
810
811 def toggleMainWindow(self):
812 """Toggle the main window."""
813 if self._mainWindow.get_visible():
814 self.hideMainWindow()
815 else:
816 self.showMainWindow()
817
818 def hideMonitorWindow(self, savePosition = True):
819 """Hide the monitor window."""
820 if savePosition:
821 (self._monitorWindowX, self._monitorWindowY) = \
822 self._monitorWindow.get_window().get_root_origin()
823 else:
824 self._monitorWindowX = self._monitorWindowY = None
825 self._monitorWindow.hide()
826 self._statusIcon.monitorWindowHidden()
827 if self._showMonitorMenuItem.get_active():
828 self._selfToggling = True
829 self._showMonitorMenuItem.set_active(False)
830 return True
831
832 def showMonitorWindow(self):
833 """Show the monitor window."""
834 if self._monitorWindowX is not None and self._monitorWindowY is not None:
835 self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
836 self._monitorWindow.show_all()
837 self._statusIcon.monitorWindowShown()
838 if not self._showMonitorMenuItem.get_active():
839 self._selfToggling = True
840 self._showMonitorMenuItem.set_active(True)
841
842 def _toggleMonitorWindow(self, menuItem):
843 if self._selfToggling:
844 self._selfToggling = False
845 elif self._monitorWindow.get_visible():
846 self.hideMonitorWindow()
847 else:
848 self.showMonitorWindow()
849
850 def restart(self):
851 """Quit and restart the application."""
852 self.toRestart = True
853 self._quit(force = True)
854
855 def flushStdIO(self):
856 """Flush any text to the standard error that could not be logged."""
857 if self._stdioText:
858 sys.__stderr__.write(self._stdioText)
859
860 def writeStdIO(self, text):
861 """Write the given text into standard I/O log."""
862 with self._stdioLock:
863 self._stdioText += text
864
865 GObject.idle_add(self._writeStdIO)
866
867 def beginBusy(self, message):
868 """Begin a period of background processing."""
869 self._wizard.set_sensitive(False)
870 self._weightHelp.set_sensitive(False)
871 self._mainWindow.get_window().set_cursor(self._busyCursor)
872 self._statusbar.updateBusyState(message)
873
874 def updateBusyState(self, message):
875 """Update the busy state."""
876 self._statusbar.updateBusyState(message)
877
878 def endBusy(self):
879 """End a period of background processing."""
880 self._mainWindow.get_window().set_cursor(None)
881 self._weightHelp.set_sensitive(True)
882 self._wizard.set_sensitive(True)
883 self._statusbar.updateBusyState(None)
884
885 def initializeWeightHelp(self):
886 """Initialize the weight help tab."""
887 self._weightHelp.reset()
888 self._weightHelp.enable()
889
890 def getFleetAsync(self, callback = None, force = None):
891 """Get the fleet asynchronously."""
892 GObject.idle_add(self.getFleet, callback, force)
893
894 def getFleet(self, callback = None, force = False, busyCallback = None):
895 """Get the fleet.
896
897 If force is False, and we already have a fleet retrieved,
898 that one will be used."""
899 if self._fleet is None or force:
900 self._fleetCallback = callback
901 self._fleetBusyCallback = busyCallback
902 if busyCallback is not None:
903 busyCallback(True)
904 self.beginBusy(xstr("fleet_busy"))
905 self.webHandler.getFleet(self._fleetResultCallback)
906 else:
907 callback(self._fleet)
908
909 def commentsChanged(self):
910 """Indicate that the comments have changed."""
911 self._wizard.commentsChanged()
912
913 def delayCodesChanged(self):
914 """Called when the delay codes have changed."""
915 self._wizard.delayCodesChanged()
916
917 def faultExplanationsChanged(self):
918 """Called when the status of the explanations of the faults have
919 changed."""
920 self._wizard.faultExplanationsChanged()
921
922 def updateRTO(self, inLoop = False):
923 """Indicate that the RTO state should be updated."""
924 if inLoop:
925 self._wizard.updateRTO()
926 else:
927 GObject.idle_add(self.updateRTO, True)
928
929 def rtoToggled(self, indicated):
930 """Called when the user has toggled the RTO checkbox."""
931 self._flight.rtoToggled(indicated)
932
933 def _fleetResultCallback(self, returned, result):
934 """Called when the fleet has been queried."""
935 GObject.idle_add(self._handleFleetResult, returned, result)
936
937 def _handleFleetResult(self, returned, result):
938 """Handle the fleet result."""
939 self.endBusy()
940 if self._fleetBusyCallback is not None:
941 self._fleetBusyCallback(False)
942 if returned:
943 self._fleet = result.fleet
944 else:
945 self._fleet = None
946
947 dialog = Gtk.MessageDialog(parent = self.mainWindow,
948 type = Gtk.MessageType.ERROR,
949 message_format = xstr("fleet_failed"))
950 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
951 dialog.set_title(WINDOW_TITLE_BASE)
952 dialog.run()
953 dialog.hide()
954
955 callback = self._fleetCallback
956 self._fleetCallback = None
957 self._fleetBusyCallback = None
958 if callback is not None:
959 callback(self._fleet)
960 self._fleetGateStatus.handleFleet(self._fleet)
961
962 def updatePlane(self, tailNumber, status,
963 gateNumber = None, callback = None):
964 """Update the status of the given plane."""
965 self.beginBusy(xstr("fleet_update_busy"))
966
967 self._updatePlaneCallback = callback
968
969 self._updatePlaneTailNumber = tailNumber
970 self._updatePlaneStatus = status
971 self._updatePlaneGateNumber = gateNumber
972
973 self.webHandler.updatePlane(self._updatePlaneResultCallback,
974 tailNumber, status, gateNumber)
975
976 def _updatePlaneResultCallback(self, returned, result):
977 """Called when the status of a plane has been updated."""
978 GObject.idle_add(self._handleUpdatePlaneResult, returned, result)
979
980 def _handleUpdatePlaneResult(self, returned, result):
981 """Handle the plane update result."""
982 self.endBusy()
983 if returned:
984 success = result.success
985 if success:
986 if self._fleet is not None:
987 self._fleet.updatePlane(self._updatePlaneTailNumber,
988 self._updatePlaneStatus,
989 self._updatePlaneGateNumber)
990 self._fleetGateStatus.handleFleet(self._fleet)
991 else:
992 dialog = Gtk.MessageDialog(parent = self.mainWindow,
993 type = Gtk.MessageType.ERROR,
994 message_format = xstr("fleet_update_failed"))
995 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.ACCEPT)
996 dialog.set_title(WINDOW_TITLE_BASE)
997 dialog.run()
998 dialog.hide()
999
1000 success = None
1001
1002 callback = self._updatePlaneCallback
1003 self._updatePlaneCallback = None
1004 if callback is not None:
1005 callback(success)
1006
1007 def _writeStdIO(self):
1008 """Perform the real writing."""
1009 with self._stdioLock:
1010 text = self._stdioText
1011 self._stdioText = ""
1012 if not text: return
1013
1014 lines = text.splitlines()
1015 if text[-1]=="\n":
1016 text = ""
1017 else:
1018 text = lines[-1]
1019 lines = lines[:-1]
1020
1021 now = datetime.datetime.now()
1022 timeStr = "%02d:%02d:%02d: " % (now.hour, now.minute, now.second)
1023
1024 for line in lines:
1025 #print >> sys.__stdout__, line
1026 if self._stdioStartingLine:
1027 self._writeLog(timeStr, self._debugLogView)
1028 self._writeLog(line + "\n", self._debugLogView)
1029 self._stdioStartingLine = True
1030
1031 if text:
1032 #print >> sys.__stdout__, text,
1033 if self._stdioStartingLine:
1034 self._writeLog(timeStr, self._debugLogView)
1035 self._writeLog(text, self._debugLogView)
1036 self._stdioStartingLine = False
1037
1038 def connectSimulator(self, bookedFlight, simulatorType):
1039 """Connect to the simulator for the first time."""
1040 self._logger.reset()
1041
1042 self._flight = flight.Flight(self._logger, self)
1043 self._flight.flareTimeFromFS = self.config.flareTimeFromFS
1044 self._flight.aircraftType = bookedFlight.aircraftType
1045 self._flight.aircraft = acft.Aircraft.create(self._flight, bookedFlight)
1046 self._flight.aircraft._checkers.append(self)
1047
1048 if self._simulator is None:
1049 self._simulator = fs.createSimulator(simulatorType, self)
1050 fs.setupMessageSending(self.config, self._simulator)
1051 self._setupTimeSync()
1052
1053 self._flight.simulator = self._simulator
1054
1055 self.beginBusy(xstr("connect_busy"))
1056 self._statusbar.updateConnection(self._connecting, self._connected)
1057
1058 self._connecting = True
1059 self._simulator.connect(self._flight.aircraft)
1060
1061 def startMonitoring(self):
1062 """Start monitoring."""
1063 if not self._monitoring:
1064 self.simulator.startMonitoring()
1065 self._monitoring = True
1066
1067 def stopMonitoring(self):
1068 """Stop monitoring."""
1069 if self._monitoring:
1070 self.simulator.stopMonitoring()
1071 self._monitoring = False
1072
1073 def cruiseLevelChanged(self):
1074 """Called when the cruise level is changed in the flight wizard."""
1075 if self._flight is not None:
1076 return self._flight.cruiseLevelChanged()
1077 else:
1078 return False
1079
1080 def _buildMenuBar(self, accelGroup):
1081 """Build the main menu bar."""
1082 menuBar = Gtk.MenuBar()
1083
1084 fileMenuItem = Gtk.MenuItem(xstr("menu_file"))
1085 fileMenu = Gtk.Menu()
1086 fileMenuItem.set_submenu(fileMenu)
1087 menuBar.append(fileMenuItem)
1088
1089 loadPIREPMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_OPEN)
1090 loadPIREPMenuItem.set_use_stock(True)
1091 loadPIREPMenuItem.set_label(xstr("menu_file_loadPIREP"))
1092 loadPIREPMenuItem.add_accelerator("activate", accelGroup,
1093 ord(xstr("menu_file_loadPIREP_key")),
1094 Gdk.ModifierType.CONTROL_MASK,
1095 Gtk.AccelFlags.VISIBLE)
1096 loadPIREPMenuItem.connect("activate", self._loadPIREP)
1097 fileMenu.append(loadPIREPMenuItem)
1098
1099 fileMenu.append(Gtk.SeparatorMenuItem())
1100
1101 quitMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_QUIT)
1102 quitMenuItem.set_use_stock(True)
1103 quitMenuItem.set_label(xstr("menu_file_quit"))
1104 quitMenuItem.add_accelerator("activate", accelGroup,
1105 ord(xstr("menu_file_quit_key")),
1106 Gdk.ModifierType.CONTROL_MASK,
1107 Gtk.AccelFlags.VISIBLE)
1108 quitMenuItem.connect("activate", self._quit)
1109 fileMenu.append(quitMenuItem)
1110
1111 toolsMenuItem = Gtk.MenuItem(xstr("menu_tools"))
1112 toolsMenu = Gtk.Menu()
1113 toolsMenuItem.set_submenu(toolsMenu)
1114 menuBar.append(toolsMenuItem)
1115
1116 self._timetableMenuItem = timetableMenuItem = \
1117 Gtk.ImageMenuItem(Gtk.STOCK_INDENT)
1118 timetableMenuItem.set_use_stock(True)
1119 timetableMenuItem.set_label(xstr("menu_tools_timetable"))
1120 timetableMenuItem.add_accelerator("activate", accelGroup,
1121 ord(xstr("menu_tools_timetable_key")),
1122 Gdk.ModifierType.CONTROL_MASK,
1123 Gtk.AccelFlags.VISIBLE)
1124 timetableMenuItem.connect("activate", self.showTimetable)
1125 self._timetableMenuItem.set_sensitive(False)
1126 toolsMenu.append(timetableMenuItem)
1127
1128 self._flightsMenuItem = flightsMenuItem = \
1129 Gtk.ImageMenuItem(Gtk.STOCK_SPELL_CHECK)
1130 flightsMenuItem.set_use_stock(True)
1131 flightsMenuItem.set_label(xstr("menu_tools_flights"))
1132 flightsMenuItem.add_accelerator("activate", accelGroup,
1133 ord(xstr("menu_tools_flights_key")),
1134 Gdk.ModifierType.CONTROL_MASK,
1135 Gtk.AccelFlags.VISIBLE)
1136 flightsMenuItem.connect("activate", self.showFlights)
1137 self._flightsMenuItem.set_sensitive(False)
1138 toolsMenu.append(flightsMenuItem)
1139
1140 checklistMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_APPLY)
1141 checklistMenuItem.set_use_stock(True)
1142 checklistMenuItem.set_label(xstr("menu_tools_chklst"))
1143 checklistMenuItem.add_accelerator("activate", accelGroup,
1144 ord(xstr("menu_tools_chklst_key")),
1145 Gdk.ModifierType.CONTROL_MASK,
1146 Gtk.AccelFlags.VISIBLE)
1147 checklistMenuItem.connect("activate", self._editChecklist)
1148 toolsMenu.append(checklistMenuItem)
1149
1150 approachCalloutsMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_EDIT)
1151 approachCalloutsMenuItem.set_use_stock(True)
1152 approachCalloutsMenuItem.set_label(xstr("menu_tools_callouts"))
1153 approachCalloutsMenuItem.add_accelerator("activate", accelGroup,
1154 ord(xstr("menu_tools_callouts_key")),
1155 Gdk.ModifierType.CONTROL_MASK,
1156 Gtk.AccelFlags.VISIBLE)
1157 approachCalloutsMenuItem.connect("activate", self._editApproachCallouts)
1158 toolsMenu.append(approachCalloutsMenuItem)
1159
1160 prefsMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_PREFERENCES)
1161 prefsMenuItem.set_use_stock(True)
1162 prefsMenuItem.set_label(xstr("menu_tools_prefs"))
1163 prefsMenuItem.add_accelerator("activate", accelGroup,
1164 ord(xstr("menu_tools_prefs_key")),
1165 Gdk.ModifierType.CONTROL_MASK,
1166 Gtk.AccelFlags.VISIBLE)
1167 prefsMenuItem.connect("activate", self._editPreferences)
1168 toolsMenu.append(prefsMenuItem)
1169
1170 toolsMenu.append(Gtk.SeparatorMenuItem())
1171
1172 bugReportMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_PASTE)
1173 bugReportMenuItem.set_use_stock(True)
1174 bugReportMenuItem.set_label(xstr("menu_tools_bugreport"))
1175 bugReportMenuItem.add_accelerator("activate", accelGroup,
1176 ord(xstr("menu_tools_bugreport_key")),
1177 Gdk.ModifierType.CONTROL_MASK,
1178 Gtk.AccelFlags.VISIBLE)
1179 bugReportMenuItem.connect("activate", self._reportBug)
1180 toolsMenu.append(bugReportMenuItem)
1181
1182 viewMenuItem = Gtk.MenuItem(xstr("menu_view"))
1183 viewMenu = Gtk.Menu()
1184 viewMenuItem.set_submenu(viewMenu)
1185 menuBar.append(viewMenuItem)
1186
1187 self._showMonitorMenuItem = Gtk.CheckMenuItem()
1188 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
1189 self._showMonitorMenuItem.set_use_underline(True)
1190 self._showMonitorMenuItem.set_active(False)
1191 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
1192 ord(xstr("menu_view_monitor_key")),
1193 Gdk.ModifierType.CONTROL_MASK,
1194 Gtk.AccelFlags.VISIBLE)
1195 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
1196 viewMenu.append(self._showMonitorMenuItem)
1197
1198 showDebugMenuItem = Gtk.CheckMenuItem()
1199 showDebugMenuItem.set_label(xstr("menu_view_debug"))
1200 showDebugMenuItem.set_use_underline(True)
1201 showDebugMenuItem.set_active(False)
1202 showDebugMenuItem.add_accelerator("activate", accelGroup,
1203 ord(xstr("menu_view_debug_key")),
1204 Gdk.ModifierType.CONTROL_MASK,
1205 Gtk.AccelFlags.VISIBLE)
1206 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
1207 viewMenu.append(showDebugMenuItem)
1208
1209 helpMenuItem = Gtk.MenuItem(xstr("menu_help"))
1210 helpMenu = Gtk.Menu()
1211 helpMenuItem.set_submenu(helpMenu)
1212 menuBar.append(helpMenuItem)
1213
1214 manualMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_HELP)
1215 manualMenuItem.set_use_stock(True)
1216 manualMenuItem.set_label(xstr("menu_help_manual"))
1217 manualMenuItem.add_accelerator("activate", accelGroup,
1218 ord(xstr("menu_help_manual_key")),
1219 Gdk.ModifierType.CONTROL_MASK,
1220 Gtk.AccelFlags.VISIBLE)
1221 manualMenuItem.connect("activate", self._showManual)
1222 helpMenu.append(manualMenuItem)
1223
1224 helpMenu.append(Gtk.SeparatorMenuItem())
1225
1226 aboutMenuItem = Gtk.ImageMenuItem(Gtk.STOCK_ABOUT)
1227 aboutMenuItem.set_use_stock(True)
1228 aboutMenuItem.set_label(xstr("menu_help_about"))
1229 aboutMenuItem.add_accelerator("activate", accelGroup,
1230 ord(xstr("menu_help_about_key")),
1231 Gdk.ModifierType.CONTROL_MASK,
1232 Gtk.AccelFlags.VISIBLE)
1233 aboutMenuItem.connect("activate", self._showAbout)
1234 helpMenu.append(aboutMenuItem)
1235
1236 return menuBar
1237
1238 def _toggleDebugLog(self, menuItem):
1239 """Toggle the debug log."""
1240 if menuItem.get_active():
1241 label = Gtk.Label(xstr("tab_debug_log"))
1242 label.set_use_underline(True)
1243 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
1244 self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
1245 self._notebook.set_current_page(self._debugLogPage)
1246 else:
1247 self._notebook.remove_page(self._debugLogPage)
1248
1249 def _buildLogWidget(self):
1250 """Build the widget for the log."""
1251 alignment = Gtk.Alignment(xscale = 1.0, yscale = 1.0)
1252
1253 alignment.set_padding(padding_top = 8, padding_bottom = 8,
1254 padding_left = 16, padding_right = 16)
1255
1256 logScroller = Gtk.ScrolledWindow()
1257 # FIXME: these should be constants in common
1258 logScroller.set_policy(Gtk.PolicyType.AUTOMATIC,
1259 Gtk.PolicyType.AUTOMATIC)
1260 logScroller.set_shadow_type(Gtk.ShadowType.IN)
1261 logView = Gtk.TextView()
1262 logView.set_editable(False)
1263 logView.set_cursor_visible(False)
1264 logScroller.add(logView)
1265
1266 logBox = Gtk.VBox()
1267 logBox.pack_start(logScroller, True, True, 0)
1268 logBox.set_size_request(-1, 200)
1269
1270 alignment.add(logBox)
1271
1272 return (alignment, logView)
1273
1274 def _writeLog(self, msg, logView, isFault = False):
1275 """Write the given message to the log."""
1276 buffer = logView.get_buffer()
1277 appendTextBuffer(buffer, msg, isFault = isFault)
1278 logView.scroll_mark_onscreen(buffer.get_insert())
1279
1280 def _quit(self, what = None, force = False):
1281 """Quit from the application."""
1282 if force:
1283 result=Gtk.ResponseType.YES
1284 else:
1285 dialog = Gtk.MessageDialog(parent = self._mainWindow,
1286 type = Gtk.MessageType.QUESTION,
1287 message_format = xstr("quit_question"))
1288
1289 dialog.add_button(xstr("button_no"), Gtk.ResponseType.NO)
1290 dialog.add_button(xstr("button_yes"), Gtk.ResponseType.YES)
1291
1292 dialog.set_title(WINDOW_TITLE_BASE)
1293 result = dialog.run()
1294 dialog.hide()
1295
1296 if result==Gtk.ResponseType.YES:
1297 self._statusIcon.destroy()
1298 return Gtk.main_quit()
1299
1300 def _notebookPageSwitch(self, notebook, page, page_num):
1301 """Called when the current page of the notebook has changed."""
1302 if page_num==0:
1303 GObject.idle_add(self._wizard.grabDefault)
1304 else:
1305 self._mainWindow.set_default(None)
1306
1307 def loginSuccessful(self):
1308 """Called when the login is successful."""
1309 self._flightsMenuItem.set_sensitive(True)
1310 self._timetableMenuItem.set_sensitive(True)
1311
1312 def isWizardActive(self):
1313 """Determine if the flight wizard is active."""
1314 return self._notebook.get_current_page()==0
1315
1316 def showTimetable(self, menuItem = None):
1317 """Callback for showing the timetable."""
1318 if self._timetableWindow.hasFlightPairs:
1319 self._timetableWindow.show_all()
1320 else:
1321 date = datetime.date.today()
1322 self._timetableWindow.setTypes(self.loginResult.types)
1323 self._timetableWindow.setDate(date)
1324 self.updateTimeTable(date)
1325 self.beginBusy(xstr("timetable_query_busy"))
1326
1327 def updateTimeTable(self, date):
1328 """Update the time table for the given date."""
1329 self.beginBusy(xstr("timetable_query_busy"))
1330 self._timetableWindow.set_sensitive(False)
1331 window = self._timetableWindow.get_window()
1332 if window is not None:
1333 window.set_cursor(self._busyCursor)
1334 self.webHandler.getTimetable(self._timetableCallback, date,
1335 self.loginResult.types)
1336
1337 def _timetableCallback(self, returned, result):
1338 """Called when the timetable has been received."""
1339 GObject.idle_add(self._handleTimetable, returned, result)
1340
1341 def _handleTimetable(self, returned, result):
1342 """Handle the result of the query for the timetable."""
1343 self.endBusy()
1344 window = self._timetableWindow.get_window()
1345 if window is not None:
1346 window.set_cursor(None)
1347 self._timetableWindow.set_sensitive(True)
1348 if returned:
1349 self._timetableWindow.setFlightPairs(result.flightPairs)
1350 self._timetableWindow.show_all()
1351 else:
1352 dialog = Gtk.MessageDialog(parent = self.mainWindow,
1353 type = Gtk.MessageType.ERROR,
1354 message_format = xstr("timetable_failed"))
1355 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1356 dialog.set_title(WINDOW_TITLE_BASE)
1357 dialog.run()
1358 dialog.hide()
1359 self._timetableWindow.clear()
1360
1361 def showFlights(self, menuItem):
1362 """Callback for showing the flight list."""
1363 if self._flightsWindow.hasFlights:
1364 self._flightsWindow.show_all()
1365 else:
1366 self.beginBusy(xstr("acceptedflt_query_busy"))
1367 self.webHandler.getAcceptedFlights(self._acceptedFlightsCallback)
1368
1369 def _acceptedFlightsCallback(self, returned, result):
1370 """Called when the accepted flights have been received."""
1371 GObject.idle_add(self._handleAcceptedFlights, returned, result)
1372
1373 def _handleAcceptedFlights(self, returned, result):
1374 """Handle the result of the query for accepted flights."""
1375 self.endBusy()
1376 if returned:
1377 self._flightsWindow.clear()
1378 for flight in result.flights:
1379 self._flightsWindow.addFlight(flight)
1380 self._flightsWindow.show_all()
1381 else:
1382 dialog = Gtk.MessageDialog(parent = self.mainWindow,
1383 type = Gtk.MessageType.ERROR,
1384 message_format = xstr("acceptedflt_failed"))
1385 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1386 dialog.set_title(WINDOW_TITLE_BASE)
1387 dialog.run()
1388 dialog.hide()
1389
1390 def _hideTimetableWindow(self, window, event):
1391 """Hide the window of the timetable."""
1392 self._timetableWindow.hide()
1393 return True
1394
1395 def _hideFlightsWindow(self, window, event):
1396 """Hide the window of the accepted flights."""
1397 self._flightsWindow.hide()
1398 return True
1399
1400 def _editChecklist(self, menuItem):
1401 """Callback for editing the checklists."""
1402 self._checklistEditor.run()
1403
1404 def _editApproachCallouts(self, menuItem):
1405 """Callback for editing the approach callouts."""
1406 self._approachCalloutsEditor.run()
1407
1408 def _editPreferences(self, menuItem):
1409 """Callback for editing the preferences."""
1410 self._clearHotkeys()
1411 self._preferences.run(self.config)
1412 self._setupTimeSync()
1413 self._listenHotkeys()
1414
1415 def _reportBug(self, menuItem):
1416 """Callback for reporting a bug."""
1417 self._bugReportDialog.run()
1418
1419 def _setupTimeSync(self):
1420 """Enable or disable the simulator time synchronization based on the
1421 configuration."""
1422 simulator = self._simulator
1423 if simulator is not None:
1424 if self.config.syncFSTime:
1425 simulator.enableTimeSync()
1426 else:
1427 simulator.disableTimeSync()
1428
1429 def viewPIREP(self, pirep):
1430 """Display the PIREP viewer window with the given PIREP."""
1431 self._pirepViewer.setPIREP(pirep)
1432 self._pirepViewer.show_all()
1433 self._pirepViewer.run()
1434 self._pirepViewer.hide()
1435
1436 def viewMessagedPIREP(self, pirep):
1437 """Display the PIREP viewer window with the given PIREP containing
1438 messages as well."""
1439 self._messagedPIREPViewer.setPIREP(pirep)
1440 self._messagedPIREPViewer.show_all()
1441 self._messagedPIREPViewer.run()
1442 self._messagedPIREPViewer.hide()
1443
1444 def editPIREP(self, pirep):
1445 """Display the PIREP editor window and allow editing the PIREP."""
1446 self._pirepEditor.setPIREP(pirep)
1447 self._pirepEditor.show_all()
1448 if self._pirepEditor.run()==Gtk.ResponseType.OK:
1449 self.beginBusy(xstr("pirepEdit_save_busy"))
1450 self.webHandler.sendPIREP(self._pirepUpdatedCallback, pirep,
1451 update = True)
1452 else:
1453 self._pirepEditor.hide()
1454
1455 def _pirepUpdatedCallback(self, returned, result):
1456 """Callback for the PIREP updating result."""
1457 GObject.idle_add(self._handlePIREPUpdated, returned, result)
1458
1459 def _handlePIREPUpdated(self, returned, result):
1460 """Callback for the PIREP updating result."""
1461 self.endBusy()
1462 secondaryMarkup = None
1463 type = Gtk.MessageType.ERROR
1464 if returned:
1465 if result.success:
1466 type = None
1467 elif result.alreadyFlown:
1468 messageFormat = xstr("sendPIREP_already")
1469 secondaryMarkup = xstr("sendPIREP_already_sec")
1470 elif result.notAvailable:
1471 messageFormat = xstr("sendPIREP_notavail")
1472 else:
1473 messageFormat = xstr("sendPIREP_unknown")
1474 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1475 else:
1476 print("PIREP sending failed", result)
1477 messageFormat = xstr("sendPIREP_failed")
1478 secondaryMarkup = xstr("sendPIREP_failed_sec")
1479
1480 if type is not None:
1481 dialog = Gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1482 type = type, message_format = messageFormat)
1483 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1484 dialog.set_title(WINDOW_TITLE_BASE)
1485 if secondaryMarkup is not None:
1486 dialog.format_secondary_markup(secondaryMarkup)
1487
1488 dialog.run()
1489 dialog.hide()
1490
1491 self._pirepEditor.hide()
1492
1493 def _loadPIREP(self, menuItem):
1494 """Load a PIREP for sending."""
1495 dialog = self._getLoadPirepDialog()
1496
1497 if self._lastLoadedPIREP:
1498 dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP))
1499 else:
1500 pirepDirectory = self.config.pirepDirectory
1501 if pirepDirectory is not None:
1502 dialog.set_current_folder(pirepDirectory)
1503
1504 result = dialog.run()
1505 dialog.hide()
1506
1507 if result==Gtk.ResponseType.OK:
1508 self._lastLoadedPIREP = dialog.get_filename()
1509
1510 pirep = PIREP.load(self._lastLoadedPIREP)
1511 if pirep is None:
1512 dialog = Gtk.MessageDialog(parent = self._mainWindow,
1513 type = Gtk.MessageType.ERROR,
1514 message_format = xstr("loadPIREP_failed"))
1515 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1516 dialog.set_title(WINDOW_TITLE_BASE)
1517 dialog.format_secondary_markup(xstr("loadPIREP_failed_sec"))
1518 dialog.run()
1519 dialog.hide()
1520 else:
1521 dialog = self._getSendLoadedDialog(pirep)
1522 dialog.show_all()
1523 while True:
1524 result = dialog.run()
1525
1526 if result==Gtk.ResponseType.OK:
1527 self.sendPIREP(pirep)
1528 elif result==1:
1529 self.viewPIREP(pirep)
1530 else:
1531 break
1532
1533 dialog.hide()
1534
1535 def _getLoadPirepDialog(self):
1536 """Get the PIREP loading file chooser dialog.
1537
1538 If it is not created yet, it will be created."""
1539 if self._loadPIREPDialog is None:
1540 dialog = Gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
1541 xstr("loadPIREP_browser_title"),
1542 action = Gtk.FileChooserAction.OPEN,
1543 buttons = (Gtk.STOCK_CANCEL,
1544 Gtk.ResponseType.CANCEL,
1545 Gtk.STOCK_OK, Gtk.ResponseType.OK),
1546 parent = self._mainWindow)
1547 dialog.set_modal(True)
1548
1549
1550 filter = Gtk.FileFilter()
1551 filter.set_name(xstr("file_filter_pireps"))
1552 filter.add_pattern("*.pirep")
1553 dialog.add_filter(filter)
1554
1555 filter = Gtk.FileFilter()
1556 filter.set_name(xstr("file_filter_all"))
1557 filter.add_pattern("*.*")
1558 dialog.add_filter(filter)
1559
1560 self._loadPIREPDialog = dialog
1561
1562 return self._loadPIREPDialog
1563
1564 def _getSendLoadedDialog(self, pirep):
1565 """Get a dialog displaying the main information of the flight from the
1566 PIREP and providing Cancel and Send buttons."""
1567 dialog = Gtk.Dialog(title = WINDOW_TITLE_BASE + " - " +
1568 xstr("loadPIREP_send_title"),
1569 parent = self._mainWindow,
1570 flags = Gtk.DialogFlags.MODAL)
1571
1572 contentArea = dialog.get_content_area()
1573
1574 label = Gtk.Label(xstr("loadPIREP_send_help"))
1575 alignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
1576 xscale = 0.0, yscale = 0.0)
1577 alignment.set_padding(padding_top = 16, padding_bottom = 0,
1578 padding_left = 48, padding_right = 48)
1579 alignment.add(label)
1580 contentArea.pack_start(alignment, False, False, 8)
1581
1582 table = Gtk.Table(5, 2)
1583 tableAlignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
1584 xscale = 0.0, yscale = 0.0)
1585 tableAlignment.set_padding(padding_top = 0, padding_bottom = 32,
1586 padding_left = 48, padding_right = 48)
1587 table.set_row_spacings(4)
1588 table.set_col_spacings(16)
1589 tableAlignment.add(table)
1590 contentArea.pack_start(tableAlignment, True, True, 8)
1591
1592 bookedFlight = pirep.bookedFlight
1593
1594 label = Gtk.Label("<b>" + xstr("loadPIREP_send_flightno") + "</b>")
1595 label.set_use_markup(True)
1596 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1597 xscale = 0.0, yscale = 0.0)
1598 labelAlignment.add(label)
1599 table.attach(labelAlignment, 0, 1, 0, 1)
1600
1601 label = Gtk.Label(bookedFlight.callsign)
1602 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1603 xscale = 0.0, yscale = 0.0)
1604 labelAlignment.add(label)
1605 table.attach(labelAlignment, 1, 2, 0, 1)
1606
1607 label = Gtk.Label("<b>" + xstr("loadPIREP_send_date") + "</b>")
1608 label.set_use_markup(True)
1609 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1610 xscale = 0.0, yscale = 0.0)
1611 labelAlignment.add(label)
1612 table.attach(labelAlignment, 0, 1, 1, 2)
1613
1614 label = Gtk.Label(str(bookedFlight.departureTime.date()))
1615 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1616 xscale = 0.0, yscale = 0.0)
1617 labelAlignment.add(label)
1618 table.attach(labelAlignment, 1, 2, 1, 2)
1619
1620 label = Gtk.Label("<b>" + xstr("loadPIREP_send_from") + "</b>")
1621 label.set_use_markup(True)
1622 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1623 xscale = 0.0, yscale = 0.0)
1624 labelAlignment.add(label)
1625 table.attach(labelAlignment, 0, 1, 2, 3)
1626
1627 label = Gtk.Label(bookedFlight.departureICAO)
1628 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1629 xscale = 0.0, yscale = 0.0)
1630 labelAlignment.add(label)
1631 table.attach(labelAlignment, 1, 2, 2, 3)
1632
1633 label = Gtk.Label("<b>" + xstr("loadPIREP_send_to") + "</b>")
1634 label.set_use_markup(True)
1635 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1636 xscale = 0.0, yscale = 0.0)
1637 labelAlignment.add(label)
1638 table.attach(labelAlignment, 0, 1, 3, 4)
1639
1640 label = Gtk.Label(bookedFlight.arrivalICAO)
1641 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1642 xscale = 0.0, yscale = 0.0)
1643 labelAlignment.add(label)
1644 table.attach(labelAlignment, 1, 2, 3, 4)
1645
1646 label = Gtk.Label("<b>" + xstr("loadPIREP_send_rating") + "</b>")
1647 label.set_use_markup(True)
1648 labelAlignment = Gtk.Alignment(xalign = 1.0, yalign = 0.5,
1649 xscale = 0.0, yscale = 0.0)
1650 labelAlignment.add(label)
1651 table.attach(labelAlignment, 0, 1, 4, 5)
1652
1653 rating = pirep.rating
1654 label = Gtk.Label()
1655 if rating<0:
1656 label.set_markup('<b><span foreground="red">NO GO</span></b>')
1657 else:
1658 label.set_text("%.1f %%" % (rating,))
1659
1660 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1661 xscale = 0.0, yscale = 0.0)
1662 labelAlignment.add(label)
1663 table.attach(labelAlignment, 1, 2, 4, 5)
1664
1665 dialog.add_button(xstr("button_cancel"), Gtk.ResponseType.REJECT)
1666 dialog.add_button(xstr("viewPIREP"), 1)
1667 dialog.add_button(xstr("sendPIREP"), Gtk.ResponseType.OK)
1668
1669 return dialog
1670
1671 def sendPIREP(self, pirep, callback = None):
1672 """Send the given PIREP."""
1673 self.beginBusy(xstr("sendPIREP_busy"))
1674 self._sendPIREPCallback = callback
1675 self.webHandler.sendPIREP(self._pirepSentCallback, pirep)
1676
1677 def _pirepSentCallback(self, returned, result):
1678 """Callback for the PIREP sending result."""
1679 GObject.idle_add(self._handlePIREPSent, returned, result)
1680
1681 def _handlePIREPSent(self, returned, result):
1682 """Callback for the PIREP sending result."""
1683 self.endBusy()
1684 secondaryMarkup = None
1685 type = Gtk.MessageType.ERROR
1686 if returned:
1687 if result.success:
1688 type = Gtk.MessageType.INFO
1689 messageFormat = xstr("sendPIREP_success")
1690 secondaryMarkup = xstr("sendPIREP_success_sec")
1691 elif result.alreadyFlown:
1692 messageFormat = xstr("sendPIREP_already")
1693 secondaryMarkup = xstr("sendPIREP_already_sec")
1694 elif result.notAvailable:
1695 messageFormat = xstr("sendPIREP_notavail")
1696 else:
1697 messageFormat = xstr("sendPIREP_unknown")
1698 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1699 else:
1700 print("PIREP sending failed", result)
1701 messageFormat = xstr("sendPIREP_failed")
1702 secondaryMarkup = xstr("sendPIREP_failed_sec")
1703
1704 dialog = Gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1705 type = type, message_format = messageFormat)
1706 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1707 dialog.set_title(WINDOW_TITLE_BASE)
1708 if secondaryMarkup is not None:
1709 dialog.format_secondary_markup(secondaryMarkup)
1710
1711 dialog.run()
1712 dialog.hide()
1713
1714 callback = self._sendPIREPCallback
1715 self._sendPIREPCallback = None
1716 if callback is not None:
1717 callback(returned, result)
1718
1719 def sendBugReport(self, summary, description, email, callback = None):
1720 """Send the bug report with the given data."""
1721 description += "\n\n" + ("=" * 40)
1722 description += "\n\nThe contents of the log:\n\n"
1723
1724 for (timestampString, text) in self._logger.lines:
1725 description += str(formatFlightLogLine(timestampString, text))
1726
1727 description += "\n\n" + ("=" * 40)
1728 description += "\n\nThe contents of the debug log:\n\n"
1729
1730 buffer = self._debugLogView.get_buffer()
1731 description += buffer.get_text(buffer.get_start_iter(),
1732 buffer.get_end_iter(), True)
1733
1734 self.beginBusy(xstr("sendBugReport_busy"))
1735 self._sendBugReportCallback = callback
1736 self.webHandler.sendBugReport(self._bugReportSentCallback,
1737 summary, description, email)
1738
1739 def _cefInitialized(self):
1740 """Called when CEF has been initialized."""
1741 self._acars.start()
1742 cef.initializeSimBrief()
1743
1744 def _bugReportSentCallback(self, returned, result):
1745 """Callback function for the bug report sending result."""
1746 GObject.idle_add(self._handleBugReportSent, returned, result)
1747
1748 def _handleBugReportSent(self, returned, result):
1749 """Callback for the bug report sending result."""
1750 self.endBusy()
1751 secondaryMarkup = None
1752 type = Gtk.MessageType.ERROR
1753 if returned:
1754 if result.success:
1755 type = Gtk.MessageType.INFO
1756 messageFormat = xstr("sendBugReport_success") % (result.ticketID,)
1757 secondaryMarkup = xstr("sendBugReport_success_sec")
1758 else:
1759 messageFormat = xstr("sendBugReport_error")
1760 secondaryMarkup = xstr("sendBugReport_siteerror_sec")
1761 else:
1762 messageFormat = xstr("sendBugReport_error")
1763 secondaryMarkup = xstr("sendBugReport_error_sec")
1764
1765 dialog = Gtk.MessageDialog(parent = self._wizard.gui._bugReportDialog,
1766 type = type, message_format = messageFormat)
1767 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
1768 dialog.set_title(WINDOW_TITLE_BASE)
1769 if secondaryMarkup is not None:
1770 dialog.format_secondary_markup(secondaryMarkup)
1771
1772 dialog.run()
1773 dialog.hide()
1774
1775 callback = self._sendBugReportCallback
1776 self._sendBugReportCallback = None
1777 if callback is not None:
1778 callback(returned, result)
1779
1780 def _listenHotkeys(self):
1781 """Setup the hotkeys based on the configuration."""
1782 if self._hotkeySetID is None and self._simulator is not None:
1783 self._pilotHotkeyIndex = None
1784 self._checklistHotkeyIndex = None
1785
1786 hotkeys = []
1787
1788 config = self.config
1789 if config.enableSounds and config.pilotControlsSounds:
1790 self._pilotHotkeyIndex = len(hotkeys)
1791 hotkeys.append(config.pilotHotkey)
1792
1793 if config.enableChecklists:
1794 self._checklistHotkeyIndex = len(hotkeys)
1795 hotkeys.append(config.checklistHotkey)
1796
1797 if hotkeys:
1798 self._hotkeySetID = \
1799 self._simulator.listenHotkeys(hotkeys, self._handleHotkeys)
1800
1801 def _clearHotkeys(self):
1802 """Clear the hotkeys."""
1803 if self._hotkeySetID is not None:
1804 self._hotkeySetID=None
1805 self._simulator.clearHotkeys()
1806
1807 def _handleHotkeys(self, id, hotkeys):
1808 """Handle the hotkeys."""
1809 if id==self._hotkeySetID:
1810 for index in hotkeys:
1811 if index==self._pilotHotkeyIndex:
1812 print("gui.GUI._handleHotkeys: pilot hotkey pressed")
1813 self._flight.pilotHotkeyPressed()
1814 elif index==self._checklistHotkeyIndex:
1815 print("gui.GUI._handleHotkeys: checklist hotkey pressed")
1816 self._flight.checklistHotkeyPressed()
1817 else:
1818 print("gui.GUI._handleHotkeys: unhandled hotkey index:", index)
1819
1820 def _showManual(self, menuitem):
1821 """Show the user's manual."""
1822 webbrowser.open(url ="file://" +
1823 os.path.join(self._programDirectory, "doc", "manual",
1824 getLanguage(), "index.html"),
1825 new = 1)
1826
1827 def _showAbout(self, menuitem):
1828 """Show the about dialog."""
1829 dialog = self._getAboutDialog()
1830 dialog.show_all()
1831 dialog.run()
1832 dialog.hide()
1833
1834 def _getAboutDialog(self):
1835 """Get the about dialog.
1836
1837 If it does not exist yet, it will be created."""
1838 if self._aboutDialog is None:
1839 dialog = Gtk.AboutDialog()
1840 dialog.set_transient_for(self._mainWindow)
1841 dialog.set_modal(True)
1842
1843 logoPath = os.path.join(self._programDirectory, "logo.png")
1844 logo = GdkPixbuf.Pixbuf.new_from_file(logoPath)
1845 dialog.set_logo(logo)
1846
1847 dialog.set_program_name(PROGRAM_NAME)
1848 dialog.set_version(const.VERSION)
1849 dialog.set_copyright("(c) 2012 - 2022 by István Váradi")
1850 dialog.set_website("http://mlx.varadiistvan.hu")
1851 dialog.set_website_label(xstr("about_website"))
1852
1853 isHungarian = getLanguage()=="hu"
1854 authors = []
1855 for (familyName, firstName, role) in GUI._authors:
1856 author = "%s %s" % \
1857 (familyName if isHungarian else firstName,
1858 firstName if isHungarian else familyName)
1859 role = xstr("about_role_" + role)
1860 authors.append(author + " (" + role + ")")
1861 dialog.set_authors(authors)
1862
1863 dialog.set_license(xstr("about_license"))
1864
1865 self._aboutDialog = dialog
1866
1867 return self._aboutDialog
1868
1869 def _showAboutURL(self, dialog, link, user_data):
1870 """Show the about URL."""
1871 webbrowser.open(url = link, new = 1)
1872
1873 def _setTakeoffAntiIceOn(self, value):
1874 """Set the anti-ice on indicator."""
1875 self._wizard.takeoffAntiIceOn = value
1876
1877 def _setLandingAntiIceOn(self, value):
1878 """Set the anti-ice on indicator."""
1879 self._wizard.landingAntiIceOn = value
1880
1881 def _getCredentialsCallback(self):
1882 """Called when the web handler asks for the credentials."""
1883 # FIXME: this is almost the same as
1884 # SimBriefSetupPage._getCredentialsCallback
1885 with self._credentialsCondition:
1886 self._credentialsAvailable = False
1887
1888 GObject.idle_add(self._getCredentials)
1889
1890 while not self._credentialsAvailable:
1891 self._credentialsCondition.wait()
1892
1893 return (self._credentialsUserName, self._credentialsPassword)
1894
1895 def _getCredentials(self):
1896 """Get the credentials."""
1897 # FIXME: this is almost the same as
1898 # SimBriefSetupPage._getCredentials
1899 with self._credentialsCondition:
1900 config = self.config
1901
1902 dialog = CredentialsDialog(self, config.pilotID, config.password,
1903 xstr("login_title"),
1904 xstr("button_cancel"),
1905 xstr("button_ok"),
1906 xstr("label_pilotID"),
1907 xstr("login_pilotID_tooltip"),
1908 xstr("label_password"),
1909 xstr("login_password_tooltip"),
1910 xstr("login_info"),
1911 config.rememberPassword,
1912 xstr("remember_password"),
1913 xstr("login_remember_tooltip"))
1914 response = dialog.run()
1915
1916 if response==Gtk.ResponseType.OK:
1917 self._credentialsUserName = dialog.userName
1918 self._credentialsPassword = dialog.password
1919 rememberPassword = dialog.rememberPassword
1920
1921 config.pilotID = self._credentialsUserName
1922
1923 config.password = \
1924 self._credentialsPassword if rememberPassword else ""
1925 config.rememberPassword = rememberPassword
1926
1927 config.save()
1928 else:
1929 self._credentialsUserName = None
1930 self._credentialsPassword = None
1931
1932 self._credentialsAvailable = True
1933 self._credentialsCondition.notify()
1934
1935 def _updateDone(self):
1936 """Called when the update is done.
1937
1938 It checks if we already know the PID, and if not, asks the user whether
1939 to register."""
1940 cef.initialize(self._cefInitialized)
1941
1942 if not self.config.pilotID and not self.config.password:
1943 dialog = Gtk.MessageDialog(parent = self._mainWindow,
1944 type = Gtk.MessageType.QUESTION,
1945 message_format = xstr("register_ask"))
1946
1947 dialog.set_title(WINDOW_TITLE_BASE)
1948 dialog.format_secondary_markup(xstr("register_ask_sec"))
1949
1950 dialog.add_button(xstr("button_cancel"), 0)
1951 dialog.add_button(xstr("button_register"), 1)
1952 dialog.set_default_response(1)
1953
1954 result = dialog.run()
1955 dialog.hide()
1956 if result == 1:
1957 self._wizard.jumpPage("register")
Note: See TracBrowser for help on using the repository browser.