source: src/mlx/gui/gui.py@ 483:a1b49fbab4f0

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

Implemented the dialog window (re #190)

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