source: src/mlx/gui/gui.py@ 478:00d38a068da9

xplane
Last change on this file since 478:00d38a068da9 was 450:d009a75685e8, checked in by István Váradi <ivaradi@…>, 12 years ago

Merged with the main branch

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