source: src/mlx/gui/gui.py@ 499:9ff36d6cab14

xplane
Last change on this file since 499:9ff36d6cab14 was 496:0dadad5a93b8, checked in by István Váradi <ivaradi@…>, 12 years ago

Merged the default branch

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