source: src/mlx/gui/gui.py@ 280:4d2a277c703b

Last change on this file since 280:4d2a277c703b was 276:b7b25febba1a, checked in by István Váradi <ivaradi@…>, 12 years ago

Using gettext to translate strings

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