source: src/mlx/gui/gui.py@ 744:39e770edda2e

Last change on this file since 744:39e770edda2e was 744:39e770edda2e, checked in by István Váradi <ivaradi@…>, 8 years ago

Added a callback to display the credentials window for logging in to the MAVA website (re #283).

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