source: src/mlx/gui/gui.py@ 482:79982e69f93b

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

Added menu option to send a bug report (re #190)

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