source: src/mlx/gui/gui.py@ 510:8590198176ae

Last change on this file since 510:8590198176ae was 491:3109e293df85, checked in by István Váradi <ivaradi@…>, 12 years ago

Added the ID of the ticket to the reply dialog (re #190)

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