source: src/mlx/gui/gui.py@ 383:fcb9932b14ee

Last change on this file since 383:fcb9932b14ee was 383:fcb9932b14ee, checked in by István Váradi <ivaradi@…>, 12 years ago

Added the new Cruise page where the cruise level can be modified (#160)

File size: 51.5 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.pirep import PIREPViewer
16
17import mlx.const as const
18import mlx.fs as fs
19import mlx.flight as flight
20import mlx.logger as logger
21import mlx.acft as acft
22import mlx.web as web
23import mlx.singleton as singleton
24import mlx.airports as airports
25from mlx.i18n import xstr, getLanguage
26from mlx.pirep import PIREP
27
28import time
29import threading
30import sys
31import datetime
32import webbrowser
33
34#------------------------------------------------------------------------------
35
36## @package mlx.gui.gui
37#
38# The main GUI class.
39#
40# The \ref GUI class is the main class of the GUI. It is a connection listener,
41# and aggregates all the windows, the menu, etc. It maintains the connection to
42# the simulator as well as the flight object.
43
44#------------------------------------------------------------------------------
45
46class GUI(fs.ConnectionListener):
47 """The main GUI class."""
48 _authors = [ (u"Váradi", u"István", "prog_test"),
49 (u"Galyassy", u"Tamás", "negotiation"),
50 (u"Petrovszki", u"Gábor", "test"),
51 (u"Zsebényi-Loksa", u"Gergely", "test"),
52 (u"Kurják", u"Ákos", "test"),
53 (u"Nagy", u"Dániel", "test"),
54 (u"Radó", u"Iván", "test") ]
55
56 def __init__(self, programDirectory, config):
57 """Construct the GUI."""
58 gobject.threads_init()
59
60 self._programDirectory = programDirectory
61 self.config = config
62 self._connecting = False
63 self._reconnecting = False
64 self._connected = False
65 self._logger = logger.Logger(self)
66 self._flight = None
67 self._simulator = None
68 self._monitoring = False
69
70 self._fleet = None
71
72 self._fleetCallback = None
73
74 self._updatePlaneCallback = None
75 self._updatePlaneTailNumber = None
76 self._updatePlaneStatus = None
77 self._updatePlaneGateNumber = None
78
79 self._stdioLock = threading.Lock()
80 self._stdioText = ""
81 self._stdioStartingLine = True
82
83 self._sendPIREPCallback = None
84
85 self.webHandler = web.Handler()
86 self.webHandler.start()
87
88 self.toRestart = False
89
90 def build(self, iconDirectory):
91 """Build the GUI."""
92
93 self._mainWindow = window = gtk.Window()
94 window.set_title(WINDOW_TITLE_BASE)
95 window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
96 window.set_resizable(False)
97 window.connect("delete-event", self.deleteMainWindow)
98 window.connect("window-state-event", self._handleMainWindowState)
99 accelGroup = gtk.AccelGroup()
100 window.add_accel_group(accelGroup)
101
102 mainVBox = gtk.VBox()
103 window.add(mainVBox)
104
105 self._preferences = Preferences(self)
106 self._checklistEditor = ChecklistEditor(self)
107 self._approachCalloutsEditor = ApproachCalloutsEditor(self)
108
109 menuBar = self._buildMenuBar(accelGroup)
110 mainVBox.pack_start(menuBar, False, False, 0)
111
112 self._notebook = gtk.Notebook()
113 mainVBox.pack_start(self._notebook, True, True, 4)
114
115 self._wizard = Wizard(self)
116 label = gtk.Label(xstr("tab_flight"))
117 label.set_use_underline(True)
118 label.set_tooltip_text(xstr("tab_flight_tooltip"))
119 self._notebook.append_page(self._wizard, label)
120
121 self._flightInfo = FlightInfo(self)
122 label = gtk.Label(xstr("tab_flight_info"))
123 label.set_use_underline(True)
124 label.set_tooltip_text(xstr("tab_flight_info_tooltip"))
125 self._notebook.append_page(self._flightInfo, label)
126 self._flightInfo.disable()
127
128 self._weightHelp = WeightHelp(self)
129 label = gtk.Label(xstr("tab_weight_help"))
130 label.set_use_underline(True)
131 label.set_tooltip_text(xstr("tab_weight_help_tooltip"))
132 self._notebook.append_page(self._weightHelp, label)
133
134 (logWidget, self._logView) = self._buildLogWidget()
135 addFaultTag(self._logView.get_buffer())
136 label = gtk.Label(xstr("tab_log"))
137 label.set_use_underline(True)
138 label.set_tooltip_text(xstr("tab_log_tooltip"))
139 self._notebook.append_page(logWidget, label)
140
141 self._fleetGateStatus = FleetGateStatus(self)
142 label = gtk.Label(xstr("tab_gates"))
143 label.set_use_underline(True)
144 label.set_tooltip_text(xstr("tab_gates_tooltip"))
145 self._notebook.append_page(self._fleetGateStatus, label)
146
147 (self._debugLogWidget, self._debugLogView) = self._buildLogWidget()
148 self._debugLogWidget.show_all()
149
150 mainVBox.pack_start(gtk.HSeparator(), False, False, 0)
151
152 self._statusbar = Statusbar(iconDirectory)
153 mainVBox.pack_start(self._statusbar, False, False, 0)
154
155 self._notebook.connect("switch-page", self._notebookPageSwitch)
156
157 self._monitorWindow = MonitorWindow(self, iconDirectory)
158 self._monitorWindow.add_accel_group(accelGroup)
159 self._monitorWindowX = None
160 self._monitorWindowY = None
161 self._selfToggling = False
162
163 self._pirepViewer = PIREPViewer(self)
164
165 window.show_all()
166 self._wizard.grabDefault()
167 self._weightHelp.reset()
168 self._weightHelp.disable()
169
170 self._statusIcon = StatusIcon(iconDirectory, self)
171
172 self._busyCursor = gdk.Cursor(gdk.CursorType.WATCH if pygobject
173 else gdk.WATCH)
174
175 self._loadPIREPDialog = None
176 self._lastLoadedPIREP = None
177
178 self._hotkeySetID = None
179 self._pilotHotkeyIndex = None
180 self._checklistHotkeyIndex = None
181
182 self._aboutDialog = None
183
184 @property
185 def mainWindow(self):
186 """Get the main window of the GUI."""
187 return self._mainWindow
188
189 @property
190 def logger(self):
191 """Get the logger used by us."""
192 return self._logger
193
194 @property
195 def simulator(self):
196 """Get the simulator used by us."""
197 return self._simulator
198
199 @property
200 def flight(self):
201 """Get the flight being performed."""
202 return self._flight
203
204 @property
205 def entranceExam(self):
206 """Get whether an entrance exam is about to be taken."""
207 return self._wizard.entranceExam
208
209 @property
210 def loggedIn(self):
211 """Indicate if the user has logged in properly."""
212 return self._wizard.loggedIn
213
214 @property
215 def loginResult(self):
216 """Get the result of the login."""
217 return self._wizard.loginResult
218
219 @property
220 def bookedFlight(self):
221 """Get the booked flight selected, if any."""
222 return self._wizard.bookedFlight
223
224 @property
225 def numCrew(self):
226 """Get the number of crew members."""
227 return self._wizard.numCrew
228
229 @property
230 def numPassengers(self):
231 """Get the number of passengers."""
232 return self._wizard.numPassengers
233
234 @property
235 def bagWeight(self):
236 """Get the bag weight."""
237 return self._wizard.bagWeight
238
239 @property
240 def cargoWeight(self):
241 """Get the cargo weight."""
242 return self._wizard.cargoWeight
243
244 @property
245 def mailWeight(self):
246 """Get the mail weight."""
247 return self._wizard.mailWeight
248
249 @property
250 def zfw(self):
251 """Get Zero-Fuel Weight calculated for the current flight."""
252 return self._wizard.zfw
253
254 @property
255 def filedCruiseAltitude(self):
256 """Get cruise altitude filed for the current flight."""
257 return self._wizard.filedCruiseAltitude
258
259 @property
260 def cruiseAltitude(self):
261 """Get cruise altitude set for the current flight."""
262 return self._wizard.cruiseAltitude
263
264 @property
265 def loggableCruiseAltitude(self):
266 """Get the cruise altitude that can be logged."""
267 return self._wizard.loggableCruiseAltitude
268
269 @property
270 def route(self):
271 """Get the flight route."""
272 return self._wizard.route
273
274 @property
275 def departureMETAR(self):
276 """Get the METAR of the deprature airport."""
277 return self._wizard.departureMETAR
278
279 @property
280 def arrivalMETAR(self):
281 """Get the METAR of the deprature airport."""
282 return self._wizard.arrivalMETAR
283
284 @property
285 def departureRunway(self):
286 """Get the name of the departure runway."""
287 return self._wizard.departureRunway
288
289 @property
290 def sid(self):
291 """Get the SID."""
292 return self._wizard.sid
293
294 @property
295 def v1(self):
296 """Get the V1 speed calculated for the flight."""
297 return self._wizard.v1
298
299 @property
300 def vr(self):
301 """Get the Vr speed calculated for the flight."""
302 return self._wizard.vr
303
304 @property
305 def v2(self):
306 """Get the V2 speed calculated for the flight."""
307 return self._wizard.v2
308
309 @property
310 def rtoIndicated(self):
311 """Get whether the pilot has indicated than an RTO has occured."""
312 return self._wizard.rtoIndicated
313
314 @property
315 def arrivalRunway(self):
316 """Get the arrival runway."""
317 return self._wizard.arrivalRunway
318
319 @property
320 def star(self):
321 """Get the STAR."""
322 return self._wizard.star
323
324 @property
325 def transition(self):
326 """Get the transition."""
327 return self._wizard.transition
328
329 @property
330 def approachType(self):
331 """Get the approach type."""
332 return self._wizard.approachType
333
334 @property
335 def vref(self):
336 """Get the Vref speed calculated for the flight."""
337 return self._wizard.vref
338
339 @property
340 def flightType(self):
341 """Get the flight type."""
342 return self._wizard.flightType
343
344 @property
345 def online(self):
346 """Get whether the flight was online or not."""
347 return self._wizard.online
348
349 @property
350 def comments(self):
351 """Get the comments."""
352 return self._flightInfo.comments
353
354 @property
355 def hasComments(self):
356 """Indicate whether there is a comment."""
357 return self._flightInfo.hasComments
358
359 @property
360 def flightDefects(self):
361 """Get the flight defects."""
362 return self._flightInfo.flightDefects
363
364 @property
365 def delayCodes(self):
366 """Get the delay codes."""
367 return self._flightInfo.delayCodes
368
369 def run(self):
370 """Run the GUI."""
371 if self.config.autoUpdate:
372 self._updater = Updater(self,
373 self._programDirectory,
374 self.config.updateURL,
375 self._mainWindow)
376 self._updater.start()
377
378 singleton.raiseCallback = self.raiseCallback
379 gtk.main()
380 singleton.raiseCallback = None
381
382 self._disconnect()
383
384 def connected(self, fsType, descriptor):
385 """Called when we have connected to the simulator."""
386 self._connected = True
387 self._logger.untimedMessage("MLX %s connected to the simulator %s" % \
388 (const.VERSION, descriptor))
389 fs.sendMessage(const.MESSAGETYPE_INFORMATION,
390 "Welcome to MAVA Logger X " + const.VERSION)
391 gobject.idle_add(self._handleConnected, fsType, descriptor)
392
393 def _handleConnected(self, fsType, descriptor):
394 """Called when the connection to the simulator has succeeded."""
395 self._statusbar.updateConnection(self._connecting, self._connected)
396 self.endBusy()
397 if not self._reconnecting:
398 self._wizard.connected(fsType, descriptor)
399 self._reconnecting = False
400 self._listenHotkeys()
401
402 def connectionFailed(self):
403 """Called when the connection failed."""
404 self._logger.untimedMessage("Connection to the simulator failed")
405 gobject.idle_add(self._connectionFailed)
406
407 def _connectionFailed(self):
408 """Called when the connection failed."""
409 self.endBusy()
410 self._statusbar.updateConnection(self._connecting, self._connected)
411
412 dialog = gtk.MessageDialog(parent = self._mainWindow,
413 type = MESSAGETYPE_ERROR,
414 message_format = xstr("conn_failed"))
415
416 dialog.set_title(WINDOW_TITLE_BASE)
417 dialog.format_secondary_markup(xstr("conn_failed_sec"))
418
419 dialog.add_button(xstr("button_cancel"), 0)
420 dialog.add_button(xstr("button_tryagain"), 1)
421 dialog.set_default_response(1)
422
423 result = dialog.run()
424 dialog.hide()
425 if result == 1:
426 self.beginBusy(xstr("connect_busy"))
427 self._simulator.reconnect()
428 else:
429 self.reset()
430
431 def disconnected(self):
432 """Called when we have disconnected from the simulator."""
433 self._connected = False
434 self._logger.untimedMessage("Disconnected from the simulator")
435
436 gobject.idle_add(self._disconnected)
437
438 def _disconnected(self):
439 """Called when we have disconnected from the simulator unexpectedly."""
440 self._statusbar.updateConnection(self._connecting, self._connected)
441
442 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
443 message_format = xstr("conn_broken"),
444 parent = self._mainWindow)
445 dialog.set_title(WINDOW_TITLE_BASE)
446 dialog.format_secondary_markup(xstr("conn_broken_sec"))
447
448 dialog.add_button(xstr("button_cancel"), 0)
449 dialog.add_button(xstr("button_reconnect"), 1)
450 dialog.set_default_response(1)
451
452 result = dialog.run()
453 dialog.hide()
454 if result == 1:
455 self.beginBusy(xstr("connect_busy"))
456 self._reconnecting = True
457 self._simulator.reconnect()
458 else:
459 self.reset()
460
461 def enableFlightInfo(self):
462 """Enable the flight info tab."""
463 self._flightInfo.enable()
464
465 def cancelFlight(self):
466 """Cancel the current file, if the user confirms it."""
467 dialog = gtk.MessageDialog(parent = self._mainWindow,
468 type = MESSAGETYPE_QUESTION,
469 message_format = xstr("cancelFlight_question"))
470
471 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
472 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
473
474 dialog.set_title(WINDOW_TITLE_BASE)
475 result = dialog.run()
476 dialog.hide()
477
478 if result==RESPONSETYPE_YES:
479 self.reset()
480
481 def reset(self):
482 """Reset the GUI."""
483 self._disconnect()
484
485 self._flightInfo.reset()
486 self._flightInfo.disable()
487 self.resetFlightStatus()
488
489 self._weightHelp.reset()
490 self._weightHelp.disable()
491 self._notebook.set_current_page(0)
492
493 self._logView.get_buffer().set_text("")
494
495 if self.loggedIn:
496 self._wizard.reloadFlights(self._handleReloadResult)
497 else:
498 self._wizard.reset(None)
499
500 def _handleReloadResult(self, returned, result):
501 """Handle the result of the reloading of the flights."""
502 self._wizard.reset(result if returned and result.loggedIn else None)
503
504 def _disconnect(self, closingMessage = None, duration = 3):
505 """Disconnect from the simulator if connected."""
506 self.stopMonitoring()
507 self._clearHotkeys()
508
509 if self._connected:
510 if closingMessage is None:
511 self._flight.simulator.disconnect()
512 else:
513 fs.sendMessage(const.MESSAGETYPE_ENVIRONMENT,
514 closingMessage, duration,
515 disconnect = True)
516 self._connected = False
517
518 self._connecting = False
519 self._reconnecting = False
520 self._statusbar.updateConnection(False, False)
521 self._weightHelp.disable()
522
523 return True
524
525 def insertFlightLogLine(self, index, timestampString, text, isFault):
526 """Insert the flight log line with the given data."""
527 gobject.idle_add(self._insertFlightLogLine, index,
528 formatFlightLogLine(timestampString, text),
529 isFault)
530
531 def _insertFlightLogLine(self, index, line, isFault):
532 """Perform the real insertion.
533
534 To be called from the event loop."""
535 buffer = self._logView.get_buffer()
536 lineIter = buffer.get_iter_at_line(index)
537 insertTextBuffer(buffer, lineIter, line, isFault = isFault)
538 self._logView.scroll_mark_onscreen(buffer.get_insert())
539
540 def removeFlightLogLine(self, index):
541 """Remove the flight log line with the given index."""
542 gobject.idle_add(self._removeFlightLogLine, index)
543
544 def _removeFlightLogLine(self, index):
545 """Perform the real removal."""
546 buffer = self._logView.get_buffer()
547 startIter = buffer.get_iter_at_line(index)
548 endIter = buffer.get_iter_at_line(index+1)
549 buffer.delete(startIter, endIter)
550 self._logView.scroll_mark_onscreen(buffer.get_insert())
551
552 def check(self, flight, aircraft, logger, oldState, state):
553 """Update the data."""
554 gobject.idle_add(self._monitorWindow.setData, state)
555 gobject.idle_add(self._statusbar.updateTime, state.timestamp)
556
557 def resetFlightStatus(self):
558 """Reset the status of the flight."""
559 self._statusbar.resetFlightStatus()
560 self._statusbar.updateTime()
561 self._statusIcon.resetFlightStatus()
562
563 def setStage(self, stage):
564 """Set the stage of the flight."""
565 gobject.idle_add(self._setStage, stage)
566
567 def _setStage(self, stage):
568 """Set the stage of the flight."""
569 self._statusbar.setStage(stage)
570 self._statusIcon.setStage(stage)
571 self._wizard.setStage(stage)
572 if stage==const.STAGE_END:
573 welcomeMessage = \
574 airports.getWelcomeMessage(self.bookedFlight.arrivalICAO)
575 self._disconnect(closingMessage =
576 "Flight plan closed. " + welcomeMessage,
577 duration = 5)
578
579 def setRating(self, rating):
580 """Set the rating of the flight."""
581 gobject.idle_add(self._setRating, rating)
582
583 def _setRating(self, rating):
584 """Set the rating of the flight."""
585 self._statusbar.setRating(rating)
586 self._statusIcon.setRating(rating)
587
588 def setNoGo(self, reason):
589 """Set the rating of the flight to No-Go with the given reason."""
590 gobject.idle_add(self._setNoGo, reason)
591
592 def _setNoGo(self, reason):
593 """Set the rating of the flight."""
594 self._statusbar.setNoGo(reason)
595 self._statusIcon.setNoGo(reason)
596
597 def _handleMainWindowState(self, window, event):
598 """Hande a change in the state of the window"""
599 iconified = gdk.WindowState.ICONIFIED if pygobject \
600 else gdk.WINDOW_STATE_ICONIFIED
601
602 if (event.changed_mask&WINDOW_STATE_WITHDRAWN)!=0:
603 if (event.new_window_state&WINDOW_STATE_WITHDRAWN)!=0:
604 self._statusIcon.mainWindowHidden()
605 else:
606 self._statusIcon.mainWindowShown()
607
608 if self.config.hideMinimizedWindow and not pygobject and \
609 (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
610 (event.new_window_state&WINDOW_STATE_ICONIFIED)!=0:
611 self.hideMainWindow(savePosition = False)
612 elif (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
613 (event.new_window_state&WINDOW_STATE_ICONIFIED)==0:
614 self._mainWindow.present()
615
616 def raiseCallback(self):
617 """Callback for the singleton handling code."""
618 gobject.idle_add(self.raiseMainWindow)
619
620 def raiseMainWindow(self):
621 """Show the main window if invisible, and raise it."""
622 if not self._mainWindow.get_visible():
623 self.showMainWindow()
624 self._mainWindow.present()
625
626 def deleteMainWindow(self, window, event):
627 """Handle the delete event for the main window."""
628 if self.config.quitOnClose:
629 self._quit()
630 else:
631 self.hideMainWindow()
632 return True
633
634 def hideMainWindow(self, savePosition = True):
635 """Hide the main window and save its position."""
636 if savePosition:
637 (self._mainWindowX, self._mainWindowY) = \
638 self._mainWindow.get_window().get_root_origin()
639 else:
640 self._mainWindowX = self._mainWindowY = None
641 self._mainWindow.hide()
642 return True
643
644 def showMainWindow(self):
645 """Show the main window at its former position."""
646 if self._mainWindowX is not None and self._mainWindowY is not None:
647 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
648
649 if pygobject:
650 self._mainWindow.show()
651 else:
652 self._mainWindow.present()
653 self._mainWindow.deiconify()
654
655 def toggleMainWindow(self):
656 """Toggle the main window."""
657 if self._mainWindow.get_visible():
658 self.hideMainWindow()
659 else:
660 self.showMainWindow()
661
662 def hideMonitorWindow(self, savePosition = True):
663 """Hide the monitor window."""
664 if savePosition:
665 (self._monitorWindowX, self._monitorWindowY) = \
666 self._monitorWindow.get_window().get_root_origin()
667 else:
668 self._monitorWindowX = self._monitorWindowY = None
669 self._monitorWindow.hide()
670 self._statusIcon.monitorWindowHidden()
671 if self._showMonitorMenuItem.get_active():
672 self._selfToggling = True
673 self._showMonitorMenuItem.set_active(False)
674 return True
675
676 def showMonitorWindow(self):
677 """Show the monitor window."""
678 if self._monitorWindowX is not None and self._monitorWindowY is not None:
679 self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
680 self._monitorWindow.show_all()
681 self._statusIcon.monitorWindowShown()
682 if not self._showMonitorMenuItem.get_active():
683 self._selfToggling = True
684 self._showMonitorMenuItem.set_active(True)
685
686 def _toggleMonitorWindow(self, menuItem):
687 if self._selfToggling:
688 self._selfToggling = False
689 elif self._monitorWindow.get_visible():
690 self.hideMonitorWindow()
691 else:
692 self.showMonitorWindow()
693
694 def restart(self):
695 """Quit and restart the application."""
696 self.toRestart = True
697 self._quit(force = True)
698
699 def flushStdIO(self):
700 """Flush any text to the standard error that could not be logged."""
701 if self._stdioText:
702 sys.__stderr__.write(self._stdioText)
703
704 def writeStdIO(self, text):
705 """Write the given text into standard I/O log."""
706 with self._stdioLock:
707 self._stdioText += text
708
709 gobject.idle_add(self._writeStdIO)
710
711 def beginBusy(self, message):
712 """Begin a period of background processing."""
713 self._wizard.set_sensitive(False)
714 self._weightHelp.set_sensitive(False)
715 self._mainWindow.get_window().set_cursor(self._busyCursor)
716 self._statusbar.updateBusyState(message)
717
718 def endBusy(self):
719 """End a period of background processing."""
720 self._mainWindow.get_window().set_cursor(None)
721 self._weightHelp.set_sensitive(True)
722 self._wizard.set_sensitive(True)
723 self._statusbar.updateBusyState(None)
724
725 def initializeWeightHelp(self):
726 """Initialize the weight help tab."""
727 self._weightHelp.reset()
728 self._weightHelp.enable()
729
730 def getFleetAsync(self, callback = None, force = None):
731 """Get the fleet asynchronously."""
732 gobject.idle_add(self.getFleet, callback, force)
733
734 def getFleet(self, callback = None, force = False):
735 """Get the fleet.
736
737 If force is False, and we already have a fleet retrieved,
738 that one will be used."""
739 if self._fleet is None or force:
740 self._fleetCallback = callback
741 self.beginBusy(xstr("fleet_busy"))
742 self.webHandler.getFleet(self._fleetResultCallback)
743 else:
744 callback(self._fleet)
745
746 def updateRTO(self, inLoop = False):
747 """Indicate that the RTO state should be updated."""
748 if inLoop:
749 self._wizard.updateRTO()
750 else:
751 gobject.idle_add(self.updateRTO, True)
752
753 def rtoToggled(self, indicated):
754 """Called when the user has toggled the RTO checkbox."""
755 self._flight.rtoToggled(indicated)
756
757 def _fleetResultCallback(self, returned, result):
758 """Called when the fleet has been queried."""
759 gobject.idle_add(self._handleFleetResult, returned, result)
760
761 def _handleFleetResult(self, returned, result):
762 """Handle the fleet result."""
763 self.endBusy()
764 if returned:
765 self._fleet = result.fleet
766 else:
767 self._fleet = None
768
769 dialog = gtk.MessageDialog(parent = self.mainWindow,
770 type = MESSAGETYPE_ERROR,
771 message_format = xstr("fleet_failed"))
772 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
773 dialog.set_title(WINDOW_TITLE_BASE)
774 dialog.run()
775 dialog.hide()
776
777 callback = self._fleetCallback
778 self._fleetCallback = None
779 if callback is not None:
780 callback(self._fleet)
781 self._fleetGateStatus.handleFleet(self._fleet)
782
783 def updatePlane(self, tailNumber, status,
784 gateNumber = None, callback = None):
785 """Update the status of the given plane."""
786 self.beginBusy(xstr("fleet_update_busy"))
787
788 self._updatePlaneCallback = callback
789
790 self._updatePlaneTailNumber = tailNumber
791 self._updatePlaneStatus = status
792 self._updatePlaneGateNumber = gateNumber
793
794 self.webHandler.updatePlane(self._updatePlaneResultCallback,
795 tailNumber, status, gateNumber)
796
797 def _updatePlaneResultCallback(self, returned, result):
798 """Called when the status of a plane has been updated."""
799 gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
800
801 def _handleUpdatePlaneResult(self, returned, result):
802 """Handle the plane update result."""
803 self.endBusy()
804 if returned:
805 success = result.success
806 if success:
807 if self._fleet is not None:
808 self._fleet.updatePlane(self._updatePlaneTailNumber,
809 self._updatePlaneStatus,
810 self._updatePlaneGateNumber)
811 self._fleetGateStatus.handleFleet(self._fleet)
812 else:
813 dialog = gtk.MessageDialog(parent = self.mainWindow,
814 type = MESSAGETYPE_ERROR,
815 message_format = xstr("fleet_update_failed"))
816 dialog.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
817 dialog.set_title(WINDOW_TITLE_BASE)
818 dialog.run()
819 dialog.hide()
820
821 success = None
822
823 callback = self._updatePlaneCallback
824 self._updatePlaneCallback = None
825 if callback is not None:
826 callback(success)
827
828 def _writeStdIO(self):
829 """Perform the real writing."""
830 with self._stdioLock:
831 text = self._stdioText
832 self._stdioText = ""
833 if not text: return
834
835 lines = text.splitlines()
836 if text[-1]=="\n":
837 text = ""
838 else:
839 text = lines[-1]
840 lines = lines[:-1]
841
842 now = datetime.datetime.now()
843 timeStr = "%02d:%02d:%02d: " % (now.hour, now.minute, now.second)
844
845 for line in lines:
846 #print >> sys.__stdout__, line
847 if self._stdioStartingLine:
848 self._writeLog(timeStr, self._debugLogView)
849 self._writeLog(line + "\n", self._debugLogView)
850 self._stdioStartingLine = True
851
852 if text:
853 #print >> sys.__stdout__, text,
854 if self._stdioStartingLine:
855 self._writeLog(timeStr, self._debugLogView)
856 self._writeLog(text, self._debugLogView)
857 self._stdioStartingLine = False
858
859 def connectSimulator(self, aircraftType):
860 """Connect to the simulator for the first time."""
861 self._logger.reset()
862
863 self._flight = flight.Flight(self._logger, self)
864 self._flight.flareTimeFromFS = self.config.flareTimeFromFS
865 self._flight.aircraftType = aircraftType
866 self._flight.aircraft = acft.Aircraft.create(self._flight)
867 self._flight.aircraft._checkers.append(self)
868
869 if self._simulator is None:
870 self._simulator = fs.createSimulator(const.SIM_MSFS9, self)
871 fs.setupMessageSending(self.config, self._simulator)
872 self._setupTimeSync()
873
874 self._flight.simulator = self._simulator
875
876 self.beginBusy(xstr("connect_busy"))
877 self._statusbar.updateConnection(self._connecting, self._connected)
878
879 self._connecting = True
880 self._simulator.connect(self._flight.aircraft)
881
882 def startMonitoring(self):
883 """Start monitoring."""
884 if not self._monitoring:
885 self.simulator.startMonitoring()
886 self._monitoring = True
887
888 def stopMonitoring(self):
889 """Stop monitoring."""
890 if self._monitoring:
891 self.simulator.stopMonitoring()
892 self._monitoring = False
893
894 def cruiseLevelChanged(self):
895 """Called when the cruise level is changed in the flight wizard."""
896 if self._flight is not None:
897 return self._flight.cruiseLevelChanged()
898 else:
899 return False
900
901 def _buildMenuBar(self, accelGroup):
902 """Build the main menu bar."""
903 menuBar = gtk.MenuBar()
904
905 fileMenuItem = gtk.MenuItem(xstr("menu_file"))
906 fileMenu = gtk.Menu()
907 fileMenuItem.set_submenu(fileMenu)
908 menuBar.append(fileMenuItem)
909
910 loadPIREPMenuItem = gtk.ImageMenuItem(gtk.STOCK_OPEN)
911 loadPIREPMenuItem.set_use_stock(True)
912 loadPIREPMenuItem.set_label(xstr("menu_file_loadPIREP"))
913 loadPIREPMenuItem.add_accelerator("activate", accelGroup,
914 ord(xstr("menu_file_loadPIREP_key")),
915 CONTROL_MASK, ACCEL_VISIBLE)
916 loadPIREPMenuItem.connect("activate", self._loadPIREP)
917 fileMenu.append(loadPIREPMenuItem)
918
919 fileMenu.append(gtk.SeparatorMenuItem())
920
921 quitMenuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
922 quitMenuItem.set_use_stock(True)
923 quitMenuItem.set_label(xstr("menu_file_quit"))
924 quitMenuItem.add_accelerator("activate", accelGroup,
925 ord(xstr("menu_file_quit_key")),
926 CONTROL_MASK, ACCEL_VISIBLE)
927 quitMenuItem.connect("activate", self._quit)
928 fileMenu.append(quitMenuItem)
929
930 toolsMenuItem = gtk.MenuItem(xstr("menu_tools"))
931 toolsMenu = gtk.Menu()
932 toolsMenuItem.set_submenu(toolsMenu)
933 menuBar.append(toolsMenuItem)
934
935 checklistMenuItem = gtk.ImageMenuItem(gtk.STOCK_APPLY)
936 checklistMenuItem.set_use_stock(True)
937 checklistMenuItem.set_label(xstr("menu_tools_chklst"))
938 checklistMenuItem.add_accelerator("activate", accelGroup,
939 ord(xstr("menu_tools_chklst_key")),
940 CONTROL_MASK, ACCEL_VISIBLE)
941 checklistMenuItem.connect("activate", self._editChecklist)
942 toolsMenu.append(checklistMenuItem)
943
944 approachCalloutsMenuItem = gtk.ImageMenuItem(gtk.STOCK_EDIT)
945 approachCalloutsMenuItem.set_use_stock(True)
946 approachCalloutsMenuItem.set_label(xstr("menu_tools_callouts"))
947 approachCalloutsMenuItem.add_accelerator("activate", accelGroup,
948 ord(xstr("menu_tools_callouts_key")),
949 CONTROL_MASK, ACCEL_VISIBLE)
950 approachCalloutsMenuItem.connect("activate", self._editApproachCallouts)
951 toolsMenu.append(approachCalloutsMenuItem)
952
953 prefsMenuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
954 prefsMenuItem.set_use_stock(True)
955 prefsMenuItem.set_label(xstr("menu_tools_prefs"))
956 prefsMenuItem.add_accelerator("activate", accelGroup,
957 ord(xstr("menu_tools_prefs_key")),
958 CONTROL_MASK, ACCEL_VISIBLE)
959 prefsMenuItem.connect("activate", self._editPreferences)
960 toolsMenu.append(prefsMenuItem)
961
962 viewMenuItem = gtk.MenuItem(xstr("menu_view"))
963 viewMenu = gtk.Menu()
964 viewMenuItem.set_submenu(viewMenu)
965 menuBar.append(viewMenuItem)
966
967 self._showMonitorMenuItem = gtk.CheckMenuItem()
968 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
969 self._showMonitorMenuItem.set_use_underline(True)
970 self._showMonitorMenuItem.set_active(False)
971 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
972 ord(xstr("menu_view_monitor_key")),
973 CONTROL_MASK, ACCEL_VISIBLE)
974 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
975 viewMenu.append(self._showMonitorMenuItem)
976
977 showDebugMenuItem = gtk.CheckMenuItem()
978 showDebugMenuItem.set_label(xstr("menu_view_debug"))
979 showDebugMenuItem.set_use_underline(True)
980 showDebugMenuItem.set_active(False)
981 showDebugMenuItem.add_accelerator("activate", accelGroup,
982 ord(xstr("menu_view_debug_key")),
983 CONTROL_MASK, ACCEL_VISIBLE)
984 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
985 viewMenu.append(showDebugMenuItem)
986
987 helpMenuItem = gtk.MenuItem(xstr("menu_help"))
988 helpMenu = gtk.Menu()
989 helpMenuItem.set_submenu(helpMenu)
990 menuBar.append(helpMenuItem)
991
992 manualMenuItem = gtk.ImageMenuItem(gtk.STOCK_HELP)
993 manualMenuItem.set_use_stock(True)
994 manualMenuItem.set_label(xstr("menu_help_manual"))
995 manualMenuItem.add_accelerator("activate", accelGroup,
996 ord(xstr("menu_help_manual_key")),
997 CONTROL_MASK, ACCEL_VISIBLE)
998 manualMenuItem.connect("activate", self._showManual)
999 helpMenu.append(manualMenuItem)
1000
1001 helpMenu.append(gtk.SeparatorMenuItem())
1002
1003 aboutMenuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
1004 aboutMenuItem.set_use_stock(True)
1005 aboutMenuItem.set_label(xstr("menu_help_about"))
1006 aboutMenuItem.add_accelerator("activate", accelGroup,
1007 ord(xstr("menu_help_about_key")),
1008 CONTROL_MASK, ACCEL_VISIBLE)
1009 aboutMenuItem.connect("activate", self._showAbout)
1010 helpMenu.append(aboutMenuItem)
1011
1012 return menuBar
1013
1014 def _toggleDebugLog(self, menuItem):
1015 """Toggle the debug log."""
1016 if menuItem.get_active():
1017 label = gtk.Label(xstr("tab_debug_log"))
1018 label.set_use_underline(True)
1019 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
1020 self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
1021 self._notebook.set_current_page(self._debugLogPage)
1022 else:
1023 self._notebook.remove_page(self._debugLogPage)
1024
1025 def _buildLogWidget(self):
1026 """Build the widget for the log."""
1027 alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
1028
1029 alignment.set_padding(padding_top = 8, padding_bottom = 8,
1030 padding_left = 16, padding_right = 16)
1031
1032 logScroller = gtk.ScrolledWindow()
1033 # FIXME: these should be constants in common
1034 logScroller.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1035 else gtk.POLICY_AUTOMATIC,
1036 gtk.PolicyType.AUTOMATIC if pygobject
1037 else gtk.POLICY_AUTOMATIC)
1038 logScroller.set_shadow_type(gtk.ShadowType.IN if pygobject
1039 else gtk.SHADOW_IN)
1040 logView = gtk.TextView()
1041 logView.set_editable(False)
1042 logView.set_cursor_visible(False)
1043 logScroller.add(logView)
1044
1045 logBox = gtk.VBox()
1046 logBox.pack_start(logScroller, True, True, 0)
1047 logBox.set_size_request(-1, 200)
1048
1049 alignment.add(logBox)
1050
1051 return (alignment, logView)
1052
1053 def _writeLog(self, msg, logView, isFault = False):
1054 """Write the given message to the log."""
1055 buffer = logView.get_buffer()
1056 appendTextBuffer(buffer, msg, isFault = isFault)
1057 logView.scroll_mark_onscreen(buffer.get_insert())
1058
1059 def _quit(self, what = None, force = False):
1060 """Quit from the application."""
1061 if force:
1062 result=RESPONSETYPE_YES
1063 else:
1064 dialog = gtk.MessageDialog(parent = self._mainWindow,
1065 type = MESSAGETYPE_QUESTION,
1066 message_format = xstr("quit_question"))
1067
1068 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
1069 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
1070
1071 dialog.set_title(WINDOW_TITLE_BASE)
1072 result = dialog.run()
1073 dialog.hide()
1074
1075 if result==RESPONSETYPE_YES:
1076 self._statusIcon.destroy()
1077 return gtk.main_quit()
1078
1079 def _notebookPageSwitch(self, notebook, page, page_num):
1080 """Called when the current page of the notebook has changed."""
1081 if page_num==0:
1082 gobject.idle_add(self._wizard.grabDefault)
1083 else:
1084 self._mainWindow.set_default(None)
1085
1086 def _editChecklist(self, menuItem):
1087 """Callback for editing the checklists."""
1088 self._checklistEditor.run()
1089
1090 def _editApproachCallouts(self, menuItem):
1091 """Callback for editing the approach callouts."""
1092 self._approachCalloutsEditor.run()
1093
1094 def _editPreferences(self, menuItem):
1095 """Callback for editing the preferences."""
1096 self._clearHotkeys()
1097 self._preferences.run(self.config)
1098 self._setupTimeSync()
1099 self._listenHotkeys()
1100
1101 def _setupTimeSync(self):
1102 """Enable or disable the simulator time synchronization based on the
1103 configuration."""
1104 simulator = self._simulator
1105 if simulator is not None:
1106 if self.config.syncFSTime:
1107 simulator.enableTimeSync()
1108 else:
1109 simulator.disableTimeSync()
1110
1111 def _loadPIREP(self, menuItem):
1112 """Load a PIREP for sending."""
1113 dialog = self._getLoadPirepDialog()
1114
1115 if self._lastLoadedPIREP:
1116 dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP))
1117 else:
1118 pirepDirectory = self.config.pirepDirectory
1119 if pirepDirectory is not None:
1120 dialog.set_current_folder(pirepDirectory)
1121
1122 result = dialog.run()
1123 dialog.hide()
1124
1125 if result==RESPONSETYPE_OK:
1126 self._lastLoadedPIREP = text2unicode(dialog.get_filename())
1127
1128 pirep = PIREP.load(self._lastLoadedPIREP)
1129 if pirep is None:
1130 dialog = gtk.MessageDialog(parent = self._mainWindow,
1131 type = MESSAGETYPE_ERROR,
1132 message_format = xstr("loadPIREP_failed"))
1133 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1134 dialog.set_title(WINDOW_TITLE_BASE)
1135 dialog.format_secondary_markup(xstr("loadPIREP_failed_sec"))
1136 dialog.run()
1137 dialog.hide()
1138 else:
1139 dialog = self._getSendLoadedDialog(pirep)
1140 dialog.show_all()
1141 while True:
1142 result = dialog.run()
1143
1144 if result==RESPONSETYPE_OK:
1145 self.sendPIREP(pirep)
1146 elif result==1:
1147 self._pirepViewer.setPIREP(pirep)
1148 self._pirepViewer.show_all()
1149 self._pirepViewer.run()
1150 self._pirepViewer.hide()
1151 else:
1152 break
1153
1154 dialog.hide()
1155
1156 def _getLoadPirepDialog(self):
1157 """Get the PIREP loading file chooser dialog.
1158
1159 If it is not created yet, it will be created."""
1160 if self._loadPIREPDialog is None:
1161 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
1162 xstr("loadPIREP_browser_title"),
1163 action = FILE_CHOOSER_ACTION_OPEN,
1164 buttons = (gtk.STOCK_CANCEL,
1165 RESPONSETYPE_CANCEL,
1166 gtk.STOCK_OK, RESPONSETYPE_OK),
1167 parent = self._mainWindow)
1168 dialog.set_modal(True)
1169
1170
1171 filter = gtk.FileFilter()
1172 filter.set_name(xstr("file_filter_pireps"))
1173 filter.add_pattern("*.pirep")
1174 dialog.add_filter(filter)
1175
1176 filter = gtk.FileFilter()
1177 filter.set_name(xstr("file_filter_all"))
1178 filter.add_pattern("*.*")
1179 dialog.add_filter(filter)
1180
1181 self._loadPIREPDialog = dialog
1182
1183 return self._loadPIREPDialog
1184
1185 def _getSendLoadedDialog(self, pirep):
1186 """Get a dialog displaying the main information of the flight from the
1187 PIREP and providing Cancel and Send buttons."""
1188 dialog = gtk.Dialog(title = WINDOW_TITLE_BASE + " - " +
1189 xstr("loadPIREP_send_title"),
1190 parent = self._mainWindow,
1191 flags = DIALOG_MODAL)
1192
1193 contentArea = dialog.get_content_area()
1194
1195 label = gtk.Label(xstr("loadPIREP_send_help"))
1196 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1197 xscale = 0.0, yscale = 0.0)
1198 alignment.set_padding(padding_top = 16, padding_bottom = 0,
1199 padding_left = 48, padding_right = 48)
1200 alignment.add(label)
1201 contentArea.pack_start(alignment, False, False, 8)
1202
1203 table = gtk.Table(5, 2)
1204 tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1205 xscale = 0.0, yscale = 0.0)
1206 tableAlignment.set_padding(padding_top = 0, padding_bottom = 32,
1207 padding_left = 48, padding_right = 48)
1208 table.set_row_spacings(4)
1209 table.set_col_spacings(16)
1210 tableAlignment.add(table)
1211 contentArea.pack_start(tableAlignment, True, True, 8)
1212
1213 bookedFlight = pirep.bookedFlight
1214
1215 label = gtk.Label("<b>" + xstr("loadPIREP_send_flightno") + "</b>")
1216 label.set_use_markup(True)
1217 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1218 xscale = 0.0, yscale = 0.0)
1219 labelAlignment.add(label)
1220 table.attach(labelAlignment, 0, 1, 0, 1)
1221
1222 label = gtk.Label(bookedFlight.callsign)
1223 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1224 xscale = 0.0, yscale = 0.0)
1225 labelAlignment.add(label)
1226 table.attach(labelAlignment, 1, 2, 0, 1)
1227
1228 label = gtk.Label("<b>" + xstr("loadPIREP_send_date") + "</b>")
1229 label.set_use_markup(True)
1230 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1231 xscale = 0.0, yscale = 0.0)
1232 labelAlignment.add(label)
1233 table.attach(labelAlignment, 0, 1, 1, 2)
1234
1235 label = gtk.Label(str(bookedFlight.departureTime.date()))
1236 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1237 xscale = 0.0, yscale = 0.0)
1238 labelAlignment.add(label)
1239 table.attach(labelAlignment, 1, 2, 1, 2)
1240
1241 label = gtk.Label("<b>" + xstr("loadPIREP_send_from") + "</b>")
1242 label.set_use_markup(True)
1243 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1244 xscale = 0.0, yscale = 0.0)
1245 labelAlignment.add(label)
1246 table.attach(labelAlignment, 0, 1, 2, 3)
1247
1248 label = gtk.Label(bookedFlight.departureICAO)
1249 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1250 xscale = 0.0, yscale = 0.0)
1251 labelAlignment.add(label)
1252 table.attach(labelAlignment, 1, 2, 2, 3)
1253
1254 label = gtk.Label("<b>" + xstr("loadPIREP_send_to") + "</b>")
1255 label.set_use_markup(True)
1256 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1257 xscale = 0.0, yscale = 0.0)
1258 labelAlignment.add(label)
1259 table.attach(labelAlignment, 0, 1, 3, 4)
1260
1261 label = gtk.Label(bookedFlight.arrivalICAO)
1262 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1263 xscale = 0.0, yscale = 0.0)
1264 labelAlignment.add(label)
1265 table.attach(labelAlignment, 1, 2, 3, 4)
1266
1267 label = gtk.Label("<b>" + xstr("loadPIREP_send_rating") + "</b>")
1268 label.set_use_markup(True)
1269 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1270 xscale = 0.0, yscale = 0.0)
1271 labelAlignment.add(label)
1272 table.attach(labelAlignment, 0, 1, 4, 5)
1273
1274 rating = pirep.rating
1275 label = gtk.Label()
1276 if rating<0:
1277 label.set_markup('<b><span foreground="red">NO GO</span></b>')
1278 else:
1279 label.set_text("%.1f %%" % (rating,))
1280
1281 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1282 xscale = 0.0, yscale = 0.0)
1283 labelAlignment.add(label)
1284 table.attach(labelAlignment, 1, 2, 4, 5)
1285
1286 dialog.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
1287 dialog.add_button(xstr("viewPIREP"), 1)
1288 dialog.add_button(xstr("sendPIREP"), RESPONSETYPE_OK)
1289
1290 return dialog
1291
1292 def sendPIREP(self, pirep, callback = None):
1293 """Send the given PIREP."""
1294 self.beginBusy(xstr("sendPIREP_busy"))
1295 self._sendPIREPCallback = callback
1296 self.webHandler.sendPIREP(self._pirepSentCallback, pirep)
1297
1298 def _pirepSentCallback(self, returned, result):
1299 """Callback for the PIREP sending result."""
1300 gobject.idle_add(self._handlePIREPSent, returned, result)
1301
1302 def _handlePIREPSent(self, returned, result):
1303 """Callback for the PIREP sending result."""
1304 self.endBusy()
1305 secondaryMarkup = None
1306 type = MESSAGETYPE_ERROR
1307 if returned:
1308 if result.success:
1309 type = MESSAGETYPE_INFO
1310 messageFormat = xstr("sendPIREP_success")
1311 secondaryMarkup = xstr("sendPIREP_success_sec")
1312 elif result.alreadyFlown:
1313 messageFormat = xstr("sendPIREP_already")
1314 secondaryMarkup = xstr("sendPIREP_already_sec")
1315 elif result.notAvailable:
1316 messageFormat = xstr("sendPIREP_notavail")
1317 else:
1318 messageFormat = xstr("sendPIREP_unknown")
1319 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1320 else:
1321 print "PIREP sending failed", result
1322 messageFormat = xstr("sendPIREP_failed")
1323 secondaryMarkup = xstr("sendPIREP_failed_sec")
1324
1325 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1326 type = type, message_format = messageFormat)
1327 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1328 dialog.set_title(WINDOW_TITLE_BASE)
1329 if secondaryMarkup is not None:
1330 dialog.format_secondary_markup(secondaryMarkup)
1331
1332 dialog.run()
1333 dialog.hide()
1334
1335 callback = self._sendPIREPCallback
1336 self._sendPIREPCallback = None
1337 if callback is not None:
1338 callback(returned, result)
1339
1340 def _listenHotkeys(self):
1341 """Setup the hotkeys based on the configuration."""
1342 if self._hotkeySetID is None and self._simulator is not None:
1343 self._pilotHotkeyIndex = None
1344 self._checklistHotkeyIndex = None
1345
1346 hotkeys = []
1347
1348 config = self.config
1349 if config.enableSounds and config.pilotControlsSounds:
1350 self._pilotHotkeyIndex = len(hotkeys)
1351 hotkeys.append(config.pilotHotkey)
1352
1353 if config.enableChecklists:
1354 self._checklistHotkeyIndex = len(hotkeys)
1355 hotkeys.append(config.checklistHotkey)
1356
1357 if hotkeys:
1358 self._hotkeySetID = \
1359 self._simulator.listenHotkeys(hotkeys, self._handleHotkeys)
1360
1361 def _clearHotkeys(self):
1362 """Clear the hotkeys."""
1363 if self._hotkeySetID is not None:
1364 self._hotkeySetID=None
1365 self._simulator.clearHotkeys()
1366
1367 def _handleHotkeys(self, id, hotkeys):
1368 """Handle the hotkeys."""
1369 if id==self._hotkeySetID:
1370 for index in hotkeys:
1371 if index==self._pilotHotkeyIndex:
1372 print "gui.GUI._handleHotkeys: pilot hotkey pressed"
1373 self._flight.pilotHotkeyPressed()
1374 elif index==self._checklistHotkeyIndex:
1375 print "gui.GUI._handleHotkeys: checklist hotkey pressed"
1376 self._flight.checklistHotkeyPressed()
1377 else:
1378 print "gui.GUI._handleHotkeys: unhandled hotkey index:", index
1379
1380 def _showManual(self, menuitem):
1381 """Show the user's manual."""
1382 webbrowser.open(url ="file://" +
1383 os.path.join(self._programDirectory, "doc", "manual",
1384 getLanguage(), "index.html"),
1385 new = 1)
1386
1387 def _showAbout(self, menuitem):
1388 """Show the about dialog."""
1389 dialog = self._getAboutDialog()
1390 dialog.show_all()
1391 dialog.run()
1392 dialog.hide()
1393
1394 def _getAboutDialog(self):
1395 """Get the about dialog.
1396
1397 If it does not exist yet, it will be created."""
1398 if self._aboutDialog is None:
1399 dialog = gtk.AboutDialog()
1400 dialog.set_transient_for(self._mainWindow)
1401 dialog.set_modal(True)
1402
1403 logoPath = os.path.join(self._programDirectory, "logo.png")
1404 logo = pixbuf_new_from_file(logoPath)
1405 dialog.set_logo(logo)
1406
1407 dialog.set_program_name(PROGRAM_NAME)
1408 dialog.set_version(const.VERSION)
1409 dialog.set_copyright("(c) 2012 by István Váradi")
1410 dialog.set_website("http://mlx.varadiistvan.hu")
1411 dialog.set_website_label(xstr("about_website"))
1412
1413 isHungarian = getLanguage()=="hu"
1414 authors = []
1415 for (familyName, firstName, role) in GUI._authors:
1416 author = "%s %s" % \
1417 (familyName if isHungarian else firstName,
1418 firstName if isHungarian else familyName)
1419 role = xstr("about_role_" + role)
1420 authors.append(author + " (" + role + ")")
1421 dialog.set_authors(authors)
1422
1423 dialog.set_license(xstr("about_license"))
1424
1425 if not pygobject:
1426 gtk.about_dialog_set_url_hook(self._showAboutURL, None)
1427
1428 self._aboutDialog = dialog
1429
1430 return self._aboutDialog
1431
1432 def _showAboutURL(self, dialog, link, user_data):
1433 """Show the about URL."""
1434 webbrowser.open(url = link, new = 1)
Note: See TracBrowser for help on using the repository browser.