source: src/mlx/gui/gui.py@ 918:cfa4d89368fa

Last change on this file since 918:cfa4d89368fa was 899:ec5238c77005, checked in by István Váradi <ivaradi@…>, 7 years ago

Cockpit and cabin crew weight is included in DOW, extra people are calulcated with specific weights, and passenger weight is set to 84 kgs (re #332)

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