source: src/mlx/gui/gui.py@ 388:61c304ff3d63

Last change on this file since 388:61c304ff3d63 was 384:97052bda0e22, checked in by István Váradi <ivaradi@…>, 12 years ago

Implemented support for entering derate values (#158)

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