source: src/mlx/gui/flight.py@ 836:df442f51e8de

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

Extracted the creation of the flight type combo box.

File size: 211.9 KB
Line 
1
2from mlx.gui.common import *
3import mlx.gui.cef as cef
4from mlx.gui.flightlist import ColumnDescriptor, FlightList, PendingFlightsWindow
5
6import mlx.const as const
7import mlx.fs as fs
8import mlx.acft as acft
9from mlx.flight import Flight
10from mlx.checks import PayloadChecker
11from mlx.gates import lhbpGates
12import mlx.util as util
13from mlx.pirep import PIREP
14from mlx.i18n import xstr, getLanguage
15from mlx.sound import startSound
16import mlx.web as web
17
18import datetime
19import time
20import os
21import tempfile
22import threading
23import re
24import webbrowser
25
26#-----------------------------------------------------------------------------
27
28## @package mlx.gui.flight
29#
30# The flight "wizard".
31#
32# This module implements the main tab of the application, the flight
33# wizard. The wizard consists of \ref Page "pages", that come one after the
34# other. As some pages might be skipped, the pages dynamically store the index
35# of the previous page so that going back to it is simpler. The \ref
36# Page.activate "activate" function is called before a page is first shown
37# during a flight. This function should initialize the page's controls and fill
38# it with initial data. When a page is left for the first time, its \ref
39# Page.finalize "finalize" function is called. It should set those controls
40# insensitive, that will not be available if the user comes back to this page.
41#
42# Each page has a title at the top displayed in inverted colors and a big
43# font. There is a help text below it centered, that shortly describes what is
44# expected on the page. There can be two help texts: one shown when the page is
45# first displayed during a flight, another shown when the user goes back to the
46# page. The main content area is below this, also centered. Finally, there are
47# some buttons at the bottom on the right. As some buttons occur very
48# frequently, there are functions to add them (\ref Page.addCancelFlightButton
49# "addCancelFlightButton", \ref Page.addPreviousButton "addPreviousButton" and
50# \ref Page.addNextButton "addNextButton".
51#
52# The \ref Wizard class is the main class to collect the pages. It also stores
53# some data passed from one page to another and provides properties to access
54# data items set via the wizard pages.
55
56#-----------------------------------------------------------------------------
57
58comboModel = gtk.ListStore(gobject.TYPE_STRING)
59comboModel.append(("N/A",))
60comboModel.append(("VECTORS",))
61
62#-----------------------------------------------------------------------------
63
64class Page(gtk.Alignment):
65 """A page in the flight wizard."""
66 def __init__(self, wizard, id, title, help, completedHelp = None):
67 """Construct the page."""
68 super(Page, self).__init__(xalign = 0.0, yalign = 0.0,
69 xscale = 1.0, yscale = 1.0)
70 self.set_padding(padding_top = 4, padding_bottom = 4,
71 padding_left = 12, padding_right = 12)
72
73 frame = gtk.Frame()
74 self.add(frame)
75
76 self._vbox = gtk.VBox()
77 self._vbox.set_homogeneous(False)
78 frame.add(self._vbox)
79
80 eventBox = gtk.EventBox()
81
82 alignment = gtk.Alignment(xalign = 0.0, xscale = 0.0)
83
84 titleLabel = gtk.Label(title)
85 titleLabel.modify_font(pango.FontDescription("bold 24"))
86 alignment.set_padding(padding_top = 4, padding_bottom = 4,
87 padding_left = 6, padding_right = 0)
88
89 alignment.add(titleLabel)
90 eventBox.add(alignment)
91
92 self._vbox.pack_start(eventBox, False, False, 0)
93
94 self._titleEventBox = eventBox
95 self._titleLabel = titleLabel
96
97 mainBox = gtk.VBox()
98
99 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
100 xscale = 1.0, yscale = 1.0)
101 alignment.set_padding(padding_top = 16, padding_bottom = 16,
102 padding_left = 16, padding_right = 16)
103 alignment.add(mainBox)
104 self._vbox.pack_start(alignment, True, True, 0)
105
106 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
107 xscale = 0.0, yscale = 0.0)
108 alignment.set_padding(padding_top = 0, padding_bottom = 16,
109 padding_left = 0, padding_right = 0)
110
111 self._help = help
112 self._completedHelp = completedHelp
113
114 if self._completedHelp is None or \
115 len(help.splitlines())>=len(completedHelp.splitlines()):
116 longerHelp = help
117 else:
118 longerHelp = completedHelp
119
120 self._helpLabel = gtk.Label(longerHelp)
121 # FIXME: should be a constant in common
122 self._helpLabel.set_justify(gtk.Justification.CENTER if pygobject
123 else gtk.JUSTIFY_CENTER)
124 self._helpLabel.set_use_markup(True)
125 alignment.add(self._helpLabel)
126 mainBox.pack_start(alignment, False, False, 0)
127
128 self._mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
129 xscale = 1.0, yscale = 1.0)
130 mainBox.pack_start(self._mainAlignment, True, True, 0)
131
132 buttonAlignment = gtk.Alignment(xalign = 1.0, xscale=0.0, yscale = 0.0)
133 buttonAlignment.set_padding(padding_top = 4, padding_bottom = 10,
134 padding_left = 16, padding_right = 16)
135
136 self._buttonBox = gtk.HBox()
137 self._buttonBox.set_homogeneous(False)
138 self._defaultButton = None
139 buttonAlignment.add(self._buttonBox)
140
141 self._vbox.pack_start(buttonAlignment, False, False, 0)
142
143 self._wizard = wizard
144 self._id = id
145 self._nextPageID = None
146
147 self._cancelFlightButton = None
148
149 self._completed = False
150 self._fromPage = None
151
152 @property
153 def id(self):
154 """Get the identifier of the page."""
155 return self._id
156
157 @property
158 def nextPageID(self):
159 """Get the identifier of the next page, if set."""
160 return self._nextPageID
161
162 @nextPageID.setter
163 def nextPageID(self, nextPageID):
164 """Set the identifier of the next page."""
165 self._nextPageID = nextPageID
166
167 def setMainWidget(self, widget):
168 """Set the given widget as the main one."""
169 self._mainAlignment.add(widget)
170
171 def addButton(self, label, default = False, sensitive = True,
172 tooltip = None, clicked = None, padding = 4,
173 clickedArg = None):
174 """Add a button with the given label.
175
176 Return the button object created."""
177 button = gtk.Button(label)
178 self._buttonBox.pack_start(button, False, False, padding)
179 button.set_use_underline(True)
180 if default:
181 button.set_can_default(True)
182 self._defaultButton = button
183 button.set_sensitive(sensitive)
184 if tooltip is not None:
185 button.set_tooltip_text(tooltip)
186 if clicked is not None:
187 if clickedArg is None:
188 button.connect("clicked", clicked)
189 else:
190 button.connect("clicked", clicked, clickedArg)
191 return button
192
193 def addCancelFlightButton(self):
194 """Add the 'Cancel flight' button to the page."""
195 self._cancelFlightButton = \
196 self.addButton(xstr("button_cancelFlight"),
197 sensitive = True,
198 tooltip = xstr("button_cancelFlight_tooltip"),
199 clicked = self._cancelFlight,
200 padding = 16)
201 return self._cancelFlightButton
202
203 def addPreviousButton(self, sensitive = True, clicked = None):
204 """Add the 'Next' button to the page."""
205 return self.addButton(xstr("button_previous"),
206 sensitive = sensitive,
207 tooltip = xstr("button_previous_tooltip"),
208 clicked = clicked)
209
210 def addNextButton(self, default = True, sensitive = True,
211 clicked = None):
212 """Add the 'Next' button to the page."""
213 return self.addButton(xstr("button_next"),
214 default = default,
215 sensitive = sensitive,
216 tooltip = xstr("button_next_tooltip"),
217 clicked = clicked)
218
219 def setStyle(self):
220 """Set the styles of some of the items on the page."""
221 if pygobject:
222 context = self.get_style_context()
223 color = context.get_background_color(gtk.StateFlags.SELECTED)
224 self._titleEventBox.modify_bg(0, color.to_color())
225 color = context.get_color(gtk.StateFlags.SELECTED)
226 self._titleLabel.modify_fg(0, color.to_color())
227 else:
228 style = self.rc_get_style()
229 self._titleEventBox.modify_bg(0, style.bg[3])
230 self._titleLabel.modify_fg(0, style.fg[3])
231
232 def initialize(self):
233 """Initialize the page.
234
235 It sets up the primary help, and calls the activate() function."""
236 self._helpLabel.set_markup(self._help)
237 self._helpLabel.set_sensitive(True)
238 self.activate()
239
240 def activate(self):
241 """Called when this page becomes active.
242
243 This default implementation does nothing."""
244 pass
245
246 def setHelp(self, help):
247 """Set the help string."""
248 self._help = help
249 if not self._completed:
250 self._helpLabel.set_markup(self._help)
251 self._helpLabel.set_sensitive(True)
252
253 def complete(self):
254 """Called when the page is completed.
255
256 It greys out/changes the help text and then calls finalize()."""
257 self.finalize()
258 if self._completedHelp is None:
259 self._helpLabel.set_sensitive(False)
260 else:
261 self._helpLabel.set_markup(self._completedHelp)
262 self._completed = True
263
264 def finalize(self):
265 """Called when the page is finalized."""
266 pass
267
268 def grabDefault(self):
269 """If the page has a default button, make it the default one."""
270 if self._defaultButton is not None:
271 self._defaultButton.grab_default()
272
273 def reset(self):
274 """Reset the page if the wizard is reset."""
275 self._completed = False
276 self._fromPage = None
277 if self._cancelFlightButton is not None:
278 self._cancelFlightButton.set_sensitive(True)
279
280 def goBack(self):
281 """Go to the page we were invoked from."""
282 assert self._fromPage is not None
283
284 self._wizard.setCurrentPage(self._fromPage, finalize = False)
285
286 def flightEnded(self):
287 """Called when the flight has ended.
288
289 This default implementation disables the cancel flight button."""
290 if self._cancelFlightButton is not None:
291 self._cancelFlightButton.set_sensitive(False)
292
293 def _cancelFlight(self, button):
294 """Called when the Cancel flight button is clicked."""
295 self._wizard.gui.cancelFlight()
296
297#-----------------------------------------------------------------------------
298
299class LoginPage(Page):
300 """The login page."""
301 def __init__(self, wizard):
302 """Construct the login page."""
303 super(LoginPage, self).__init__(wizard, "login",
304 xstr("login"), xstr("loginHelp"))
305
306 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
307 xscale = 0.0, yscale = 0.0)
308
309 table = gtk.Table(3, 2)
310 table.set_row_spacings(4)
311 table.set_col_spacings(32)
312 alignment.add(table)
313 self.setMainWidget(alignment)
314
315 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
316 xscale = 0.0, yscale = 0.0)
317 label = gtk.Label(xstr("label_pilotID"))
318 label.set_use_underline(True)
319 labelAlignment.add(label)
320 table.attach(labelAlignment, 0, 1, 0, 1)
321
322 self._pilotID = gtk.Entry()
323 self._pilotID.connect("changed", self._pilotIDChanged)
324 self._pilotID.set_tooltip_text(xstr("login_pilotID_tooltip"))
325 table.attach(self._pilotID, 1, 2, 0, 1)
326 label.set_mnemonic_widget(self._pilotID)
327
328 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
329 xscale = 0.0, yscale = 0.0)
330 label = gtk.Label(xstr("label_password"))
331 label.set_use_underline(True)
332 labelAlignment.add(label)
333 table.attach(labelAlignment, 0, 1, 1, 2)
334
335 self._password = gtk.Entry()
336 self._password.set_visibility(False)
337 self._password.connect("changed", self._setControls)
338 self._password.set_tooltip_text(xstr("login_password_tooltip"))
339 table.attach(self._password, 1, 2, 1, 2)
340 label.set_mnemonic_widget(self._password)
341
342 self._rememberButton = gtk.CheckButton(xstr("remember_password"))
343 self._rememberButton.set_use_underline(True)
344 self._rememberButton.set_tooltip_text(xstr("login_remember_tooltip"))
345 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
346
347 self.addButton(xstr("button_login_register"),
348 clicked = self._registerClicked,
349 tooltip = xstr("button_login_register_tooltip"))
350
351 self.addButton(xstr("button_offline"),
352 clicked = self._offlineClicked,
353 tooltip = xstr("button_offline_tooltip"))
354
355 self._loginButton = self.addButton(xstr("button_login"), default = True)
356 self._loginButton.connect("clicked", self._loginClicked)
357 self._loginButton.set_tooltip_text(xstr("login_button_tooltip"))
358
359
360 @property
361 def pilotID(self):
362 """Get the pilot ID, if given."""
363 return self._pilotID.get_text()
364
365 def activate(self):
366 """Activate the page."""
367 config = self._wizard.gui.config
368 self._pilotID.set_text(config.pilotID)
369 self._password.set_text(config.password)
370 self._rememberButton.set_active(config.rememberPassword)
371 self._setControls(None)
372
373 def _pilotIDChanged(self, entry):
374 """Called when the pilot ID has changed.
375
376 It sets the text to upper-case and calls _setControls to update other
377 stuff."""
378 entry.set_text(entry.get_text().upper())
379 self._setControls(entry)
380
381 def _setControls(self, entry = None):
382 """Set the sensitivity of the various controls.
383
384 The login button is sensitive only if both the pilot ID and the
385 password fields contain values.
386
387 The password field is sensitive only, if the entrance exam checkbox is
388 not selected.
389
390 The remember password checkbox is sensitive only, if the password field
391 contains text.
392
393 The entrance exam checkbox is sensitive only, if the pilot ID is not
394 empty."""
395 pilotID = self._pilotID.get_text()
396 password = self._password.get_text()
397 self._rememberButton.set_sensitive(password!="")
398 self._loginButton.set_sensitive(pilotID!="" and password!="")
399
400 def _registerClicked(self, button):
401 """Called when the Register button was clicked."""
402 self._wizard.jumpPage("register")
403
404 def _offlineClicked(self, button):
405 """Called when the offline button was clicked."""
406 print "mlx.flight.LoginPage: offline flight selected"
407 self._wizard.nextPage()
408
409 def _loginClicked(self, button):
410 """Called when the login button was clicked."""
411 print "mlx.flight.LoginPage: logging in"
412 self._wizard.login(self._handleLoginResult,
413 self._pilotID.get_text(),
414 self._password.get_text())
415
416 def _handleLoginResult(self, returned, result):
417 """Handle the login result."""
418 self._loginButton.set_sensitive(True)
419 if returned and result.loggedIn:
420 config = self._wizard.gui.config
421
422 config.pilotID = self._pilotID.get_text()
423
424 rememberPassword = self._rememberButton.get_active()
425 config.password = result.password if rememberPassword else ""
426
427 config.rememberPassword = rememberPassword
428
429 config.save()
430 if result.rank=="STU":
431 self._wizard.jumpPage("student")
432 else:
433 self._wizard.nextPage()
434
435#-----------------------------------------------------------------------------
436
437class FlightSelectionPage(Page):
438 """The page to select the flight."""
439 def __init__(self, wizard):
440 """Construct the flight selection page."""
441 help = xstr("flightsel_help")
442 completedHelp = xstr("flightsel_chelp")
443 super(FlightSelectionPage, self).__init__(wizard, "flightsel",
444 xstr("flightsel_title"),
445 help, completedHelp = completedHelp)
446
447 self._flightList = FlightList(popupMenuProducer =
448 self._createListPopupMenu,
449 widthRequest = 400)
450 self._flightList.connect("row-activated", self._rowActivated)
451 self._flightList.connect("selection-changed", self._selectionChanged)
452
453 self.setMainWidget(self._flightList)
454
455 self._pendingButton = self.addButton(xstr("flightsel_pending"),
456 sensitive = False,
457 clicked = self._pendingClicked,
458 tooltip = xstr("flightsel_pending_tooltip"))
459
460 self._saveButton = self.addButton(xstr("flightsel_save"),
461 sensitive = False,
462 clicked = self._saveClicked,
463 tooltip = xstr("flightsel_save_tooltip"))
464 self._saveDialog = None
465
466 self._refreshButton = self.addButton(xstr("flightsel_refresh"),
467 sensitive = True,
468 clicked = self._refreshClicked,
469 tooltip = xstr("flightsel_refresh_tooltip"))
470
471 self._loadButton = self.addButton(xstr("flightsel_load"),
472 sensitive = True,
473 tooltip = xstr("flightsel_load_tooltip"))
474 self._loadButton.connect("clicked", self._loadButtonClicked)
475 self._loadDialog = None
476
477 self._button = self.addNextButton(sensitive = False,
478 clicked = self._forwardClicked)
479
480 self._flights = []
481
482 self._pendingFlightsWindow = PendingFlightsWindow(self._wizard)
483 self._pendingFlightsWindowShown = False
484 self._pendingFlightsWindow.connect("delete-event",
485 self._deletePendingFlightsWindow)
486
487 def activate(self):
488 """Fill the flight list."""
489 self._setupHelp()
490 self._flightList.set_sensitive(True)
491 self._loadButton.set_sensitive(True)
492 self._refreshButton.set_sensitive(self._wizard.loggedIn)
493 self._buildFlights()
494
495 def finalize(self):
496 """Finalize the page."""
497 self._flightList.set_sensitive(False)
498 self._loadButton.set_sensitive(False)
499 self._refreshButton.set_sensitive(False)
500
501 def _setupHelp(self):
502 """Setup the help string"""
503 help = ""
504
505 if self._wizard.loggedIn:
506 numReported = len(self._wizard.loginResult.reportedFlights)
507 numRejected = len(self._wizard.loginResult.rejectedFlights)
508 if numReported==0 and numRejected==0:
509 help = xstr("flightsel_prehelp_nopending")
510 elif numReported>0 and numRejected==0:
511 help = xstr("flightsel_prehelp_rep_0rej") % (numReported,)
512 elif numReported==0 and numRejected>0:
513 help = xstr("flightsel_prehelp_0rep_rej") % (numRejected,)
514 else:
515 help = xstr("flightsel_prehelp_rep_rej") % \
516 (numReported, numRejected)
517
518 help += xstr("flightsel_help")
519
520 self.setHelp(help)
521
522 def _buildFlights(self):
523 """Rebuild the flights from the login result."""
524 self._flights = []
525 self._flightList.clear()
526 self._pendingFlightsWindow.clear()
527 loginResult = self._wizard.loginResult
528 if self._wizard.loggedIn:
529 for flight in loginResult.flights:
530 self._addFlight(flight)
531 for flight in loginResult.reportedFlights:
532 self._pendingFlightsWindow.addReportedFlight(flight)
533 for flight in loginResult.rejectedFlights:
534 self._pendingFlightsWindow.addRejectedFlight(flight)
535
536 self._updatePendingButton()
537
538 def _addFlight(self, flight):
539 """Add the given file to the list of flights."""
540 self._flights.append(flight)
541 self._flightList.addFlight(flight)
542
543 def _reflyFlight(self, flight):
544 """Refly the given flight."""
545 self._addFlight(flight)
546 self._updatePending()
547
548 def _updatePending(self):
549 """Update the stuff depending on the set of pending flights."""
550 self._setupHelp()
551 self._updatePendingButton()
552
553 def _pendingClicked(self, button):
554 """Called when the Pending flights button is clicked."""
555 self._pendingFlightsWindow.show_all()
556 self._pendingFlightsWindowShown = True
557 self._updateNextButton()
558
559 def _deletePendingFlightsWindow(self, window, event):
560 """Called when the pending flights window is closed."""
561 self._pendingFlightsWindow.hide()
562 self._pendingFlightsWindowShown = False
563 self._updateNextButton()
564 return True
565
566 def _saveClicked(self, button):
567 """Called when the Save flight button is clicked."""
568 self._saveSelected()
569
570 def _saveSelected(self):
571 """Save the selected flight."""
572 flight = self._getSelectedFlight()
573 date = flight.departureTime.date()
574 name = "%04d-%02d-%02d %s %s-%s.vaflight" % \
575 (date.year, date.month, date.day, flight.callsign,
576 flight.departureICAO, flight.arrivalICAO)
577
578 dialog = self._getSaveDialog()
579 dialog.set_current_name(name)
580 dialog.show_all()
581 response = dialog.run()
582 dialog.hide()
583
584 if response==RESPONSETYPE_OK:
585 fileName = text2unicode(dialog.get_filename())
586 print "Saving", fileName
587 try:
588 with open(fileName, "wt") as f:
589 flight.writeIntoFile(f)
590 except Exception, e:
591 print "Failed to save flight:", util.utf2unicode(str(e))
592 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
593 type = MESSAGETYPE_ERROR,
594 message_format =
595 xstr("flightsel_save_failed"))
596 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
597 dialog.set_title(WINDOW_TITLE_BASE)
598 secondary = xstr("flightsel_save_failed_sec")
599 dialog.format_secondary_markup(secondary)
600 dialog.run()
601 dialog.hide()
602
603 def _refreshClicked(self, button):
604 """Called when the refresh button is clicked."""
605 self._wizard.reloadFlights(self._refreshCallback)
606
607 def _refreshCallback(self, returned, result):
608 """Callback for the refresh."""
609 if returned:
610 self._setupHelp()
611 if result.loggedIn:
612 self._buildFlights()
613
614 def _selectionChanged(self, flightList, indexes):
615 """Called when the selection is changed."""
616 self._saveButton.set_sensitive(len(indexes)==1)
617 self._updateNextButton()
618
619 def _updatePendingButton(self):
620 """Update the senstivity of the Pending button."""
621 self._pendingButton.set_sensitive(self._pendingFlightsWindow.hasFlights)
622
623 def _updateNextButton(self):
624 """Update the sensitivity of the Next button."""
625 sensitive = len(self._flightList.selectedIndexes)==1 and \
626 not self._pendingFlightsWindowShown
627 self._button.set_sensitive(sensitive)
628
629 def _loadButtonClicked(self, loadButton):
630 """Called when the load a flight button is clicked."""
631 dialog = self._getLoadDialog()
632 dialog.show_all()
633 response = dialog.run()
634 dialog.hide()
635
636 if response==RESPONSETYPE_OK:
637 fileName = text2unicode(dialog.get_filename())
638 print "Loading", fileName
639 bookedFlight = web.BookedFlight()
640 try:
641 with open(fileName, "rt") as f:
642 bookedFlight.readFromFile(f)
643 self._addFlight(bookedFlight)
644 except Exception, e:
645 print "Failed to load flight:", util.utf2unicode(str(e))
646 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
647 type = MESSAGETYPE_ERROR,
648 message_format =
649 xstr("flightsel_load_failed"))
650 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
651 dialog.set_title(WINDOW_TITLE_BASE)
652 secondary = xstr("flightsel_load_failed_sec")
653 dialog.format_secondary_markup(secondary)
654 dialog.run()
655 dialog.hide()
656
657 def _forwardClicked(self, button):
658 """Called when the forward button was clicked."""
659 if self._completed:
660 self._wizard.jumpPage(self._nextID, finalize = False)
661 else:
662 self._flightSelected()
663
664 def _rowActivated(self, flightList, index):
665 """Called when a row is activated."""
666 if not self._completed:
667 self._flightSelected()
668
669 def _flightSelected(self):
670 """Called when a flight has been selected."""
671 flight = self._getSelectedFlight()
672 self._wizard._bookedFlight = flight
673 self._wizard.gui.enableFlightInfo(flight.aircraftType)
674
675 self._updateDepartureGate()
676
677 def _getSelectedFlight(self):
678 """Get the currently selected flight."""
679 indexes = self._flightList.selectedIndexes
680 assert(len(indexes)==1)
681 return self._flights[indexes[0]]
682
683 def _updateDepartureGate(self):
684 """Update the departure gate for the booked flight."""
685 flight = self._wizard._bookedFlight
686 if self._wizard.gui.config.onlineGateSystem and \
687 self._wizard.loggedIn and not self._wizard.entranceExam:
688 if flight.departureICAO=="LHBP":
689 self._wizard.getFleet(self._fleetRetrieved)
690 else:
691 self._wizard.updatePlane(self._planeUpdated,
692 flight.tailNumber,
693 const.PLANE_AWAY)
694 else:
695 self._nextID = "connect"
696 self._wizard.jumpPage("connect")
697
698 def _fleetRetrieved(self, fleet):
699 """Called when the fleet has been retrieved."""
700 if fleet is None:
701 self._nextID = "connect"
702 self._wizard.jumpPage("connect")
703 else:
704 plane = fleet[self._wizard._bookedFlight.tailNumber]
705 if plane is None:
706 self._nextID = "connect"
707 self._wizard.jumpPage("connect")
708 elif plane.gateNumber is not None and \
709 not fleet.isGateConflicting(plane):
710 self._wizard._departureGate = plane.gateNumber
711 self._nextID = "connect"
712 self._wizard.jumpPage("connect")
713 else:
714 self._nextID = "gatesel"
715 self._wizard.jumpPage("gatesel")
716
717 def _planeUpdated(self, success):
718 """Callback for the plane updating."""
719 self._nextID = "connect"
720 self._wizard.jumpPage("connect")
721
722 def _getSaveDialog(self):
723 """Get the dialog to load a flight file."""
724 if self._saveDialog is not None:
725 return self._saveDialog
726
727 gui = self._wizard.gui
728 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
729 xstr("flightsel_save_title"),
730 action = FILE_CHOOSER_ACTION_SAVE,
731 buttons = (gtk.STOCK_CANCEL,
732 RESPONSETYPE_CANCEL,
733 gtk.STOCK_OK, RESPONSETYPE_OK),
734 parent = gui.mainWindow)
735 dialog.set_modal(True)
736 dialog.set_do_overwrite_confirmation(True)
737
738 filter = gtk.FileFilter()
739 filter.set_name(xstr("flightsel_filter_flights"))
740 filter.add_pattern("*.vaflight")
741 dialog.add_filter(filter)
742
743 filter = gtk.FileFilter()
744 filter.set_name(xstr("file_filter_all"))
745 filter.add_pattern("*.*")
746 dialog.add_filter(filter)
747
748 self._saveDialog = dialog
749
750 return dialog
751
752 def _getLoadDialog(self):
753 """Get the dialog to load a flight file."""
754 if self._loadDialog is not None:
755 return self._loadDialog
756
757 gui = self._wizard.gui
758 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
759 xstr("flightsel_load_title"),
760 action = FILE_CHOOSER_ACTION_OPEN,
761 buttons = (gtk.STOCK_CANCEL,
762 RESPONSETYPE_CANCEL,
763 gtk.STOCK_OK, RESPONSETYPE_OK),
764 parent = gui.mainWindow)
765 dialog.set_modal(True)
766
767 filter = gtk.FileFilter()
768 filter.set_name(xstr("flightsel_filter_flights"))
769 filter.add_pattern("*.vaflight")
770 dialog.add_filter(filter)
771
772 filter = gtk.FileFilter()
773 filter.set_name(xstr("file_filter_all"))
774 filter.add_pattern("*.*")
775 dialog.add_filter(filter)
776
777 self._loadDialog = dialog
778
779 return dialog
780
781 def _createListPopupMenu(self):
782 """Get the flight list popup menu."""
783 menu = gtk.Menu()
784
785 menuItem = gtk.MenuItem()
786 menuItem.set_label(xstr("flightsel_popup_select"))
787 menuItem.set_use_underline(True)
788 menuItem.connect("activate", self._popupSelect)
789 menuItem.show()
790
791 menu.append(menuItem)
792
793 menuItem = gtk.MenuItem()
794 menuItem.set_label(xstr("flightsel_popup_save"))
795 menuItem.set_use_underline(True)
796 menuItem.connect("activate", self._popupSave)
797 menuItem.show()
798
799 menu.append(menuItem)
800
801 return menu
802
803 def _popupSelect(self, menuItem):
804 """Called when the Select menu item is activated in the popup menu."""
805 if not self._completed:
806 self._flightSelected()
807
808 def _popupSave(self, menuItem):
809 """Called when the Save menu item is activated in the popup menu."""
810 if not self._completed:
811 self._saveSelected()
812
813#-----------------------------------------------------------------------------
814
815class GateSelectionPage(Page):
816 """Page to select a free gate at LHBP.
817 This page should be displayed only if we have fleet information!."""
818 def __init__(self, wizard):
819 """Construct the gate selection page."""
820 super(GateSelectionPage, self).__init__(wizard, "gatesel",
821 xstr("gatesel_title"),
822 xstr("gatesel_help"))
823
824 self._listStore = gtk.ListStore(str)
825 self._gateList = gtk.TreeView(self._listStore)
826 column = gtk.TreeViewColumn(None, gtk.CellRendererText(),
827 text = 0)
828 column.set_expand(True)
829 self._gateList.append_column(column)
830 self._gateList.set_headers_visible(False)
831 self._gateList.connect("row-activated", self._rowActivated)
832
833 gateSelection = self._gateList.get_selection()
834 gateSelection.connect("changed", self._selectionChanged)
835
836 scrolledWindow = gtk.ScrolledWindow()
837 scrolledWindow.add(self._gateList)
838 scrolledWindow.set_size_request(50, -1)
839 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
840 else gtk.POLICY_AUTOMATIC,
841 gtk.PolicyType.AUTOMATIC if pygobject
842 else gtk.POLICY_AUTOMATIC)
843 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
844 else gtk.SHADOW_IN)
845
846 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
847 alignment.add(scrolledWindow)
848
849 self.setMainWidget(alignment)
850
851 self.addCancelFlightButton()
852
853 self.addPreviousButton(clicked = self._backClicked)
854
855 self._button = self.addNextButton(sensitive = False,
856 clicked = self._forwardClicked)
857
858 def activate(self):
859 """Fill the gate list."""
860 self._listStore.clear()
861 self._gateList.set_sensitive(True)
862 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
863 for gate in lhbpGates.gates:
864 if gate.isAvailable(lhbpGates, occupiedGateNumbers):
865 self._listStore.append([gate.number])
866
867 def finalize(self):
868 """Finalize the page."""
869 self._gateList.set_sensitive(False)
870
871 def _selectionChanged(self, selection):
872 """Called when the selection is changed."""
873 self._button.set_sensitive(selection.count_selected_rows()==1)
874
875 def _backClicked(self, button):
876 """Called when the Back button is pressed."""
877 self.goBack()
878
879 def _forwardClicked(self, button):
880 """Called when the forward button is clicked."""
881 if not self._completed:
882 self._gateSelected()
883 else:
884 self._wizard.jumpPage("connect")
885
886 def _rowActivated(self, flightList, path, column):
887 """Called when a row is activated."""
888 if not self._completed:
889 self._gateSelected()
890
891 def _gateSelected(self):
892 """Called when a gate has been selected."""
893 selection = self._gateList.get_selection()
894 (listStore, iter) = selection.get_selected()
895 (gateNumber,) = listStore.get(iter, 0)
896
897 self._wizard._departureGate = gateNumber
898
899 self._wizard.updatePlane(self._planeUpdated,
900 self._wizard._bookedFlight.tailNumber,
901 const.PLANE_HOME, gateNumber)
902
903 def _planeUpdated(self, success):
904 """Callback for the plane updating call."""
905 if success is None or success:
906 self._wizard.jumpPage("connect")
907 else:
908 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
909 type = MESSAGETYPE_ERROR,
910 message_format = xstr("gatesel_conflict"))
911 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
912 dialog.set_title(WINDOW_TITLE_BASE)
913 dialog.format_secondary_markup(xstr("gatesel_conflict_sec"))
914 dialog.run()
915 dialog.hide()
916
917 self._wizard.getFleet(self._fleetRetrieved)
918
919 def _fleetRetrieved(self, fleet):
920 """Called when the fleet has been retrieved."""
921 if fleet is None:
922 self._wizard.jumpPage("connect")
923 else:
924 self.activate()
925
926#-----------------------------------------------------------------------------
927
928class RegisterPage(Page):
929 """A page to enter the registration data."""
930
931 # The minimal year of birth
932 _minYearOfBirth = 1900
933
934 # The maximal year of birth
935 _maxYearOfBirth = datetime.date.today().year
936
937 # The regular expression to check the e-mail address with
938 _emailAddressRE = re.compile("[^@]+@[^@]+\.[^@]+")
939
940 def __init__(self, wizard):
941 """Construct the registration page."""
942 super(RegisterPage, self).__init__(wizard, "register",
943 xstr("register_title"),
944 xstr("register_help"))
945
946 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
947 xscale = 0.0, yscale = 0.0)
948
949 table = gtk.Table(12, 3)
950 table.set_row_spacings(4)
951 table.set_col_spacings(32)
952 alignment.add(table)
953 self.setMainWidget(alignment)
954
955 row = 0
956
957 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
958 xscale = 0.0, yscale = 0.0)
959 label = gtk.Label(xstr("register_name1"))
960 label.set_use_underline(True)
961 labelAlignment.add(label)
962 table.attach(labelAlignment, 0, 1, row, row+1)
963
964 self._name1 = gtk.Entry()
965 self._name1.connect("changed", self._updateButtons)
966 self._name1.set_tooltip_text(xstr("register_name1_tooltip"))
967 self._name1.set_width_chars(15)
968 table.attach(self._name1, 1, 2, row, row+1)
969 label.set_mnemonic_widget(self._name1)
970
971 row += 1
972
973 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
974 xscale = 0.0, yscale = 0.0)
975 label = gtk.Label(xstr("register_name2"))
976 label.set_use_underline(True)
977 labelAlignment.add(label)
978 table.attach(labelAlignment, 0, 1, row, row+1)
979
980 self._name2 = gtk.Entry()
981 self._name2.connect("changed", self._updateButtons)
982 self._name2.set_tooltip_text(xstr("register_name2_tooltip"))
983 self._name2.set_width_chars(15)
984 table.attach(self._name2, 1, 2, row, row+1)
985 label.set_mnemonic_widget(self._name2)
986
987 row += 1
988
989 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
990 xscale = 0.0, yscale = 0.0)
991 label = gtk.Label(xstr("register_year_of_birth"))
992 label.set_use_underline(True)
993 labelAlignment.add(label)
994 table.attach(labelAlignment, 0, 1, row, row+1)
995
996 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
997 xscale = 0.0, yscale = 0.0)
998
999 self._yearOfBirth = gtk.SpinButton()
1000 self._yearOfBirth.set_increments(step = 1, page = 10)
1001 self._yearOfBirth.set_range(min = RegisterPage._minYearOfBirth,
1002 max = RegisterPage._maxYearOfBirth)
1003 self._yearOfBirth.set_numeric(True)
1004 self._yearOfBirth.set_tooltip_text(xstr("register_year_of_birth_tooltip"))
1005 self._yearOfBirth.set_width_chars(5)
1006 self._yearOfBirth.connect("changed", self._updateButtons)
1007 self._yearOfBirth.connect("value-changed", self._updateButtons)
1008 alignment.add(self._yearOfBirth)
1009 table.attach(alignment, 1, 2, row, row+1)
1010 label.set_mnemonic_widget(self._yearOfBirth)
1011
1012 row += 1
1013
1014 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1015 xscale = 0.0, yscale = 0.0)
1016 label = gtk.Label(xstr("register_email"))
1017 label.set_use_underline(True)
1018 labelAlignment.add(label)
1019 table.attach(labelAlignment, 0, 1, row, row+1)
1020
1021 self._emailAddress = gtk.Entry()
1022 self._emailAddress.connect("changed", self._updateButtons)
1023 self._emailAddress.set_tooltip_text(xstr("register_email_tooltip"))
1024 table.attach(self._emailAddress, 1, 2, row, row+1)
1025 label.set_mnemonic_widget(self._emailAddress)
1026
1027 self._emailAddressPublic = gtk.CheckButton(xstr("register_email_public"))
1028 self._emailAddressPublic.set_use_underline(True)
1029 self._emailAddressPublic.set_tooltip_text(xstr("register_email_public_tooltip"))
1030 table.attach(self._emailAddressPublic, 2, 3, row, row+1)
1031
1032 row += 1
1033
1034 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1035 xscale = 0.0, yscale = 0.0)
1036 label = gtk.Label(xstr("register_vatsim_id"))
1037 label.set_use_underline(True)
1038 labelAlignment.add(label)
1039 table.attach(labelAlignment, 0, 1, row, row+1)
1040
1041 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1042 xscale = 0.0, yscale = 0.0)
1043 self._vatsimID = IntegerEntry()
1044 self._vatsimID.connect("changed", self._updateButtons)
1045 self._vatsimID.set_tooltip_text(xstr("register_vatsim_id_tooltip"))
1046 self._vatsimID.set_width_chars(7)
1047 alignment.add(self._vatsimID)
1048 table.attach(alignment, 1, 2, row, row+1)
1049 label.set_mnemonic_widget(self._vatsimID)
1050
1051 row += 1
1052
1053 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1054 xscale = 0.0, yscale = 0.0)
1055 label = gtk.Label(xstr("register_ivao_id"))
1056 label.set_use_underline(True)
1057 labelAlignment.add(label)
1058 table.attach(labelAlignment, 0, 1, row, row+1)
1059
1060 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1061 xscale = 0.0, yscale = 0.0)
1062 self._ivaoID = IntegerEntry()
1063 self._ivaoID.connect("changed", self._updateButtons)
1064 self._ivaoID.set_tooltip_text(xstr("register_ivao_id_tooltip"))
1065 self._ivaoID.set_width_chars(7)
1066 alignment.add(self._ivaoID)
1067 table.attach(alignment, 1, 2, row, row+1)
1068 label.set_mnemonic_widget(self._ivaoID)
1069
1070 row += 1
1071
1072 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1073 xscale = 0.0, yscale = 0.0)
1074 label = gtk.Label(xstr("register_phone_num"))
1075 label.set_use_underline(True)
1076 label.set_use_markup(True)
1077 labelAlignment.add(label)
1078 table.attach(labelAlignment, 0, 1, row, row+1)
1079
1080 self._phoneNumber = gtk.Entry()
1081 self._phoneNumber.set_tooltip_text(xstr("register_phone_num_tooltip"))
1082 table.attach(self._phoneNumber, 1, 2, row, row+1)
1083 label.set_mnemonic_widget(self._phoneNumber)
1084
1085 row += 1
1086
1087 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1088 xscale = 0.0, yscale = 0.0)
1089 label = gtk.Label(xstr("register_nationality"))
1090 label.set_use_underline(True)
1091 label.set_use_markup(True)
1092 labelAlignment.add(label)
1093 table.attach(labelAlignment, 0, 1, row, row+1)
1094
1095
1096 self._nationality = gtk.Entry()
1097 self._nationality.set_tooltip_text(xstr("register_nationality_tooltip"))
1098 table.attach(self._nationality, 1, 2, row, row+1)
1099 label.set_mnemonic_widget(self._nationality)
1100
1101 placeholder = gtk.Label()
1102 placeholder.set_text(xstr("register_password_mismatch"))
1103 placeholder.set_use_markup(True)
1104 placeholder.set_child_visible(False)
1105 placeholder.hide()
1106 table.attach(placeholder, 2, 3, row, row+1)
1107
1108 row += 1
1109
1110 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1111 xscale = 0.0, yscale = 0.0)
1112 label = gtk.Label(xstr("register_password"))
1113 label.set_use_underline(True)
1114 labelAlignment.add(label)
1115 table.attach(labelAlignment, 0, 1, row, row+1)
1116
1117 self._password = gtk.Entry()
1118 self._password.set_visibility(False)
1119 self._password.connect("changed", self._updateButtons)
1120 self._password.set_tooltip_text(xstr("register_password_tooltip"))
1121 table.attach(self._password, 1, 2, row, row+1)
1122 label.set_mnemonic_widget(self._password)
1123
1124 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1125 xscale = 0.0, yscale = 0.0)
1126 self._passwordStatus = gtk.Label()
1127 alignment.add(self._passwordStatus)
1128 table.attach(alignment, 2, 3, row, row+1)
1129
1130 row += 1
1131
1132 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1133 xscale = 0.0, yscale = 0.0)
1134 label = gtk.Label(xstr("register_password2"))
1135 label.set_use_underline(True)
1136 labelAlignment.add(label)
1137 table.attach(labelAlignment, 0, 1, row, row+1)
1138
1139 self._password2 = gtk.Entry()
1140 self._password2.set_visibility(False)
1141 self._password2.connect("changed", self._updateButtons)
1142 self._password2.set_tooltip_text(xstr("register_password2_tooltip"))
1143 table.attach(self._password2, 1, 2, row, row+1)
1144 label.set_mnemonic_widget(self._password2)
1145
1146 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1147 xscale = 0.0, yscale = 0.0)
1148 self._password2Status = gtk.Label()
1149 alignment.add(self._password2Status)
1150 table.attach(alignment, 2, 3, row, row+1)
1151
1152 row += 1
1153
1154 self._rememberButton = gtk.CheckButton(xstr("remember_password"))
1155 self._rememberButton.set_use_underline(True)
1156 self._rememberButton.set_tooltip_text(xstr("login_remember_tooltip"))
1157 table.attach(self._rememberButton, 1, 2, row, row+1)
1158
1159 cancelButton = \
1160 self.addButton(xstr("button_cancel"))
1161 cancelButton.connect("clicked", self._cancelClicked)
1162
1163 self._registerButton = \
1164 self.addButton(xstr("button_register"), default = True,
1165 tooltip = xstr("button_register_tooltip"))
1166 self._registerButton.connect("clicked", self._registerClicked)
1167
1168 self._updateButtons()
1169
1170 @property
1171 def name1(self):
1172 """Get the first name component entered."""
1173 return self._name1.get_text()
1174
1175 @property
1176 def name2(self):
1177 """Get the second name component entered."""
1178 return self._name2.get_text()
1179
1180 @property
1181 def yearOfBirth(self):
1182 """Get the year of birth."""
1183 yearOfBirthText = self._yearOfBirth.get_text()
1184 return int(yearOfBirthText) if yearOfBirthText else 0
1185
1186 @property
1187 def emailAddress(self):
1188 """Get the e-mail address."""
1189 return self._emailAddress.get_text()
1190
1191 @property
1192 def emailAddressPublic(self):
1193 """Get the whether the e-mail address is public."""
1194 return self._emailAddressPublic.get_active()
1195
1196 @property
1197 def vatsimID(self):
1198 """Get the VATSIM ID."""
1199 return self._vatsimID.get_int()
1200
1201 @property
1202 def ivaoID(self):
1203 """Get the IVAO ID."""
1204 return self._ivaoID.get_int()
1205
1206 @property
1207 def phoneNumber(self):
1208 """Get the phone number."""
1209 return self._phoneNumber.get_text()
1210
1211 @property
1212 def nationality(self):
1213 """Get the nationality."""
1214 return self._nationality.get_text()
1215
1216 @property
1217 def password(self):
1218 """Get the password."""
1219 return self._password.get_text()
1220
1221 @property
1222 def rememberPassword(self):
1223 """Get whether the password should be remembered."""
1224 return self._rememberButton.get_active()
1225
1226 def activate(self):
1227 """Setup the route from the booked flight."""
1228 self._yearOfBirth.set_value(0)
1229 self._yearOfBirth.set_text("")
1230 self._updateButtons()
1231
1232 def _updateButtons(self, widget = None):
1233 """Update the sensitive state of the buttons"""
1234 yearOfBirth = self.yearOfBirth
1235
1236 emailAddress = self.emailAddress
1237 emailAddressMatch = RegisterPage._emailAddressRE.match(emailAddress)
1238
1239 vatsimID = self.vatsimID
1240 ivaoID = self.ivaoID
1241
1242 password = self.password
1243 password2 = self._password2.get_text()
1244 if not password:
1245 self._passwordStatus.set_text("")
1246 elif len(password)<5:
1247 self._passwordStatus.set_text(xstr("register_password_too_short"))
1248 else:
1249 self._passwordStatus.set_text(xstr("register_password_ok"))
1250 self._passwordStatus.set_use_markup(True)
1251
1252 if len(password)<5 or not password2:
1253 self._password2Status.set_text("")
1254 elif password!=password2:
1255 self._password2Status.set_text(xstr("register_password_mismatch"))
1256 else:
1257 self._password2Status.set_text(xstr("register_password_ok"))
1258 self._password2Status.set_use_markup(True)
1259
1260 sensitive = \
1261 len(self.name1)>0 and len(self.name2)>0 and \
1262 yearOfBirth>=RegisterPage._minYearOfBirth and \
1263 yearOfBirth<=RegisterPage._maxYearOfBirth and \
1264 emailAddressMatch is not None and \
1265 (vatsimID>=800000 or ivaoID>=100000) and \
1266 len(password)>=5 and password==password2
1267
1268 self._registerButton.set_sensitive(sensitive)
1269
1270 def _cancelClicked(self, button):
1271 """Called when the Cancel button is clicked."""
1272 self.goBack()
1273
1274 def _registerClicked(self, button):
1275 """Called when the Register button is clicked."""
1276 nameOrder = xstr("register_nameorder")
1277
1278 if nameOrder=="eastern":
1279 surName = self.name1
1280 firstName = self.name2
1281 else:
1282 surName = self.name2
1283 firstName = self.name1
1284
1285 nationality = self.nationality.lower()
1286
1287 if getLanguage().lower()=="hu" or nationality.find("hung")!=-1 or \
1288 nationality.find("magyar")!=-1:
1289 requestedNameOrder = "eastern"
1290 else:
1291 requestedNameOrder = "western"
1292
1293 registrationData = web.Registration(surName, firstName,
1294 requestedNameOrder,
1295 self.yearOfBirth,
1296 self.emailAddress,
1297 self.emailAddressPublic,
1298 self.vatsimID, self.ivaoID,
1299 self.phoneNumber, self.nationality,
1300 self.password)
1301 print "Registering with data:"
1302 print " name:", self.name1, self.name2, registrationData.firstName, registrationData.surName, requestedNameOrder
1303 print " yearOfBirth:", self.yearOfBirth, registrationData.yearOfBirth
1304 print " emailAddress:", self.emailAddress, registrationData.emailAddress
1305 print " emailAddressPublic:", self.emailAddressPublic, registrationData.emailAddressPublic
1306 print " vatsimID:", self.vatsimID, registrationData.vatsimID
1307 print " ivaoID:", self.ivaoID, registrationData.ivaoID
1308 print " phoneNumber:", self.phoneNumber, registrationData.phoneNumber
1309 print " nationality:", self.nationality, registrationData.nationality
1310
1311 gui = self._wizard.gui
1312 gui.beginBusy(xstr("register_busy"))
1313 gui.webHandler.register(self._registerResultCallback, registrationData)
1314
1315 def _registerResultCallback(self, returned, result):
1316 """Called when the registration result is available."""
1317 gobject.idle_add(self._handleRegisterResult, returned, result)
1318
1319 def _handleRegisterResult(self, returned, result):
1320 """Handle the registration result."""
1321 gui = self._wizard.gui
1322
1323 gui.endBusy()
1324
1325 print "Registration result:"
1326 print " returned:", returned
1327 if returned:
1328 print " registered:", result.registered
1329 if result.registered:
1330 print " pilotID", result.pilotID
1331 print " loggedIn", result.loggedIn
1332 print " emailAlreadyRegistered:", result.emailAlreadyRegistered
1333 print " invalidData:", result.invalidData
1334
1335 registrationOK = returned and result.registered
1336
1337 message = xstr("register_ok") if registrationOK \
1338 else xstr("register_failed")
1339 secondaryMessage = None
1340 if registrationOK:
1341 if result.loggedIn:
1342 secondaryMessage = xstr("register_info") % (result.pilotID,)
1343 else:
1344 secondaryMessage = xstr("register_nologin") % (result.pilotID,)
1345 messageType = MESSAGETYPE_INFO
1346
1347 config = gui.config
1348 config.pilotID = result.pilotID
1349 config.rememberPassword = self.rememberPassword
1350 if config.rememberPassword:
1351 config.password = self.password
1352 else:
1353 config.password = ""
1354
1355 config.save()
1356 elif returned and result.emailAlreadyRegistered:
1357 secondaryMessage = xstr("register_email_already")
1358 messageType = MESSAGETYPE_ERROR
1359 elif returned and result.invalidData:
1360 secondaryMessage = xstr("register_invalid_data")
1361 messageType = MESSAGETYPE_ERROR
1362 else:
1363 secondaryMessage = xstr("register_error")
1364 messageType = MESSAGETYPE_ERROR
1365
1366 dialog = gtk.MessageDialog(parent = gui.mainWindow,
1367 type = messageType,
1368 message_format = message)
1369 dialog.set_title(WINDOW_TITLE_BASE + " - " +
1370 xstr("register_result_title"))
1371 dialog.format_secondary_markup(secondaryMessage)
1372
1373 dialog.add_button(xstr("button_ok"), 0)
1374
1375 dialog.run()
1376 dialog.hide()
1377
1378 if registrationOK:
1379 if result.loggedIn:
1380 self._wizard._loginResult = result
1381 self._wizard.nextPage()
1382 else:
1383 self._wizard.jumpPage("login")
1384
1385#-----------------------------------------------------------------------------
1386
1387class StudentPage(Page):
1388 """A page displayed to students after logging in."""
1389 _entryExamStatusQueryInterval = 60*1000
1390
1391 def __init__(self, wizard):
1392 """Construct the student page."""
1393 super(StudentPage, self).__init__(wizard, "student",
1394 xstr("student_title"),
1395 xstr("student_help"))
1396
1397
1398 self._getEntryExamStatusCancelled = False
1399
1400 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1401 xscale = 0.5, yscale = 0.0)
1402
1403 table = gtk.Table(6, 4)
1404 table.set_row_spacings(4)
1405 table.set_col_spacings(0)
1406 table.set_homogeneous(False)
1407 alignment.add(table)
1408 self.setMainWidget(alignment)
1409
1410 row = 0
1411
1412 labelAlignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
1413 xscale=0.0, yscale = 0.0)
1414 label = gtk.Label(xstr("student_entry_exam_status"))
1415 label.set_alignment(0.0, 0.5)
1416 labelAlignment.add(label)
1417 labelAlignment.resize_children()
1418 table.attach(labelAlignment, 0, 1, row, row + 1, xoptions = FILL)
1419
1420 alignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
1421 xscale=1.0, yscale = 0.0)
1422 self._entryExamStatus = gtk.Label()
1423 self._entryExamStatus.set_use_markup(True)
1424 self._entryExamStatus.set_alignment(0.0, 0.5)
1425 alignment.add(self._entryExamStatus)
1426 alignment.resize_children()
1427 table.attach(alignment, 1, 4, row, row + 1)
1428
1429 row += 1
1430
1431 buttonAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
1432 button = self._entryExamButton = gtk.Button(xstr("student_entry_exam"))
1433 button.set_use_underline(True)
1434 button.connect("clicked", self._entryExamClicked)
1435 button.set_tooltip_text(xstr("student_entry_exam_tooltip"))
1436
1437 buttonAlignment.add(button)
1438 table.attach(buttonAlignment, 0, 4, row, row + 1, xoptions = FILL,
1439 ypadding = 4)
1440
1441 row += 3
1442
1443 labelAlignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
1444 xscale=0.0, yscale = 0.0)
1445 label = gtk.Label(xstr("student_check_flight_status"))
1446 labelAlignment.add(label)
1447 table.attach(labelAlignment, 0, 1, row, row + 1, xoptions = FILL)
1448
1449 alignment = gtk.Alignment(xalign=0.0, yalign = 0.5,
1450 xscale=1.0, yscale = 0.0)
1451 self._checkFlightStatus = gtk.Label()
1452 self._checkFlightStatus.set_use_markup(True)
1453 self._checkFlightStatus.set_alignment(0.0, 0.5)
1454 alignment.add(self._checkFlightStatus)
1455 table.attach(alignment, 1, 4, row, row + 1)
1456
1457 row += 1
1458
1459 alignment = gtk.Alignment(xalign=0.0, xscale=1.0)
1460
1461 hbox = gtk.HBox()
1462 hbox.set_homogeneous(False)
1463 hbox.set_spacing(0)
1464
1465 aircraftTypesModel = gtk.ListStore(str, int)
1466 for aircraftType in web.BookedFlight.checkFlightTypes:
1467 aircraftTypesModel.append([aircraftNames[aircraftType],
1468 aircraftType])
1469
1470 aircraftTypeAlignment = gtk.Alignment(xalign = 0.0, xscale = 1.0)
1471
1472 self._aircraftType = gtk.ComboBox(model = aircraftTypesModel)
1473 renderer = gtk.CellRendererText()
1474 self._aircraftType.pack_start(renderer, True)
1475 self._aircraftType.add_attribute(renderer, "text", 0)
1476 self._aircraftType.set_tooltip_text(xstr("student_check_flight_type_tooltip"))
1477 self._aircraftType.set_active(0)
1478
1479 aircraftTypeAlignment.add(self._aircraftType)
1480
1481 hbox.pack_start(aircraftTypeAlignment, False, False, 0)
1482
1483 buttonAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
1484 button = self._checkFlightButton = gtk.Button(xstr("student_check_flight"))
1485 button.set_use_underline(True)
1486 button.connect("clicked", self._checkFlightClicked)
1487 button.set_tooltip_text(xstr("student_check_flight_tooltip"))
1488
1489 hbox.pack_start(button, True, True, 0)
1490
1491 alignment.add(hbox)
1492 table.attach(alignment, 0, 4, row, row + 1, xoptions = FILL)
1493
1494 @property
1495 def aircraftType(self):
1496 """Get the type of the aircraft used to perform the check flight."""
1497 index = self._aircraftType.get_active()
1498 return self._aircraftType.get_model()[index][1]
1499
1500 def activate(self):
1501 """Activate the student page."""
1502 print "StudentPage.activate"
1503 self._getEntryExamStatusCancelled = False
1504
1505 loginResult = self._wizard.loginResult
1506 self._entryExamLink = loginResult.entryExamLink
1507
1508 self._updateEntryExamStatus(loginResult.entryExamPassed)
1509 self._getEntryExamStatus()
1510
1511 # FIXME: call with real value
1512 self._updateCheckFlightStatus(self._wizard.loginResult.checkFlightStatus)
1513
1514 def finalize(self):
1515 """Finalize the page."""
1516 print "StudentPage.finalize"
1517 self._getEntryExamStatusCancelled = True
1518
1519 def _entryExamClicked(self, button):
1520 """Called when the entry exam button is clicked."""
1521 webbrowser.open(self._entryExamLink)
1522
1523 def _getEntryExamStatus(self):
1524 """Initiate the query of the entry exam status after the interval."""
1525 if not self._getEntryExamStatusCancelled:
1526 gobject.timeout_add(StudentPage._entryExamStatusQueryInterval,
1527 lambda: self._wizard.gui.webHandler. \
1528 getEntryExamStatus(self._entryExamStatusCallback))
1529
1530 def _entryExamStatusCallback(self, returned, result):
1531 """Called when the entry exam status is available."""
1532 gobject.idle_add(self._handleEntryExamStatus, returned, result)
1533
1534 def _handleEntryExamStatus(self, returned, result):
1535 """Called when the entry exam status is availabe."""
1536 print "_handleEntryExamStatus", returned, result
1537 if returned and not self._getEntryExamStatusCancelled:
1538 self._entryExamLink = result.entryExamLink
1539 self._updateEntryExamStatus(result.entryExamPassed)
1540 if result.madeFO:
1541 self._madeFO()
1542 else:
1543 self._getEntryExamStatus()
1544
1545 def _updateEntryExamStatus(self, passed):
1546 """Update the entry exam status display and button."""
1547 self._entryExamStatus.set_text(xstr("student_entry_exam_passed")
1548 if passed else
1549 xstr("student_entry_exam_not_passed"))
1550 self._entryExamStatus.set_use_markup(True)
1551 self._entryExamButton.set_sensitive(not passed)
1552 self._wizard._loginResult.entryExamPassed = passed
1553
1554 def _checkFlightClicked(self, button):
1555 """Called when the check flight button is clicked."""
1556 aircraftType = self.aircraftType
1557 self._wizard._bookedFlight = \
1558 web.BookedFlight.forCheckFlight(aircraftType)
1559 self._wizard.gui.enableFlightInfo(aircraftType)
1560 self._wizard.jumpPage("connect")
1561
1562 def _updateCheckFlightStatus(self, passed):
1563 """Update the status of the check flight."""
1564 self._aircraftType.set_sensitive(not passed)
1565 self._checkFlightStatus.set_text(xstr("student_check_flight_passed")
1566 if passed else
1567 xstr("student_check_flight_not_passed"))
1568 self._checkFlightStatus.set_use_markup(True)
1569 self._checkFlightButton.set_sensitive(not passed)
1570
1571 def _madeFO(self):
1572 """Handle the event when the pilot has become a first officer."""
1573 wizard = self._wizard
1574 loginResult = wizard.loginResult
1575 loginResult.rank = "FO"
1576
1577 gui = wizard.gui
1578
1579 dialog = gtk.MessageDialog(parent = gui.mainWindow,
1580 type = MESSAGETYPE_INFO,
1581 message_format = xstr("student_fo"))
1582
1583 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1584 dialog.set_title(WINDOW_TITLE_BASE)
1585 secondary = xstr("student_fo_secondary")
1586 dialog.format_secondary_markup(secondary)
1587 dialog.run()
1588 dialog.hide()
1589
1590 gui.reset()
1591
1592#-----------------------------------------------------------------------------
1593
1594class ConnectPage(Page):
1595 """Page which displays the departure airport and gate (if at LHBP)."""
1596 def __init__(self, wizard):
1597 """Construct the connect page."""
1598 help = "Load the aircraft below into the simulator and park it\n" \
1599 "at the given airport, at the gate below, if present.\n\n" \
1600 "Then press the Connect button to connect to the simulator."
1601 completedHelp = "The basic data of your flight can be read below."
1602 super(ConnectPage, self).__init__(wizard, "connect",
1603 xstr("connect_title"),
1604 xstr("connect_help"),
1605 completedHelp = xstr("connect_chelp"))
1606
1607 self._selectSimulator = os.name=="nt" or "FORCE_SELECT_SIM" in os.environ
1608
1609 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1610 xscale = 0.0, yscale = 0.0)
1611
1612 table = gtk.Table(7 if self._selectSimulator else 5, 2)
1613 table.set_row_spacings(4)
1614 table.set_col_spacings(16)
1615 table.set_homogeneous(True)
1616 alignment.add(table)
1617 self.setMainWidget(alignment)
1618
1619 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1620 label = gtk.Label(xstr("connect_flightno"))
1621 labelAlignment.add(label)
1622 table.attach(labelAlignment, 0, 1, 0, 1)
1623
1624 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1625 self._flightNumber = gtk.Label()
1626 self._flightNumber.set_width_chars(9)
1627 self._flightNumber.set_alignment(0.0, 0.5)
1628 labelAlignment.add(self._flightNumber)
1629 table.attach(labelAlignment, 1, 2, 0, 1)
1630
1631 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1632 label = gtk.Label(xstr("connect_acft"))
1633 labelAlignment.add(label)
1634 table.attach(labelAlignment, 0, 1, 1, 2)
1635
1636 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1637 self._aircraft = gtk.Label()
1638 self._aircraft.set_width_chars(25)
1639 self._aircraft.set_alignment(0.0, 0.5)
1640 labelAlignment.add(self._aircraft)
1641 table.attach(labelAlignment, 1, 2, 1, 2)
1642
1643 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1644 label = gtk.Label(xstr("connect_tailno"))
1645 labelAlignment.add(label)
1646 table.attach(labelAlignment, 0, 1, 2, 3)
1647
1648 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1649 self._tailNumber = gtk.Label()
1650 self._tailNumber.set_width_chars(10)
1651 self._tailNumber.set_alignment(0.0, 0.5)
1652 labelAlignment.add(self._tailNumber)
1653 table.attach(labelAlignment, 1, 2, 2, 3)
1654
1655 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1656 label = gtk.Label(xstr("connect_airport"))
1657 labelAlignment.add(label)
1658 table.attach(labelAlignment, 0, 1, 3, 4)
1659
1660 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1661 self._departureICAO = gtk.Label()
1662 self._departureICAO.set_width_chars(6)
1663 self._departureICAO.set_alignment(0.0, 0.5)
1664 labelAlignment.add(self._departureICAO)
1665 table.attach(labelAlignment, 1, 2, 3, 4)
1666
1667 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
1668 label = gtk.Label(xstr("connect_gate"))
1669 labelAlignment.add(label)
1670 table.attach(labelAlignment, 0, 1, 4, 5)
1671
1672 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
1673 self._departureGate = gtk.Label()
1674 self._departureGate.set_width_chars(5)
1675 self._departureGate.set_alignment(0.0, 0.5)
1676 labelAlignment.add(self._departureGate)
1677 table.attach(labelAlignment, 1, 2, 4, 5)
1678
1679 if self._selectSimulator:
1680 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0, yalign=0.5)
1681 label = gtk.Label(xstr("connect_sim"))
1682 labelAlignment.add(label)
1683 table.attach(labelAlignment, 0, 1, 5, 7)
1684
1685 selectAlignment = gtk.Alignment(xalign=0.0, xscale=0.0, yalign=0.5)
1686
1687 selectBox = gtk.HBox()
1688 if pygobject:
1689 self._selectMSFS = \
1690 gtk.RadioButton.new_with_mnemonic_from_widget(None,
1691 xstr("connect_sim_msfs"))
1692 else:
1693 self._selectMSFS = gtk.RadioButton(None,
1694 xstr("connect_sim_msfs"))
1695
1696 selectBox.pack_start(self._selectMSFS, False, False, 0);
1697
1698 if pygobject:
1699 self._selectXPlane = \
1700 gtk.RadioButton.new_with_mnemonic_from_widget(self._selectMSFS,
1701 xstr("connect_sim_xplane"))
1702 else:
1703 self._selectXPlane = gtk.RadioButton(self._selectMSFS,
1704 xstr("connect_sim_xplane"))
1705
1706 selectBox.pack_start(self._selectXPlane, False, False, 8);
1707
1708 selectAlignment.add(selectBox)
1709 table.attach(selectAlignment, 1, 2, 5, 7)
1710
1711
1712 self.addCancelFlightButton()
1713
1714 self._previousButton = \
1715 self.addPreviousButton(clicked = self._backClicked)
1716
1717 self._button = self.addButton(xstr("button_connect"), default = True,
1718 tooltip = xstr("button_connect_tooltip"))
1719 self._clickedID = self._button.connect("clicked", self._connectClicked)
1720
1721 def activate(self):
1722 """Setup the departure information."""
1723 self._button.set_label(xstr("button_connect"))
1724 self._button.set_use_underline(True)
1725 self._button.set_tooltip_text(xstr("button_connect_tooltip"))
1726 self._button.disconnect(self._clickedID)
1727 self._clickedID = self._button.connect("clicked", self._connectClicked)
1728
1729 bookedFlight = self._wizard._bookedFlight
1730
1731 self._flightNumber.set_markup("<b>" + bookedFlight.callsign + "</b>")
1732
1733 aircraftType = aircraftNames[bookedFlight.aircraftType]
1734 self._aircraft.set_markup("<b>" + aircraftType + "</b>")
1735
1736 self._tailNumber.set_markup("<b>" + bookedFlight.tailNumber + "</b>")
1737
1738 icao = bookedFlight.departureICAO
1739 self._departureICAO.set_markup("<b>" + icao + "</b>")
1740 gate = self._wizard._departureGate
1741 if gate!="-":
1742 gate = "<b>" + gate + "</b>"
1743 self._departureGate.set_markup(gate)
1744
1745 if self._selectSimulator:
1746 config = self._wizard.gui.config
1747 self._selectMSFS.set_active(config.defaultMSFS)
1748 self._selectXPlane.set_active(not config.defaultMSFS)
1749
1750 self._previousButton.set_sensitive(not self._wizard.entranceExam)
1751
1752 def finalize(self):
1753 """Finalize the page."""
1754 self._button.set_label(xstr("button_next"))
1755 self._button.set_use_underline(True)
1756 self._button.set_tooltip_text(xstr("button_next_tooltip"))
1757 self._button.disconnect(self._clickedID)
1758 self._clickedID = self._button.connect("clicked", self._forwardClicked)
1759
1760 def _backClicked(self, button):
1761 """Called when the Back button is pressed."""
1762 self.goBack()
1763
1764 def _connectClicked(self, button):
1765 """Called when the Connect button is pressed."""
1766 if self._selectSimulator:
1767 simulatorType = const.SIM_MSFS9 if self._selectMSFS.get_active() \
1768 else const.SIM_XPLANE10
1769 else:
1770 simulatorType = const.SIM_MSFS9 if os.name=="nt" \
1771 else const.SIM_XPLANE10
1772
1773 config = self._wizard.gui.config
1774 config.defaultMSFS = simulatorType == const.SIM_MSFS9
1775 config.save()
1776
1777 self._wizard._connectSimulator(simulatorType)
1778
1779 def _forwardClicked(self, button):
1780 """Called when the Forward button is pressed."""
1781 self._wizard.nextPage()
1782
1783#-----------------------------------------------------------------------------
1784
1785class PayloadPage(Page):
1786 """Page to allow setting up the payload."""
1787 def __init__(self, wizard):
1788 """Construct the page."""
1789 super(PayloadPage, self).__init__(wizard, "payload",
1790 xstr("payload_title"),
1791 xstr("payload_help"),
1792 completedHelp = xstr("payload_chelp"))
1793
1794 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1795 xscale = 0.0, yscale = 0.0)
1796
1797 table = gtk.Table(7, 3)
1798 table.set_row_spacings(4)
1799 table.set_col_spacings(16)
1800 table.set_homogeneous(False)
1801 alignment.add(table)
1802 self.setMainWidget(alignment)
1803
1804 label = gtk.Label(xstr("payload_crew"))
1805 label.set_use_underline(True)
1806 label.set_alignment(0.0, 0.5)
1807 table.attach(label, 0, 1, 0, 1)
1808
1809 self._numCrew = IntegerEntry(defaultValue = 0)
1810 self._numCrew.set_width_chars(6)
1811 self._numCrew.connect("integer-changed", self._weightChanged)
1812 self._numCrew.set_tooltip_text(xstr("payload_crew_tooltip"))
1813 table.attach(self._numCrew, 1, 2, 0, 1)
1814 label.set_mnemonic_widget(self._numCrew)
1815
1816 label = gtk.Label(xstr("payload_pax"))
1817 label.set_use_underline(True)
1818 label.set_alignment(0.0, 0.5)
1819 table.attach(label, 0, 1, 1, 2)
1820
1821 self._numPassengers = IntegerEntry(defaultValue = 0)
1822 self._numPassengers.set_width_chars(6)
1823 self._numPassengers.connect("integer-changed", self._weightChanged)
1824 self._numPassengers.set_tooltip_text(xstr("payload_pax_tooltip"))
1825 table.attach(self._numPassengers, 1, 2, 1, 2)
1826 label.set_mnemonic_widget(self._numPassengers)
1827
1828 label = gtk.Label(xstr("payload_bag"))
1829 label.set_use_underline(True)
1830 label.set_alignment(0.0, 0.5)
1831 table.attach(label, 0, 1, 2, 3)
1832
1833 self._bagWeight = IntegerEntry(defaultValue = 0)
1834 self._bagWeight.set_width_chars(6)
1835 self._bagWeight.connect("integer-changed", self._weightChanged)
1836 self._bagWeight.set_tooltip_text(xstr("payload_bag_tooltip"))
1837 table.attach(self._bagWeight, 1, 2, 2, 3)
1838 label.set_mnemonic_widget(self._bagWeight)
1839
1840 table.attach(gtk.Label("kg"), 2, 3, 2, 3)
1841
1842 label = gtk.Label(xstr("payload_cargo"))
1843 label.set_use_underline(True)
1844 label.set_alignment(0.0, 0.5)
1845 table.attach(label, 0, 1, 3, 4)
1846
1847 self._cargoWeight = IntegerEntry(defaultValue = 0)
1848 self._cargoWeight.set_width_chars(6)
1849 self._cargoWeight.connect("integer-changed", self._weightChanged)
1850 self._cargoWeight.set_tooltip_text(xstr("payload_cargo_tooltip"))
1851 table.attach(self._cargoWeight, 1, 2, 3, 4)
1852 label.set_mnemonic_widget(self._cargoWeight)
1853
1854 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
1855
1856 label = gtk.Label(xstr("payload_mail"))
1857 label.set_use_underline(True)
1858 label.set_alignment(0.0, 0.5)
1859 table.attach(label, 0, 1, 4, 5)
1860
1861 self._mailWeight = IntegerEntry(defaultValue = 0)
1862 self._mailWeight.set_width_chars(6)
1863 self._mailWeight.connect("integer-changed", self._weightChanged)
1864 self._mailWeight.set_tooltip_text(xstr("payload_mail_tooltip"))
1865 table.attach(self._mailWeight, 1, 2, 4, 5)
1866 label.set_mnemonic_widget(self._mailWeight)
1867
1868 table.attach(gtk.Label("kg"), 2, 3, 4, 5)
1869
1870 label = gtk.Label("<b>" + xstr("payload_zfw") + "</b>")
1871 label.set_alignment(0.0, 0.5)
1872 label.set_use_markup(True)
1873 table.attach(label, 0, 1, 5, 6)
1874
1875 self._calculatedZFW = gtk.Label()
1876 self._calculatedZFW.set_width_chars(6)
1877 self._calculatedZFW.set_alignment(1.0, 0.5)
1878 table.attach(self._calculatedZFW, 1, 2, 5, 6)
1879
1880 table.attach(gtk.Label("kg"), 2, 3, 5, 6)
1881
1882 self._zfwButton = gtk.Button(xstr("payload_fszfw"))
1883 self._zfwButton.set_use_underline(True)
1884 self._zfwButton.connect("clicked", self._zfwRequested)
1885 self._zfwButton.set_tooltip_text(xstr("payload_fszfw_tooltip"))
1886 table.attach(self._zfwButton, 0, 1, 6, 7)
1887
1888 self._simulatorZFW = gtk.Label("-")
1889 self._simulatorZFW.set_width_chars(6)
1890 self._simulatorZFW.set_alignment(1.0, 0.5)
1891 table.attach(self._simulatorZFW, 1, 2, 6, 7)
1892 self._simulatorZFWValue = None
1893
1894 table.attach(gtk.Label("kg"), 2, 3, 6, 7)
1895
1896 self.addCancelFlightButton()
1897 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1898 self._button = self.addNextButton(clicked = self._forwardClicked)
1899
1900 @property
1901 def numCrew(self):
1902 """The number of the crew members on the flight."""
1903 return self._numCrew.get_int()
1904
1905 @property
1906 def numPassengers(self):
1907 """The number of the passengers on the flight."""
1908 return self._numPassengers.get_int()
1909
1910 @property
1911 def bagWeight(self):
1912 """Get the bag weight entered."""
1913 return self._bagWeight.get_int()
1914
1915 @property
1916 def cargoWeight(self):
1917 """Get the cargo weight entered."""
1918 return self._cargoWeight.get_int()
1919
1920 @property
1921 def mailWeight(self):
1922 """Get the bag weight entered."""
1923 return self._mailWeight.get_int()
1924
1925 def activate(self):
1926 """Setup the information."""
1927 bookedFlight = self._wizard._bookedFlight
1928
1929 self._numCrew.set_int(bookedFlight.numCrew)
1930 self._numCrew.set_sensitive(True)
1931 self._numPassengers.set_int(bookedFlight.numPassengers)
1932 self._numPassengers.set_sensitive(True)
1933
1934 self._bagWeight.set_int(bookedFlight.bagWeight)
1935 self._bagWeight.set_sensitive(True)
1936 self._cargoWeight.set_int(bookedFlight.cargoWeight)
1937 self._cargoWeight.set_sensitive(True)
1938 self._mailWeight.set_int(bookedFlight.mailWeight)
1939 self._mailWeight.set_sensitive(True)
1940
1941 self._simulatorZFW.set_text("-")
1942 self._simulatorZFWValue = None
1943 self._zfwButton.set_sensitive(True)
1944 self._updateCalculatedZFW()
1945
1946 def finalize(self):
1947 """Finalize the payload page."""
1948 self._numCrew.set_sensitive(False)
1949 self._numPassengers.set_sensitive(False)
1950 self._bagWeight.set_sensitive(False)
1951 self._cargoWeight.set_sensitive(False)
1952 self._mailWeight.set_sensitive(False)
1953 self._wizard.gui.initializeWeightHelp()
1954
1955 def calculateZFW(self):
1956 """Calculate the ZFW value."""
1957 zfw = self._wizard.gui._flight.aircraft.dow
1958 zfw += (self._numCrew.get_int() + self._numPassengers.get_int()) * 82
1959 zfw += self._bagWeight.get_int()
1960 zfw += self._cargoWeight.get_int()
1961 zfw += self._mailWeight.get_int()
1962 return zfw
1963
1964 def _updateCalculatedZFW(self):
1965 """Update the calculated ZFW"""
1966 zfw = self.calculateZFW()
1967
1968 markupBegin = "<b>"
1969 markupEnd = "</b>"
1970 if self._simulatorZFWValue is not None and \
1971 PayloadChecker.isZFWFaulty(self._simulatorZFWValue, zfw):
1972 markupBegin += '<span foreground="red">'
1973 markupEnd = "</span>" + markupEnd
1974 self._calculatedZFW.set_markup(markupBegin + str(zfw) + markupEnd)
1975
1976 def _weightChanged(self, entry, weight):
1977 """Called when one of the weight values or humanm counts has changed."""
1978 self._updateCalculatedZFW()
1979
1980 def _zfwRequested(self, button):
1981 """Called when the ZFW is requested from the simulator."""
1982 self._zfwButton.set_sensitive(False)
1983 self._backButton.set_sensitive(False)
1984 self._button.set_sensitive(False)
1985 gui = self._wizard.gui
1986 gui.beginBusy(xstr("payload_zfw_busy"))
1987 gui.simulator.requestZFW(self._handleZFW)
1988
1989 def _handleZFW(self, zfw):
1990 """Called when the ZFW value is retrieved."""
1991 gobject.idle_add(self._processZFW, zfw)
1992
1993 def _processZFW(self, zfw):
1994 """Process the given ZFW value received from the simulator."""
1995 self._wizard.gui.endBusy()
1996 self._zfwButton.set_sensitive(True)
1997 self._backButton.set_sensitive(True)
1998 self._button.set_sensitive(True)
1999 self._simulatorZFWValue = zfw
2000 self._simulatorZFW.set_text("%.0f" % (zfw,))
2001 self._updateCalculatedZFW()
2002
2003 def _forwardClicked(self, button):
2004 """Called when the forward button is clicked."""
2005 self._wizard.nextPage()
2006
2007 def _backClicked(self, button):
2008 """Called when the Back button is pressed."""
2009 self.goBack()
2010
2011#-----------------------------------------------------------------------------
2012
2013class TimePage(Page):
2014 """Page displaying the departure and arrival times and allows querying the
2015 current time from the flight simulator."""
2016 def __init__(self, wizard):
2017 super(TimePage, self).__init__(wizard, "time",
2018 xstr("time_title"),
2019 xstr("time_help"),
2020 completedHelp = xstr("time_chelp"))
2021
2022 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2023 xscale = 0.0, yscale = 0.0)
2024
2025 table = gtk.Table(3, 2)
2026 table.set_row_spacings(4)
2027 table.set_col_spacings(16)
2028 table.set_homogeneous(False)
2029 alignment.add(table)
2030 self.setMainWidget(alignment)
2031
2032 label = gtk.Label(xstr("time_departure"))
2033 label.set_alignment(0.0, 0.5)
2034 table.attach(label, 0, 1, 0, 1)
2035
2036 self._departure = gtk.Label()
2037 self._departure.set_alignment(0.0, 0.5)
2038 table.attach(self._departure, 1, 2, 0, 1)
2039
2040 label = gtk.Label(xstr("time_arrival"))
2041 label.set_alignment(0.0, 0.5)
2042 table.attach(label, 0, 1, 1, 2)
2043
2044 self._arrival = gtk.Label()
2045 self._arrival.set_alignment(0.0, 0.5)
2046 table.attach(self._arrival, 1, 2, 1, 2)
2047
2048 self._timeButton = gtk.Button(xstr("time_fs"))
2049 self._timeButton.set_use_underline(True)
2050 self._timeButton.set_tooltip_text(xstr("time_fs_tooltip"))
2051 self._timeButton.connect("clicked", self._timeRequested)
2052 table.attach(self._timeButton, 0, 1, 2, 3)
2053
2054 self._simulatorTime = gtk.Label("-")
2055 self._simulatorTime.set_alignment(0.0, 0.5)
2056 table.attach(self._simulatorTime, 1, 2, 2, 3)
2057
2058 self.addCancelFlightButton()
2059
2060 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2061 self._button = self.addNextButton(clicked = self._forwardClicked)
2062
2063 def activate(self):
2064 """Activate the page."""
2065 self._timeButton.set_sensitive(True)
2066 bookedFlight = self._wizard._bookedFlight
2067 self._departure.set_text(str(bookedFlight.departureTime.time()))
2068 self._arrival.set_text(str(bookedFlight.arrivalTime.time()))
2069 self._simulatorTime.set_text("-")
2070
2071 def _timeRequested(self, button):
2072 """Request the time from the simulator."""
2073 self._timeButton.set_sensitive(False)
2074 self._backButton.set_sensitive(False)
2075 self._button.set_sensitive(False)
2076 self._wizard.gui.beginBusy(xstr("time_busy"))
2077 self._wizard.gui.simulator.requestTime(self._handleTime)
2078
2079 def _handleTime(self, timestamp):
2080 """Handle the result of a time retrieval."""
2081 gobject.idle_add(self._processTime, timestamp)
2082
2083 def _processTime(self, timestamp):
2084 """Process the given time."""
2085 self._wizard.gui.endBusy()
2086 self._timeButton.set_sensitive(True)
2087 self._backButton.set_sensitive(True)
2088 self._button.set_sensitive(True)
2089 tm = time.gmtime(timestamp)
2090 t = datetime.time(tm.tm_hour, tm.tm_min, tm.tm_sec)
2091 self._simulatorTime.set_text(str(t))
2092
2093 ts = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec
2094 dt = self._wizard._bookedFlight.departureTime.time()
2095 dts = dt.hour * 3600 + dt.minute * 60 + dt.second
2096 diff = dts-ts
2097
2098 markupBegin = ""
2099 markupEnd = ""
2100 if diff < 0:
2101 markupBegin = '<b><span foreground="red">'
2102 markupEnd = '</span></b>'
2103 elif diff < 3*60 or diff > 30*60:
2104 markupBegin = '<b><span foreground="orange">'
2105 markupEnd = '</span></b>'
2106
2107 self._departure.set_markup(markupBegin + str(dt) + markupEnd)
2108
2109 def _backClicked(self, button):
2110 """Called when the Back button is pressed."""
2111 self.goBack()
2112
2113 def _forwardClicked(self, button):
2114 """Called when the forward button is clicked."""
2115 if not self._completed:
2116 gui = self._wizard.gui
2117 gui.beginBusy(xstr("fuel_get_busy"))
2118
2119 gui.simulator.getFuel(self._handleFuel)
2120 else:
2121 self._wizard.nextPage()
2122
2123 def _handleFuel(self, fuelData):
2124 """Callback for the fuel query operation."""
2125 gobject.idle_add(self._processFuel, fuelData)
2126
2127 def _processFuel(self, fuelData):
2128 """Process the given fuel data."""
2129 self._wizard.gui.endBusy()
2130 self._wizard._fuelData = fuelData
2131 self._wizard.nextPage()
2132
2133#-----------------------------------------------------------------------------
2134
2135class RoutePage(Page):
2136 """The page containing the route and the flight level."""
2137 def __init__(self, wizard):
2138 """Construct the page."""
2139 super(RoutePage, self).__init__(wizard, "route",
2140 xstr("route_title"),
2141 xstr("route_help"),
2142 completedHelp = xstr("route_chelp"))
2143
2144 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2145 xscale = 0.0, yscale = 0.0)
2146
2147 mainBox = gtk.VBox()
2148 alignment.add(mainBox)
2149 self.setMainWidget(alignment)
2150
2151 levelBox = gtk.HBox()
2152
2153 label = gtk.Label(xstr("route_level"))
2154 label.set_use_underline(True)
2155 levelBox.pack_start(label, True, True, 0)
2156
2157 self._cruiseLevel = gtk.SpinButton()
2158 self._cruiseLevel.set_increments(step = 10, page = 100)
2159 self._cruiseLevel.set_range(min = 0, max = 500)
2160 self._cruiseLevel.set_tooltip_text(xstr("route_level_tooltip"))
2161 self._cruiseLevel.set_numeric(True)
2162 self._cruiseLevel.connect("changed", self._cruiseLevelChanged)
2163 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
2164 label.set_mnemonic_widget(self._cruiseLevel)
2165
2166 levelBox.pack_start(self._cruiseLevel, False, False, 8)
2167
2168 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
2169 xscale = 0.0, yscale = 0.0)
2170 alignment.add(levelBox)
2171
2172 mainBox.pack_start(alignment, False, False, 0)
2173
2174
2175 routeBox = gtk.VBox()
2176
2177 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
2178 xscale = 0.0, yscale = 0.0)
2179 label = gtk.Label(xstr("route_route"))
2180 label.set_use_underline(True)
2181 alignment.add(label)
2182 routeBox.pack_start(alignment, True, True, 0)
2183
2184 routeWindow = gtk.ScrolledWindow()
2185 routeWindow.set_size_request(400, 80)
2186 routeWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
2187 else gtk.SHADOW_IN)
2188 routeWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
2189 else gtk.POLICY_AUTOMATIC,
2190 gtk.PolicyType.AUTOMATIC if pygobject
2191 else gtk.POLICY_AUTOMATIC)
2192
2193 self._uppercasingRoute = False
2194
2195 self._route = gtk.TextView()
2196 self._route.set_tooltip_text(xstr("route_route_tooltip"))
2197 self._route.set_wrap_mode(WRAP_WORD)
2198 self._route.get_buffer().connect("changed", self._routeChanged)
2199 self._route.get_buffer().connect_after("insert-text", self._routeInserted)
2200 routeWindow.add(self._route)
2201
2202 label.set_mnemonic_widget(self._route)
2203 routeBox.pack_start(routeWindow, True, True, 0)
2204
2205 mainBox.pack_start(routeBox, True, True, 8)
2206
2207 alternateBox = gtk.HBox()
2208
2209 label = gtk.Label(xstr("route_altn"))
2210 label.set_use_underline(True)
2211 alternateBox.pack_start(label, True, True, 0)
2212
2213 self._alternate = gtk.Entry()
2214 self._alternate.set_width_chars(6)
2215 self._alternate.connect("changed", self._alternateChanged)
2216 self._alternate.set_tooltip_text(xstr("route_altn_tooltip"))
2217 label.set_mnemonic_widget(self._alternate)
2218
2219 alternateBox.pack_start(self._alternate, False, False, 8)
2220
2221 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
2222 xscale = 0.0, yscale = 0.0)
2223 alignment.add(alternateBox)
2224
2225 mainBox.pack_start(alignment, False, False, 0)
2226
2227 self.addCancelFlightButton()
2228
2229 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2230 self._button = self.addNextButton(clicked = self._forwardClicked)
2231
2232 @property
2233 def filedCruiseLevel(self):
2234 """Get the filed cruise level."""
2235 return self._cruiseLevel.get_value_as_int()
2236
2237 @property
2238 def route(self):
2239 """Get the route."""
2240 return self._getRoute()
2241
2242 @property
2243 def alternate(self):
2244 """Get the ICAO code of the alternate airport."""
2245 return self._alternate.get_text()
2246
2247 def activate(self):
2248 """Setup the route from the booked flight."""
2249 self._cruiseLevel.set_value(0)
2250 self._cruiseLevel.set_text("")
2251 self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
2252 self._alternate.set_text("")
2253 self._updateForwardButton()
2254
2255 def _getRoute(self):
2256 """Get the text of the route."""
2257 buffer = self._route.get_buffer()
2258 return buffer.get_text(buffer.get_start_iter(),
2259 buffer.get_end_iter(), True)
2260
2261 def _updateForwardButton(self):
2262 """Update the sensitivity of the forward button."""
2263 cruiseLevelText = self._cruiseLevel.get_text()
2264 cruiseLevel = int(cruiseLevelText) if cruiseLevelText else 0
2265 alternate = self._alternate.get_text()
2266 self._button.set_sensitive(cruiseLevel>=50 and self._getRoute()!="" and
2267 (len(alternate)==4 or self._wizard.entranceExam))
2268
2269 def _cruiseLevelChanged(self, *arg):
2270 """Called when the cruise level has changed."""
2271 self._updateForwardButton()
2272
2273 def _routeChanged(self, textBuffer):
2274 """Called when the route has changed."""
2275 if not self._uppercasingRoute:
2276 self._updateForwardButton()
2277
2278 def _routeInserted(self, textBuffer, iter, text, length):
2279 """Called when new characters are inserted into the route.
2280
2281 It uppercases all characters."""
2282 if not self._uppercasingRoute:
2283 self._uppercasingRoute = True
2284
2285 iter1 = iter.copy()
2286 iter1.backward_chars(length)
2287 textBuffer.delete(iter, iter1)
2288
2289 textBuffer.insert(iter, text.upper())
2290
2291 self._uppercasingRoute = False
2292
2293 def _alternateChanged(self, entry):
2294 """Called when the alternate airport has changed."""
2295 entry.set_text(entry.get_text().upper())
2296 self._updateForwardButton()
2297
2298 def _backClicked(self, button):
2299 """Called when the Back button is pressed."""
2300 self.goBack()
2301
2302 def _forwardClicked(self, button):
2303 """Called when the Forward button is clicked."""
2304 if self._wizard.gui.flight.aircraft.simBriefData is None:
2305 self._wizard.usingSimBrief = False
2306 if self._wizard.gui.config.useSimBrief and \
2307 self._wizard.usingSimBrief is not False:
2308 self._wizard.jumpPage("simbrief_setup")
2309 else:
2310 self._wizard.usingSimBrief = False
2311 self._wizard.jumpPage("fuel")
2312
2313#-----------------------------------------------------------------------------
2314
2315class SimBriefCredentialsDialog(gtk.Dialog):
2316 """A dialog window to ask for SimBrief credentials."""
2317 def __init__(self, gui, userName, password, rememberPassword):
2318 """Construct the dialog."""
2319 super(SimBriefCredentialsDialog, self).__init__(WINDOW_TITLE_BASE + " - " +
2320 xstr("simbrief_credentials_title"),
2321 gui.mainWindow,
2322 DIALOG_MODAL)
2323 self.add_button(xstr("button_cancel"), RESPONSETYPE_CANCEL)
2324 self.add_button(xstr("button_ok"), RESPONSETYPE_OK)
2325
2326 contentArea = self.get_content_area()
2327
2328 contentAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2329 xscale = 0.0, yscale = 0.0)
2330 contentAlignment.set_padding(padding_top = 4, padding_bottom = 16,
2331 padding_left = 8, padding_right = 8)
2332
2333 contentArea.pack_start(contentAlignment, False, False, 0)
2334
2335 contentVBox = gtk.VBox()
2336 contentAlignment.add(contentVBox)
2337
2338 label = gtk.Label(xstr("simbrief_login_failed"))
2339 label.set_alignment(0.0, 0.0)
2340
2341 contentVBox.pack_start(label, False, False, 0)
2342
2343 tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2344 xscale = 0.0, yscale = 0.0)
2345 tableAlignment.set_padding(padding_top = 24, padding_bottom = 0,
2346 padding_left = 0, padding_right = 0)
2347
2348 table = gtk.Table(3, 2)
2349 table.set_row_spacings(4)
2350 table.set_col_spacings(16)
2351 table.set_homogeneous(False)
2352
2353 tableAlignment.add(table)
2354 contentVBox.pack_start(tableAlignment, True, True, 0)
2355
2356 label = gtk.Label(xstr("simbrief_username"))
2357 label.set_use_underline(True)
2358 label.set_alignment(0.0, 0.5)
2359 table.attach(label, 0, 1, 0, 1)
2360
2361 self._userName = gtk.Entry()
2362 self._userName.set_width_chars(16)
2363 #self._userName.connect("changed",
2364 # lambda button: self._updateForwardButton())
2365 self._userName.set_tooltip_text(xstr("simbrief_username_tooltip"))
2366 self._userName.set_text(userName)
2367 table.attach(self._userName, 1, 2, 0, 1)
2368 label.set_mnemonic_widget(self._userName)
2369
2370 label = gtk.Label(xstr("simbrief_password"))
2371 label.set_use_underline(True)
2372 label.set_alignment(0.0, 0.5)
2373 table.attach(label, 0, 1, 1, 2)
2374
2375 self._password = gtk.Entry()
2376 self._password.set_visibility(False)
2377 #self._password.connect("changed",
2378 # lambda button: self._updateForwardButton())
2379 self._password.set_tooltip_text(xstr("simbrief_password_tooltip"))
2380 self._password.set_text(password)
2381 table.attach(self._password, 1, 2, 1, 2)
2382 label.set_mnemonic_widget(self._password)
2383
2384 self._rememberButton = gtk.CheckButton(xstr("simbrief_remember_password"))
2385 self._rememberButton.set_use_underline(True)
2386 self._rememberButton.set_tooltip_text(xstr("simbrief_remember_tooltip"))
2387 self._rememberButton.set_active(rememberPassword)
2388 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
2389
2390 @property
2391 def userName(self):
2392 """Get the user name entered."""
2393 return self._userName.get_text()
2394
2395 @property
2396 def password(self):
2397 """Get the password entered."""
2398 return self._password.get_text()
2399
2400 @property
2401 def rememberPassword(self):
2402 """Get whether the password is to be remembered."""
2403 return self._rememberButton.get_active()
2404
2405 def run(self):
2406 """Run the dialog."""
2407 self.show_all()
2408
2409 response = super(SimBriefCredentialsDialog, self).run()
2410
2411 self.hide()
2412
2413 return response
2414
2415#-----------------------------------------------------------------------------
2416
2417class SimBriefSetupPage(Page):
2418 """Page for setting up some parameters for SimBrief."""
2419 monthNum2Name = [
2420 "JAN",
2421 "FEB",
2422 "MAR",
2423 "APR",
2424 "MAY",
2425 "JUN",
2426 "JUL",
2427 "AUG",
2428 "SEP",
2429 "OCT",
2430 "NOV",
2431 "DEC"
2432 ]
2433
2434 progress2Message = {
2435 cef.SIMBRIEF_PROGRESS_SEARCHING_BROWSER: "simbrief_progress_searching_browser",
2436 cef.SIMBRIEF_PROGRESS_LOADING_FORM: "simbrief_progress_loading_form",
2437 cef.SIMBRIEF_PROGRESS_FILLING_FORM: "simbrief_progress_filling_form",
2438 cef.SIMBRIEF_PROGRESS_WAITING_LOGIN: "simbrief_progress_waiting_login",
2439 cef.SIMBRIEF_PROGRESS_LOGGING_IN: "simbrief_progress_logging_in",
2440 cef.SIMBRIEF_PROGRESS_WAITING_RESULT: "simbrief_progress_waiting_result",
2441 cef.SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING: "simbrief_progress_retrieving_briefing"
2442 }
2443
2444 result2Message = {
2445 cef.SIMBRIEF_RESULT_ERROR_OTHER: "simbrief_result_error_other",
2446 cef.SIMBRIEF_RESULT_ERROR_NO_FORM: "simbrief_result_error_no_form",
2447 cef.SIMBRIEF_RESULT_ERROR_NO_POPUP: "simbrief_result_error_no_popup",
2448 cef.SIMBRIEF_RESULT_ERROR_LOGIN_FAILED: "simbrief_result_error_login_failed"
2449 }
2450
2451 @staticmethod
2452 def getHTMLFilePath():
2453 """Get the path of the HTML file to contain the generated flight
2454 plan."""
2455 if os.name=="nt":
2456 return os.path.join(tempfile.gettempdir(),
2457 "mlx_simbrief" +
2458 (".secondary" if secondaryInstallation else "") +
2459 ".html")
2460 else:
2461 import pwd
2462 return os.path.join(tempfile.gettempdir(),
2463 "mlx_simbrief." + pwd.getpwuid(os.getuid())[0] + "" +
2464 (".secondary" if secondaryInstallation else "") +
2465 ".html")
2466
2467 def __init__(self, wizard):
2468 """Construct the setup page."""
2469
2470 super(SimBriefSetupPage, self).__init__(wizard, "simbrief_setup",
2471 xstr("simbrief_setup_title"),
2472 xstr("simbrief_setup_help"),
2473 xstr("simbrief_setup_chelp"))
2474
2475 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2476 xscale = 0.0, yscale = 0.0)
2477
2478 table = gtk.Table(9, 3)
2479 table.set_row_spacings(4)
2480 table.set_col_spacings(16)
2481 table.set_homogeneous(False)
2482 alignment.add(table)
2483 self.setMainWidget(alignment)
2484
2485 label = gtk.Label(xstr("simbrief_username"))
2486 label.set_use_underline(True)
2487 label.set_alignment(0.0, 0.5)
2488 table.attach(label, 0, 1, 0, 1)
2489
2490 self._userName = gtk.Entry()
2491 self._userName.set_width_chars(16)
2492 self._userName.connect("changed",
2493 lambda button: self._updateForwardButton())
2494 self._userName.set_tooltip_text(xstr("simbrief_username_tooltip"))
2495 table.attach(self._userName, 1, 2, 0, 1)
2496 label.set_mnemonic_widget(self._userName)
2497
2498 label = gtk.Label(xstr("simbrief_password"))
2499 label.set_use_underline(True)
2500 label.set_alignment(0.0, 0.5)
2501 table.attach(label, 0, 1, 1, 2)
2502
2503 self._password = gtk.Entry()
2504 self._password.set_visibility(False)
2505 self._password.connect("changed",
2506 lambda button: self._updateForwardButton())
2507 self._password.set_tooltip_text(xstr("simbrief_password_tooltip"))
2508 table.attach(self._password, 1, 2, 1, 2)
2509 label.set_mnemonic_widget(self._password)
2510
2511 self._rememberButton = gtk.CheckButton(xstr("simbrief_remember_password"))
2512 self._rememberButton.set_use_underline(True)
2513 self._rememberButton.set_tooltip_text(xstr("simbrief_remember_tooltip"))
2514 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
2515
2516 label = gtk.Label(xstr("simbrief_extra_fuel"))
2517 label.set_use_underline(True)
2518 label.set_alignment(0.0, 0.5)
2519 table.attach(label, 0, 1, 3, 4)
2520
2521 self._extraFuel = IntegerEntry(defaultValue = 0)
2522 self._extraFuel.set_width_chars(6)
2523 self._extraFuel.set_tooltip_text(xstr("simbrief_extra_fuel_tooltip"))
2524 table.attach(self._extraFuel, 1, 2, 3, 4)
2525 label.set_mnemonic_widget(self._extraFuel)
2526
2527 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
2528
2529 label = gtk.Label(xstr("simbrief_takeoff_runway"))
2530 label.set_use_underline(True)
2531 label.set_alignment(0.0, 0.5)
2532 table.attach(label, 0, 1, 4, 5)
2533
2534 self._takeoffRunway = gtk.Entry()
2535 self._takeoffRunway.set_width_chars(10)
2536 self._takeoffRunway.set_tooltip_text(xstr("simbrief_takeoff_runway_tooltip"))
2537 self._takeoffRunway.connect("changed", self._upperChanged)
2538 table.attach(self._takeoffRunway, 1, 2, 4, 5)
2539 label.set_mnemonic_widget(self._takeoffRunway)
2540
2541 label = gtk.Label(xstr("simbrief_landing_runway"))
2542 label.set_use_underline(True)
2543 label.set_alignment(0.0, 0.5)
2544 table.attach(label, 0, 1, 5, 6)
2545
2546 self._landingRunway = gtk.Entry()
2547 self._landingRunway.set_width_chars(10)
2548 self._landingRunway.set_tooltip_text(xstr("simbrief_takeoff_runway_tooltip"))
2549 self._landingRunway.connect("changed", self._upperChanged)
2550 table.attach(self._landingRunway, 1, 2, 5, 6)
2551 label.set_mnemonic_widget(self._landingRunway)
2552
2553 label = gtk.Label(xstr("simbrief_climb_profile"))
2554 label.set_use_underline(True)
2555 label.set_alignment(0.0, 0.5)
2556 table.attach(label, 0, 1, 6, 7)
2557
2558 self._climbProfile = gtk.ComboBox()
2559 renderer = gtk.CellRendererText()
2560 self._climbProfile.pack_start(renderer, True)
2561 self._climbProfile.add_attribute(renderer, "text", 0)
2562 self._climbProfile.set_tooltip_text(xstr("simbrief_climb_profile_tooltip"))
2563 table.attach(self._climbProfile, 1, 2, 6, 7)
2564 label.set_mnemonic_widget(self._climbProfile)
2565
2566 label = gtk.Label(xstr("simbrief_cruise_profile"))
2567 label.set_use_underline(True)
2568 label.set_alignment(0.0, 0.5)
2569 table.attach(label, 0, 1, 7, 8)
2570
2571 self._cruiseProfile = gtk.ComboBox()
2572 renderer = gtk.CellRendererText()
2573 self._cruiseProfile.pack_start(renderer, True)
2574 self._cruiseProfile.add_attribute(renderer, "text", 0)
2575 self._cruiseProfile.set_tooltip_text(xstr("simbrief_cruise_profile_tooltip"))
2576 table.attach(self._cruiseProfile, 1, 2, 7, 8)
2577 label.set_mnemonic_widget(self._cruiseProfile)
2578
2579 label = gtk.Label(xstr("simbrief_descent_profile"))
2580 label.set_use_underline(True)
2581 label.set_alignment(0.0, 0.5)
2582 table.attach(label, 0, 1, 8, 9)
2583
2584 self._descentProfile = gtk.ComboBox()
2585 renderer = gtk.CellRendererText()
2586 self._descentProfile.pack_start(renderer, True)
2587 self._descentProfile.add_attribute(renderer, "text", 0)
2588 self._descentProfile.set_tooltip_text(xstr("simbrief_descent_profile_tooltip"))
2589 table.attach(self._descentProfile, 1, 2, 8, 9)
2590 label.set_mnemonic_widget(self._descentProfile)
2591
2592 self.addCancelFlightButton()
2593
2594 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2595 self._button = self.addNextButton(clicked = self._forwardClicked)
2596
2597 def activate(self):
2598 """Activate the SimBrief setup page"""
2599 config = self._wizard.gui.config
2600
2601 self._userName.set_text(config.simBriefUserName)
2602 self._userName.set_sensitive(True)
2603
2604 self._password.set_text(config.simBriefPassword)
2605 self._password.set_sensitive(True)
2606
2607 self._rememberButton.set_active(config.rememberSimBriefPassword)
2608 self._rememberButton.set_sensitive(True)
2609
2610 self._extraFuel.set_int(0)
2611 self._extraFuel.set_sensitive(True)
2612
2613 self._takeoffRunway.set_text("")
2614 self._takeoffRunway.set_sensitive(True)
2615
2616 self._landingRunway.set_text("")
2617 self._landingRunway.set_sensitive(True)
2618
2619 simBriefData = self._wizard.gui.flight.aircraft.simBriefData
2620 for (control, profiles) in [(self._climbProfile,
2621 simBriefData.climbProfiles),
2622 (self._cruiseProfile,
2623 simBriefData.cruiseProfiles),
2624 (self._descentProfile,
2625 simBriefData.descentProfiles)]:
2626 model = gtk.ListStore(str)
2627 for profile in profiles:
2628 model.append([profile])
2629 control.set_model(model)
2630 control.set_sensitive(True)
2631
2632 self._climbProfile.set_active(0)
2633 self._cruiseProfile.set_active(0)
2634 self._descentProfile.set_active(0)
2635
2636 self._updateForwardButton()
2637
2638 def _updateForwardButton(self):
2639 """Update the sensitivity of the forward button."""
2640 self._button.set_sensitive(len(self._userName.get_text())>0 and
2641 len(self._password.get_text())>0)
2642
2643 def _backClicked(self, button):
2644 """Called when the Back button is pressed."""
2645 self.goBack()
2646
2647 def _forwardClicked(self, button):
2648 if self._completed:
2649 self._wizard.nextPage()
2650 else:
2651 config = self._wizard.gui.config
2652
2653 config.simBriefUserName = self._userName.get_text()
2654
2655 rememberPassword = self._rememberButton.get_active()
2656 config.simBriefPassword = \
2657 self._password.get_text() if rememberPassword else ""
2658 config.rememberSimBriefPassword = rememberPassword
2659
2660 config.save()
2661
2662 plan = self._getPlan()
2663 print "plan:", plan
2664
2665 takeoffRunway = self._takeoffRunway.get_text()
2666 if takeoffRunway:
2667 self._wizard.takeoffRunway = takeoffRunway
2668
2669 landingRunway = self._landingRunway.get_text()
2670 if landingRunway:
2671 self._wizard.landingRunway = landingRunway
2672
2673 self._userName.set_sensitive(False)
2674 self._password.set_sensitive(False)
2675 self._rememberButton.set_sensitive(False)
2676 self._extraFuel.set_sensitive(False)
2677 self._takeoffRunway.set_sensitive(False)
2678 self._landingRunway.set_sensitive(False)
2679
2680 self._climbProfile.set_sensitive(False)
2681 self._cruiseProfile.set_sensitive(False)
2682 self._descentProfile.set_sensitive(False)
2683
2684 self._wizard.gui.beginBusy(xstr("simbrief_calling"))
2685
2686 cef.callSimBrief(plan,
2687 self._getCredentials,
2688 self._simBriefProgress,
2689 SimBriefSetupPage.getHTMLFilePath())
2690
2691 startSound(const.SOUND_NOTAM)
2692
2693 def _getCredentials(self, count):
2694 """Get the credentials.
2695
2696 If count is 0, the user name and password entered into the setup page
2697 are returned. Otherwise a dialog box is displayed informing the user of
2698 invalid credentials and requesting another set of them."""
2699 print "_getCredentials", count
2700 if count==0:
2701 return (self._userName.get_text(), self._password.get_text())
2702 else:
2703 gui = self._wizard.gui
2704 config = gui.config
2705
2706 dialog = SimBriefCredentialsDialog(gui,
2707 config.simBriefUserName,
2708 config.simBriefPassword,
2709 config.rememberSimBriefPassword)
2710 response = dialog.run()
2711
2712 if response==RESPONSETYPE_OK:
2713 userName = dialog.userName
2714 self._userName.set_text(userName)
2715 password = dialog.password
2716 self._password.set_text(password)
2717 rememberPassword = dialog.rememberPassword
2718
2719 config.simBriefUserName = userName
2720
2721 config.simBriefPassword = \
2722 password if rememberPassword else ""
2723 config.rememberSimBriefPassword = rememberPassword
2724
2725 config.save()
2726
2727 return (userName, password)
2728 else:
2729 return (None, None)
2730
2731 def _simBriefProgress(self, progress, result, flightInfo):
2732 """The real SimBrief progress handler."""
2733 print "_simBriefProgress", progress, result, flightInfo
2734 if result==cef.SIMBRIEF_RESULT_NONE:
2735 message = SimBriefSetupPage.progress2Message.get(progress,
2736 "simbrief_progress_unknown")
2737 self._wizard.gui.updateBusyState(xstr(message))
2738 else:
2739 self._wizard.gui.endBusy()
2740
2741 if result==cef.SIMBRIEF_RESULT_OK:
2742 self._wizard.departureMETARChanged(flightInfo["orig_metar"],
2743 self)
2744 self._wizard.arrivalMETARChanged(flightInfo["dest_metar"], self)
2745 self._wizard.nextPage()
2746 else:
2747 message = SimBriefSetupPage.result2Message.get(result,
2748 "simbrief_result_unknown")
2749 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
2750 type = MESSAGETYPE_ERROR,
2751 message_format =
2752 xstr(message) + "\n"+
2753 xstr("simbrief_cancelled"))
2754
2755 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
2756 dialog.set_title(WINDOW_TITLE_BASE)
2757 secondary = xstr("flightsel_save_failed_sec")
2758 dialog.format_secondary_markup(secondary)
2759 dialog.run()
2760 dialog.hide()
2761
2762 self._wizard.usingSimBrief = False
2763 self._wizard.jumpPage("fuel", fromPageShift = 1)
2764
2765 def _getPlan(self):
2766 """Get the flight plan data for SimBrief."""
2767 plan = {
2768 "airline": "MAH",
2769 "selcal": "XXXX",
2770 "fuelfactor": "P000",
2771 "contpct": "0.05",
2772 "resvrule": "45",
2773 "taxiout": "10",
2774 "taxiin": "10",
2775 "civalue": "AUTO"
2776 }
2777
2778 wizard = self._wizard
2779 gui = wizard.gui
2780
2781 loginResult = wizard.loginResult
2782 plan["cpt"] = loginResult.pilotName
2783 plan["pid"] = loginResult.pilotID
2784
2785 bookedFlight = wizard.bookedFlight
2786 plan["fltnum"] = wizard.bookedFlight.callsign[2:]
2787 plan["type"] = const.icaoCodes[bookedFlight.aircraftType]
2788 plan["orig"] = bookedFlight.departureICAO
2789 plan["dest"] = bookedFlight.arrivalICAO
2790 plan["reg"] = bookedFlight.tailNumber
2791 plan["fin"] = bookedFlight.tailNumber[3:]
2792 plan["pax"] = str(bookedFlight.numPassengers)
2793
2794 departureTime = bookedFlight.departureTime
2795 plan["date"] = "%d%s%d" % (departureTime.day,
2796 SimBriefSetupPage.monthNum2Name[departureTime.month-1],
2797 departureTime.year%100)
2798 plan["deph"] = str(departureTime.hour)
2799 plan["depm"] = str(departureTime.minute)
2800
2801 arrivalTime = bookedFlight.arrivalTime
2802 plan["steh"] = str(arrivalTime.hour)
2803 plan["stem"] = str(arrivalTime.minute)
2804
2805 plan["manualzfw"] = str(wizard.zfw / 1000.0)
2806 plan["cargo"] = str((wizard.bagWeight + wizard.cargoWeight + wizard.mailWeight)/1000.0)
2807
2808 plan["route"] = wizard.route
2809 plan["fl"] = str(wizard.filedCruiseAltitude)
2810 plan["altn"] = wizard.alternate
2811
2812 plan["addedfuel"] = str(self._extraFuel.get_int() / 1000.0)
2813 plan["origrwy"] = self._takeoffRunway.get_text()
2814 plan["destrwy"] = self._landingRunway.get_text()
2815
2816 for (key, control) in [("climb", self._climbProfile),
2817 ("cruise", self._cruiseProfile),
2818 ("descent", self._descentProfile)]:
2819 model = control.get_model()
2820 active = control.get_active_iter()
2821 value = model.get_value(active, 0)
2822 plan[key] = value
2823
2824 return plan
2825
2826 def _upperChanged(self, entry, arg = None):
2827 """Called when the value of some entry widget has changed and the value
2828 should be converted to uppercase."""
2829 entry.set_text(entry.get_text().upper())
2830
2831#-----------------------------------------------------------------------------
2832
2833class SimBriefingPage(Page):
2834 """Page to display the SimBrief HTML briefing."""
2835 class BrowserLifeSpanHandler(object):
2836 """The life-span handler of a browser."""
2837 def __init__(self, simBriefingPage):
2838 """Construct the life-span handler for the given page."""
2839 self._simBriefingPage = simBriefingPage
2840
2841 def OnBeforeClose(self, browser):
2842 """Called before closing the browser."""
2843 self._simBriefingPage._invalidateBrowser()
2844
2845 def __init__(self, wizard):
2846 """Construct the setup page."""
2847
2848 super(SimBriefingPage, self).__init__(wizard, "simbrief_result",
2849 xstr("simbrief_result_title"), "")
2850
2851 self._alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2852 xscale = 1.0, yscale = 1.0)
2853
2854 self._container = cef.getContainer()
2855 self._alignment.add(self._container)
2856
2857 self.setMainWidget(self._alignment)
2858
2859 self._browser = None
2860
2861 self.addCancelFlightButton()
2862
2863 self.addPreviousButton(clicked = self._backClicked)
2864
2865 self._button = self.addNextButton(clicked = self._forwardClicked)
2866 self._button.set_label(xstr("briefing_button"))
2867 self._button.set_has_tooltip(False)
2868 self._button.set_use_stock(False)
2869
2870 def activate(self):
2871 """Activate the SimBrief flight plan page"""
2872 if self._browser is None:
2873 self._startBrowser()
2874 else:
2875 self._browser.Reload()
2876
2877 def grabDefault(self):
2878 """If the page has a default button, make it the default one."""
2879 super(SimBriefingPage, self).grabDefault()
2880
2881 if self._browser is None:
2882 self._startBrowser()
2883
2884 def _backClicked(self, button):
2885 """Called when the Back button has been pressed."""
2886 self.goBack()
2887
2888 def _forwardClicked(self, button):
2889 """Called when the Forward button has been pressed."""
2890 if not self._completed:
2891 self._button.set_label(xstr("button_next"))
2892 self._button.set_tooltip_text(xstr("button_next_tooltip"))
2893 self._wizard.usingSimBrief = True
2894 self.complete()
2895
2896 self._wizard.nextPage()
2897
2898 def _startBrowser(self):
2899 """Start the browser.
2900
2901 If a container is needed, create one."""
2902 if self._container is None:
2903 self._container = cef.getContainer()
2904 self._alignment.add(self._container)
2905
2906 url = "file://" + SimBriefSetupPage.getHTMLFilePath()
2907 self._browser = cef.startInContainer(self._container, url)
2908
2909 lifeSpanHandler = SimBriefingPage.BrowserLifeSpanHandler(self)
2910 self._browser.SetClientHandler(lifeSpanHandler)
2911
2912 def _invalidateBrowser(self):
2913 """Invalidate the browser (and associated stuff)."""
2914 self._alignment.remove(self._container)
2915 self._container = None
2916 self._browser = None
2917
2918#-----------------------------------------------------------------------------
2919
2920class FuelTank(gtk.VBox):
2921 """Widget for the fuel tank."""
2922 def __init__(self, fuelTank, name, capacity, currentWeight):
2923 """Construct the widget for the tank with the given name."""
2924 super(FuelTank, self).__init__()
2925
2926 self._enabled = True
2927 self.fuelTank = fuelTank
2928 self.capacity = capacity
2929 self.currentWeight = currentWeight
2930 self.expectedWeight = currentWeight
2931
2932 label = gtk.Label("<b>" + name + "</b>")
2933 label.set_use_markup(True)
2934 label.set_use_underline(True)
2935 label.set_justify(JUSTIFY_CENTER)
2936 label.set_alignment(0.5, 1.0)
2937 self.pack_start(label, False, False, 4)
2938
2939 self._tankFigure = gtk.EventBox()
2940 self._tankFigure.set_size_request(38, -1)
2941 self._tankFigure.set_visible_window(False)
2942 self._tankFigure.set_tooltip_markup(xstr("fuel_tank_tooltip"))
2943
2944 if pygobject:
2945 self._tankFigure.connect("draw", self._drawTankFigure)
2946 else:
2947 self._tankFigure.connect("expose_event", self._drawTankFigure)
2948 self._tankFigure.connect("button_press_event", self._buttonPressed)
2949 self._tankFigure.connect("motion_notify_event", self._motionNotify)
2950 self._tankFigure.connect("scroll-event", self._scrolled)
2951
2952 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2953 xscale = 0.0, yscale = 1.0)
2954 alignment.add(self._tankFigure)
2955
2956 self.pack_start(alignment, True, True, 4)
2957
2958 self._expectedButton = gtk.SpinButton()
2959 self._expectedButton.set_numeric(True)
2960 self._expectedButton.set_range(0, self.capacity)
2961 self._expectedButton.set_increments(10, 100)
2962 self._expectedButton.set_value(currentWeight)
2963 self._expectedButton.set_alignment(1.0)
2964 self._expectedButton.set_width_chars(5)
2965 self._expectedButton.connect("value-changed", self._expectedChanged)
2966
2967 label.set_mnemonic_widget(self._expectedButton)
2968
2969 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2970 xscale = 0.0, yscale = 1.0)
2971 alignment.add(self._expectedButton)
2972 self.pack_start(alignment, False, False, 4)
2973
2974 def setCurrent(self, currentWeight):
2975 """Set the current weight."""
2976 self.currentWeight = currentWeight
2977 self._redraw()
2978
2979 def isCorrect(self):
2980 """Determine if the contents of the fuel tank are as expected"""
2981 return abs(self.expectedWeight - self.currentWeight)<=1
2982
2983 def disable(self):
2984 """Disable the fuel tank."""
2985 self._expectedButton.set_sensitive(False)
2986 self._enabled = False
2987
2988 def _redraw(self):
2989 """Redraw the tank figure."""
2990 self._tankFigure.queue_draw()
2991
2992 def _drawTankFigure(self, tankFigure, eventOrContext):
2993 """Draw the tank figure."""
2994 triangleSize = 5
2995
2996 context = eventOrContext if pygobject else tankFigure.window.cairo_create()
2997 (xOffset, yOffset) = (0, 0) if pygobject \
2998 else (tankFigure.allocation.x, tankFigure.allocation.y)
2999
3000 width = tankFigure.get_allocated_width() if pygobject \
3001 else tankFigure.allocation.width
3002 height = tankFigure.get_allocated_height() if pygobject \
3003 else tankFigure.allocation.height
3004
3005 rectangleX0 = triangleSize
3006 rectangleY0 = triangleSize
3007 rectangleX1 = width - 1 - triangleSize
3008 rectangleY1 = height - 1 - triangleSize
3009 rectangleLineWidth = 2.0
3010
3011 context.set_source_rgb(0.0, 0.0, 0.0)
3012 context.set_line_width(rectangleLineWidth)
3013 context.rectangle(xOffset + rectangleX0 + rectangleLineWidth/2,
3014 yOffset + rectangleY0 + rectangleLineWidth/2,
3015 rectangleX1 - rectangleX0 - rectangleLineWidth,
3016 rectangleY1 - rectangleY0 - rectangleLineWidth)
3017 context.stroke()
3018
3019 rectangleInnerLeft = rectangleX0 + rectangleLineWidth
3020 rectangleInnerRight = rectangleX1 - rectangleLineWidth
3021 self._rectangleInnerTop = rectangleInnerTop = rectangleY0 + rectangleLineWidth
3022 self._rectangleInnerBottom = rectangleInnerBottom = rectangleY1 - rectangleLineWidth
3023
3024 rectangleInnerWidth = rectangleInnerRight - rectangleInnerLeft
3025 rectangleInnerHeight = rectangleInnerBottom - rectangleInnerTop
3026
3027 context.set_source_rgb(1.0, 0.9, 0.6)
3028 currentHeight = self.currentWeight * rectangleInnerHeight / self.capacity
3029 currentX = rectangleInnerTop + rectangleInnerHeight - currentHeight
3030 context.rectangle(xOffset + rectangleInnerLeft,
3031 yOffset + rectangleInnerTop +
3032 rectangleInnerHeight - currentHeight,
3033 rectangleInnerWidth, currentHeight)
3034 context.fill()
3035
3036 expectedHeight = self.expectedWeight * rectangleInnerHeight / self.capacity
3037 expectedY = rectangleInnerTop + rectangleInnerHeight - expectedHeight
3038
3039 context.set_line_width(1.5)
3040 context.set_source_rgb(0.0, 0.85, 0.85)
3041 context.move_to(xOffset + rectangleX0, yOffset + expectedY)
3042 context.line_to(xOffset + rectangleX1, yOffset + expectedY)
3043 context.stroke()
3044
3045 context.set_line_width(0.0)
3046 context.move_to(xOffset + 0, yOffset + expectedY - triangleSize)
3047 context.line_to(xOffset + 0, yOffset + expectedY + triangleSize)
3048 context.line_to(xOffset + rectangleX0 + 1, yOffset + expectedY)
3049 context.line_to(xOffset + 0, yOffset + expectedY - triangleSize)
3050 context.fill()
3051
3052 context.set_line_width(0.0)
3053 context.move_to(xOffset + width, yOffset + expectedY - triangleSize)
3054 context.line_to(xOffset + width, yOffset + expectedY + triangleSize)
3055 context.line_to(xOffset + rectangleX1 - 1, yOffset + expectedY)
3056 context.line_to(xOffset + width, yOffset + expectedY - triangleSize)
3057 context.fill()
3058
3059 return True
3060
3061 def _setExpectedFromY(self, y):
3062 """Set the expected weight from the given Y-coordinate."""
3063 level = (self._rectangleInnerBottom - y) / \
3064 (self._rectangleInnerBottom - self._rectangleInnerTop)
3065 level = min(1.0, max(0.0, level))
3066 self._expectedButton.set_value(level * self.capacity)
3067
3068 def _buttonPressed(self, tankFigure, event):
3069 """Called when a button is pressed in the figure.
3070
3071 The expected level will be set there."""
3072 if self._enabled and event.button==1:
3073 self._setExpectedFromY(event.y)
3074
3075 def _motionNotify(self, tankFigure, event):
3076 """Called when the mouse pointer moves within the area of a tank figure."""
3077 if self._enabled and event.state==BUTTON1_MASK:
3078 self._setExpectedFromY(event.y)
3079
3080 def _scrolled(self, tankFigure, event):
3081 """Called when a scroll event is received."""
3082 if self._enabled:
3083 increment = 1 if event.state==CONTROL_MASK \
3084 else 100 if event.state==SHIFT_MASK \
3085 else 10 if event.state==0 else 0
3086 if increment!=0:
3087 if event.direction==SCROLL_DOWN:
3088 increment *= -1
3089 self._expectedButton.spin(SPIN_USER_DEFINED, increment)
3090
3091 def _expectedChanged(self, spinButton):
3092 """Called when the expected value has changed."""
3093 self.expectedWeight = spinButton.get_value_as_int()
3094 self._redraw()
3095
3096#-----------------------------------------------------------------------------
3097
3098class FuelPage(Page):
3099 """The page containing the fuel tank filling."""
3100 _pumpStep = 0.02
3101
3102 def __init__(self, wizard):
3103 """Construct the page."""
3104 super(FuelPage, self).__init__(wizard, "fuel",
3105 xstr("fuel_title"),
3106 xstr("fuel_help_pre") +
3107 xstr("fuel_help_post"),
3108 completedHelp = xstr("fuel_chelp"))
3109
3110 self._fuelTanks = []
3111 self._fuelTable = None
3112 self._fuelAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3113 xscale = 0.0, yscale = 1.0)
3114 self.setMainWidget(self._fuelAlignment)
3115
3116 tankData = [(tank, 2500, 3900) for tank in acft.mostFuelTanks]
3117 self._setupTanks(tankData)
3118
3119 self.addCancelFlightButton()
3120
3121 self._backButton = self.addPreviousButton(clicked = self._backClicked)
3122 self._button = self.addNextButton(clicked = self._forwardClicked)
3123
3124 self._pumpIndex = 0
3125
3126 def activate(self):
3127 """Activate the page."""
3128 self._setupTanks(self._wizard._fuelData)
3129
3130 aircraft = self._wizard.gui.flight.aircraft
3131 minLandingFuel = aircraft.minLandingFuel
3132 recommendedLandingFuel = aircraft.recommendedLandingFuel
3133
3134 middleHelp = "" if minLandingFuel is None else \
3135 (xstr("fuel_help_min") % (minLandingFuel,)) \
3136 if recommendedLandingFuel is None else \
3137 (xstr("fuel_help_min_rec") % (minLandingFuel,
3138 recommendedLandingFuel))
3139 self.setHelp(xstr("fuel_help_pre") + middleHelp + xstr("fuel_help_post"))
3140
3141 def finalize(self):
3142 """Finalize the page."""
3143 for fuelTank in self._fuelTanks:
3144 fuelTank.disable()
3145
3146 def _backClicked(self, button):
3147 """Called when the Back button is pressed."""
3148 self.goBack()
3149
3150 def _forwardClicked(self, button):
3151 """Called when the forward button is clicked."""
3152 if not self._completed:
3153 self._pumpIndex = 0
3154 self._wizard.gui.beginBusy(xstr("fuel_pump_busy"))
3155 self._pump()
3156 elif self._wizard.usingSimBrief:
3157 self._wizard.jumpPage("takeoff")
3158 else:
3159 self._wizard.jumpPage("briefing1")
3160
3161 def _setupTanks(self, tankData):
3162 """Setup the tanks for the given data."""
3163 numTanks = len(tankData)
3164 if self._fuelTable is not None:
3165 self._fuelAlignment.remove(self._fuelTable)
3166
3167 self._fuelTanks = []
3168 self._fuelTable = gtk.Table(numTanks, 1)
3169 self._fuelTable.set_col_spacings(16)
3170 index = 0
3171 for (tank, current, capacity) in tankData:
3172 fuelTank = FuelTank(tank,
3173 xstr("fuel_tank_" +
3174 const.fuelTank2string(tank)),
3175 capacity, current)
3176 self._fuelTable.attach(fuelTank, index, index+1, 0, 1)
3177 self._fuelTanks.append(fuelTank)
3178 index += 1
3179
3180 self._fuelAlignment.add(self._fuelTable)
3181 self.show_all()
3182
3183 def _pump(self):
3184 """Perform one step of pumping.
3185
3186 It is checked, if the current tank's contents are of the right
3187 quantity. If not, it is filled one step further to the desired
3188 contents. Otherwise the next tank is started. If all tanks are are
3189 filled, the next page is selected."""
3190 numTanks = len(self._fuelTanks)
3191
3192 fuelTank = None
3193 while self._pumpIndex < numTanks:
3194 fuelTank = self._fuelTanks[self._pumpIndex]
3195 if fuelTank.isCorrect():
3196 self._pumpIndex += 1
3197 fuelTank = None
3198 else:
3199 break
3200
3201 if fuelTank is None:
3202 self._wizard.gui.endBusy()
3203 if self._wizard.usingSimBrief:
3204 self._wizard.gui.startMonitoring()
3205 self._wizard.jumpPage("takeoff")
3206 else:
3207 bookedFlight = self._wizard._bookedFlight
3208 self._wizard.gui.beginBusy(xstr("route_down_notams"))
3209 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
3210 bookedFlight.departureICAO,
3211 bookedFlight.arrivalICAO)
3212 startSound(const.SOUND_NOTAM)
3213 else:
3214 currentLevel = fuelTank.currentWeight / fuelTank.capacity
3215 expectedLevel = fuelTank.expectedWeight / fuelTank.capacity
3216 if currentLevel<expectedLevel:
3217 currentLevel += FuelPage._pumpStep
3218 if currentLevel>expectedLevel: currentLevel = expectedLevel
3219 else:
3220 currentLevel -= FuelPage._pumpStep
3221 if currentLevel<expectedLevel: currentLevel = expectedLevel
3222 fuelTank.setCurrent(currentLevel * fuelTank.capacity)
3223 self._wizard.gui.simulator.setFuelLevel([(fuelTank.fuelTank,
3224 currentLevel)])
3225 gobject.timeout_add(50, self._pump)
3226
3227 def _notamsCallback(self, returned, result):
3228 """Callback for the NOTAMs."""
3229 gobject.idle_add(self._handleNOTAMs, returned, result)
3230
3231 def _handleNOTAMs(self, returned, result):
3232 """Handle the NOTAMs."""
3233 if returned:
3234 self._wizard._departureNOTAMs = result.departureNOTAMs
3235 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
3236 else:
3237 self._wizard._departureNOTAMs = None
3238 self._wizard._arrivalNOTAMs = None
3239
3240 bookedFlight = self._wizard._bookedFlight
3241 self._wizard.gui.beginBusy(xstr("route_down_metars"))
3242 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
3243 [bookedFlight.departureICAO,
3244 bookedFlight.arrivalICAO])
3245
3246 def _metarsCallback(self, returned, result):
3247 """Callback for the METARs."""
3248 gobject.idle_add(self._handleMETARs, returned, result)
3249
3250 def _handleMETARs(self, returned, result):
3251 """Handle the METARs."""
3252 self._wizard._departureMETAR = None
3253 self._wizard._arrivalMETAR = None
3254 bookedFlight = self._wizard._bookedFlight
3255 if returned:
3256 if bookedFlight.departureICAO in result.metars:
3257 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
3258 if bookedFlight.arrivalICAO in result.metars:
3259 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
3260
3261 self._wizard.gui.endBusy()
3262 self._backButton.set_sensitive(True)
3263 self._button.set_sensitive(True)
3264 self._wizard.nextPage()
3265
3266#-----------------------------------------------------------------------------
3267
3268class BriefingPage(Page):
3269 """Page for the briefing."""
3270 def __init__(self, wizard, departure):
3271 """Construct the briefing page."""
3272 self._departure = departure
3273
3274 number = 1 if departure else 2
3275
3276 title = xstr("briefing_title") % (number,
3277 xstr("briefing_departure")
3278 if departure
3279 else xstr("briefing_arrival"))
3280 super(BriefingPage, self).__init__(wizard,
3281 "briefing%d" % (number,),
3282 title, xstr("briefing_help"),
3283 completedHelp = xstr("briefing_chelp"))
3284
3285 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3286 xscale = 1.0, yscale = 1.0)
3287
3288 mainBox = gtk.VBox()
3289 alignment.add(mainBox)
3290 self.setMainWidget(alignment)
3291
3292 self._notamsFrame = gtk.Frame()
3293 self._notamsFrame.set_label(xstr("briefing_notams_init"))
3294 scrolledWindow = gtk.ScrolledWindow()
3295 scrolledWindow.set_size_request(-1, 128)
3296 # FIXME: these constants should be in common
3297 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
3298 else gtk.POLICY_AUTOMATIC,
3299 gtk.PolicyType.AUTOMATIC if pygobject
3300 else gtk.POLICY_AUTOMATIC)
3301 self._notams = gtk.TextView()
3302 self._notams.set_editable(False)
3303 self._notams.set_accepts_tab(False)
3304 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
3305 scrolledWindow.add(self._notams)
3306 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
3307 xscale = 1.0, yscale = 1.0)
3308 alignment.set_padding(padding_top = 4, padding_bottom = 0,
3309 padding_left = 0, padding_right = 0)
3310 alignment.add(scrolledWindow)
3311 self._notamsFrame.add(alignment)
3312 mainBox.pack_start(self._notamsFrame, True, True, 4)
3313
3314 self._metarFrame = gtk.Frame()
3315 self._metarFrame.set_label(xstr("briefing_metar_init"))
3316 scrolledWindow = gtk.ScrolledWindow()
3317 scrolledWindow.set_size_request(-1, 32)
3318 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
3319 else gtk.POLICY_AUTOMATIC,
3320 gtk.PolicyType.AUTOMATIC if pygobject
3321 else gtk.POLICY_AUTOMATIC)
3322
3323 self._updatingMETAR = False
3324
3325 self._metar = gtk.TextView()
3326 self._metar.set_accepts_tab(False)
3327 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
3328 self._metar.get_buffer().connect("changed", self._metarChanged)
3329 self._metar.get_buffer().connect_after("insert-text", self._metarInserted)
3330 scrolledWindow.add(self._metar)
3331 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
3332 xscale = 1.0, yscale = 1.0)
3333 alignment.set_padding(padding_top = 4, padding_bottom = 0,
3334 padding_left = 0, padding_right = 0)
3335 alignment.add(scrolledWindow)
3336 self._metarFrame.add(alignment)
3337 mainBox.pack_start(self._metarFrame, True, True, 4)
3338 self.metarEdited = False
3339
3340 self.addCancelFlightButton()
3341
3342 self.addPreviousButton(clicked = self._backClicked)
3343 self._button = self.addNextButton(clicked = self._forwardClicked)
3344
3345 @property
3346 def metar(self):
3347 """Get the METAR on the page."""
3348 buffer = self._metar.get_buffer()
3349 return buffer.get_text(buffer.get_start_iter(),
3350 buffer.get_end_iter(), True)
3351
3352 def setMETAR(self, metar):
3353 """Set the METAR."""
3354 self._metar.get_buffer().set_text(metar)
3355 self.metarEdited = False
3356
3357 def changeMETAR(self, metar):
3358 """Change the METAR as a result of an edit on one of the other
3359 pages."""
3360 self._updatingMETAR = True
3361 self._metar.get_buffer().set_text(metar)
3362 self._updatingMETAR = False
3363
3364 self._updateButton()
3365 self.metarEdited = True
3366
3367 def activate(self):
3368 """Activate the page."""
3369 if not self._departure:
3370 self._button.set_label(xstr("briefing_button"))
3371 self._button.set_has_tooltip(False)
3372 self._button.set_use_stock(False)
3373
3374 bookedFlight = self._wizard._bookedFlight
3375
3376 icao = bookedFlight.departureICAO if self._departure \
3377 else bookedFlight.arrivalICAO
3378 notams = self._wizard._departureNOTAMs if self._departure \
3379 else self._wizard._arrivalNOTAMs
3380 metar = self._wizard._departureMETAR if self._departure \
3381 else self._wizard._arrivalMETAR
3382
3383 self._notamsFrame.set_label(xstr("briefing_notams_template") % (icao,))
3384 buffer = self._notams.get_buffer()
3385 if notams is None:
3386 buffer.set_text(xstr("briefing_notams_failed"))
3387 elif not notams:
3388 buffer.set_text(xstr("briefing_notams_missing"))
3389 else:
3390 s = ""
3391 for notam in notams:
3392 s += str(notam)
3393 s += "-------------------- * --------------------\n"
3394 buffer.set_text(s)
3395
3396 self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
3397 buffer = self._metar.get_buffer()
3398 self._updatingMETAR = True
3399 if metar is None:
3400 buffer.set_text("")
3401 self.setHelp(xstr("briefing_help_nometar"))
3402 else:
3403 buffer.set_text(metar)
3404 self._updatingMETAR = False
3405 self._updateButton()
3406
3407 label = self._metarFrame.get_label_widget()
3408 label.set_use_underline(True)
3409 label.set_mnemonic_widget(self._metar)
3410
3411 self.metarEdited = False
3412
3413 def _backClicked(self, button):
3414 """Called when the Back button is pressed."""
3415 self.goBack()
3416
3417 def _forwardClicked(self, button):
3418 """Called when the forward button is clicked."""
3419 if not self._departure:
3420 if not self._completed:
3421 self._wizard.gui.startMonitoring()
3422 self._button.set_label(xstr("button_next"))
3423 self._button.set_tooltip_text(xstr("button_next_tooltip"))
3424 self.complete()
3425
3426 self._wizard.nextPage()
3427
3428 def _metarChanged(self, buffer):
3429 """Called when the METAR has changed."""
3430 print "BriefingPage.metarChanged", self._updatingMETAR
3431 if not self._updatingMETAR:
3432 self.metarEdited = True
3433 self._updateButton()
3434 metar = buffer.get_text(buffer.get_start_iter(),
3435 buffer.get_end_iter(), True)
3436 self._wizard.metarChanged(metar, self)
3437
3438 def _metarInserted(self, textBuffer, iter, text, length):
3439 """Called when new characters are inserted into the METAR.
3440
3441 It uppercases all characters."""
3442 print "BriefingPage.metarInserted", self._updatingMETAR
3443 if not self._updatingMETAR:
3444 self._updatingMETAR = True
3445
3446 iter1 = iter.copy()
3447 iter1.backward_chars(length)
3448 textBuffer.delete(iter, iter1)
3449
3450 textBuffer.insert(iter, text.upper())
3451
3452 self._updatingMETAR = False
3453
3454 def _updateButton(self):
3455 """Update the sensitivity of the Next button based on the contents of
3456 the METAR field."""
3457 buffer = self._metar.get_buffer()
3458 self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
3459 buffer.get_end_iter(),
3460 True)!="")
3461
3462
3463#-----------------------------------------------------------------------------
3464
3465class TakeoffPage(Page):
3466 """Page for entering the takeoff data."""
3467 def __init__(self, wizard):
3468 """Construct the takeoff page."""
3469 super(TakeoffPage, self).__init__(wizard, "takeoff",
3470 xstr("takeoff_title"),
3471 xstr("takeoff_help"),
3472 completedHelp = xstr("takeoff_chelp"))
3473
3474 self._forwardAllowed = False
3475
3476 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
3477 xscale = 0.0, yscale = 0.0)
3478
3479 table = gtk.Table(9, 24)
3480 table.set_row_spacings(4)
3481 table.set_col_spacings(16)
3482 table.set_homogeneous(False)
3483 alignment.add(table)
3484 self.setMainWidget(alignment)
3485
3486 row = 0
3487
3488 label = gtk.Label(xstr("takeoff_metar"))
3489 label.set_use_underline(True)
3490 label.set_alignment(0.0, 0.5)
3491 table.attach(label, 0, 1, row, row+1)
3492
3493 self._metar = gtk.Entry()
3494 self._metar.set_width_chars(40)
3495 self._metar.set_tooltip_text(xstr("takeoff_metar_tooltip"))
3496 self._metar.connect("changed", self._metarChanged)
3497 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
3498 table.attach(self._metar, 1, 24, row, row+1)
3499 label.set_mnemonic_widget(self._metar)
3500
3501 self._updatingMETAR = False
3502
3503 row += 1
3504
3505 label = gtk.Label(xstr("takeoff_runway"))
3506 label.set_use_underline(True)
3507 label.set_alignment(0.0, 0.5)
3508 table.attach(label, 0, 1, row, row+1)
3509
3510 self._runway = gtk.Entry()
3511 self._runway.set_width_chars(10)
3512 self._runway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
3513 self._runway.connect("changed", self._upperChanged)
3514 table.attach(self._runway, 1, 3, row, row+1)
3515 label.set_mnemonic_widget(self._runway)
3516
3517 row += 1
3518
3519 label = gtk.Label(xstr("takeoff_sid"))
3520 label.set_use_underline(True)
3521 label.set_alignment(0.0, 0.5)
3522 table.attach(label, 0, 1, row, row+1)
3523
3524 if pygobject:
3525 self._sid = gtk.ComboBox.new_with_model_and_entry(comboModel)
3526 else:
3527 self._sid = gtk.ComboBoxEntry(comboModel)
3528
3529 self._sid.set_entry_text_column(0)
3530 self._sid.get_child().set_width_chars(10)
3531 self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
3532 self._sid.connect("changed", self._upperChangedComboBox)
3533 table.attach(self._sid, 1, 3, row, row+1)
3534 label.set_mnemonic_widget(self._sid)
3535
3536 row += 1
3537
3538 label = gtk.Label(xstr("takeoff_v1"))
3539 label.set_use_markup(True)
3540 label.set_use_underline(True)
3541 label.set_alignment(0.0, 0.5)
3542 table.attach(label, 0, 1, row, row+1)
3543
3544 self._v1 = IntegerEntry()
3545 self._v1.set_width_chars(4)
3546 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip_knots"))
3547 self._v1.connect("integer-changed", self._valueChanged)
3548 table.attach(self._v1, 2, 3, row, row+1)
3549 label.set_mnemonic_widget(self._v1)
3550
3551 self._v1Unit = gtk.Label(xstr("label_knots"))
3552 self._v1Unit.set_alignment(0.0, 0.5)
3553 table.attach(self._v1Unit, 3, 4, row, row+1)
3554
3555 row += 1
3556
3557 label = gtk.Label(xstr("takeoff_vr"))
3558 label.set_use_markup(True)
3559 label.set_use_underline(True)
3560 label.set_alignment(0.0, 0.5)
3561 table.attach(label, 0, 1, row, row+1)
3562
3563 self._vr = IntegerEntry()
3564 self._vr.set_width_chars(4)
3565 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip_knots"))
3566 self._vr.connect("integer-changed", self._valueChanged)
3567 table.attach(self._vr, 2, 3, row, row+1)
3568 label.set_mnemonic_widget(self._vr)
3569
3570 self._vrUnit = gtk.Label(xstr("label_knots"))
3571 self._vrUnit.set_alignment(0.0, 0.5)
3572 table.attach(self._vrUnit, 3, 4, row, row+1)
3573
3574 row += 1
3575
3576 label = gtk.Label(xstr("takeoff_v2"))
3577 label.set_use_markup(True)
3578 label.set_use_underline(True)
3579 label.set_alignment(0.0, 0.5)
3580 table.attach(label, 0, 1, row, row+1)
3581
3582 self._v2 = IntegerEntry()
3583 self._v2.set_width_chars(4)
3584 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip_knots"))
3585 self._v2.connect("integer-changed", self._valueChanged)
3586 table.attach(self._v2, 2, 3, row, row+1)
3587 label.set_mnemonic_widget(self._v2)
3588
3589 self._v2Unit = gtk.Label(xstr("label_knots"))
3590 self._v2Unit.set_alignment(0.0, 0.5)
3591 table.attach(self._v2Unit, 3, 4, row, row+1)
3592
3593 row += 1
3594
3595 self._derateType = acft.DERATE_NONE
3596
3597 self._derateLabel = gtk.Label()
3598 self._derateLabel.set_use_underline(True)
3599 self._derateLabel.set_markup(xstr("takeoff_derate_tupolev"))
3600 self._derateLabel.set_alignment(0.0, 0.5)
3601 table.attach(self._derateLabel, 0, 1, row, row+1)
3602
3603 self._derate = gtk.Alignment()
3604 table.attach(self._derate, 2, 4, row, row+1)
3605 self._derateWidget = None
3606 self._derateEntry = None
3607 self._derateUnit = None
3608 self._derateButtons = None
3609
3610 row += 1
3611
3612 self._antiIceOn = gtk.CheckButton(xstr("takeoff_antiice"))
3613 self._antiIceOn.set_use_underline(True)
3614 self._antiIceOn.set_tooltip_text(xstr("takeoff_antiice_tooltip"))
3615 table.attach(self._antiIceOn, 2, 4, row, row+1)
3616
3617 row += 1
3618
3619 self._rto = gtk.CheckButton(xstr("takeoff_rto"))
3620 self._rto.set_use_underline(True)
3621 self._rto.set_tooltip_text(xstr("takeoff_rto_tooltip"))
3622 self._rto.connect("toggled", self._rtoToggled)
3623 table.attach(self._rto, 2, 4, row, row+1, ypadding = 8)
3624
3625 self.addCancelFlightButton()
3626
3627 self.addPreviousButton(clicked = self._backClicked)
3628
3629 self._button = self.addNextButton(clicked = self._forwardClicked)
3630
3631 self._active = False
3632
3633 @property
3634 def runway(self):
3635 """Get the runway."""
3636 return self._runway.get_text()
3637
3638 @property
3639 def sid(self):
3640 """Get the SID."""
3641 text = self._sid.get_child().get_text()
3642 return text if self._sid.get_active()!=0 and text and text!="N/A" \
3643 else None
3644
3645 @property
3646 def v1(self):
3647 """Get the v1 speed."""
3648 return self._v1.get_int()
3649
3650 @property
3651 def vr(self):
3652 """Get the vr speed."""
3653 return self._vr.get_int()
3654
3655 @property
3656 def v2(self):
3657 """Get the v2 speed."""
3658 return self._v2.get_int()
3659
3660 @property
3661 def derate(self):
3662 """Get the derate value, if any."""
3663 if self._derateWidget is None:
3664 return None
3665 if self._derateType==acft.DERATE_BOEING:
3666 derate = self._derateEntry.get_text()
3667 return derate if derate else None
3668 elif self._derateType==acft.DERATE_EPR:
3669 derate = self._derateWidget.get_text()
3670 return derate if derate else None
3671 elif self._derateType==acft.DERATE_TUPOLEV:
3672 return acft.DERATE_TUPOLEV_NOMINAL \
3673 if self._derateButtons[0].get_active() \
3674 else acft.DERATE_TUPOLEV_TAKEOFF
3675 elif self._derateType==acft.DERATE_B462:
3676 return self._derateWidget.get_active()
3677 else:
3678 return None
3679
3680 @property
3681 def antiIceOn(self):
3682 """Get whether the anti-ice system has been turned on."""
3683 return self._antiIceOn.get_active()
3684
3685 @antiIceOn.setter
3686 def antiIceOn(self, value):
3687 """Set the anti-ice indicator."""
3688 self._antiIceOn.set_active(value)
3689
3690 @property
3691 def rtoIndicated(self):
3692 """Get whether the pilot has indicated if there was an RTO."""
3693 return self._rto.get_active()
3694
3695 def activate(self):
3696 """Activate the page."""
3697 print "TakeoffPage.activate"
3698
3699 self._updatingMETAR = True
3700 self._metar.get_buffer().set_text(self._wizard.departureMETAR, -1)
3701 self._updatingMETAR = False
3702
3703 if self._wizard.takeoffRunway is None:
3704 self._runway.set_text("")
3705 else:
3706 self._runway.set_text(self._wizard.takeoffRunway)
3707 self._runway.set_sensitive(True)
3708 self._sid.set_active(0)
3709 self._sid.set_sensitive(True)
3710 self._v1.set_int(None)
3711 self._v1.set_sensitive(True)
3712 self._vr.set_int(None)
3713 self._vr.set_sensitive(True)
3714 self._v2.set_int(None)
3715 self._v2.set_sensitive(True)
3716
3717 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
3718 speedUnit = xstr("label" + i18nSpeedUnit)
3719 self._v1Unit.set_text(speedUnit)
3720 self._vrUnit.set_text(speedUnit)
3721 self._v2Unit.set_text(speedUnit)
3722
3723 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
3724 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
3725 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
3726
3727 self._derateType = self._wizard.gui.flight.aircraft.derateType
3728
3729 self._setupDerateWidget()
3730
3731 self._rto.set_active(False)
3732 self._rto.set_sensitive(False)
3733
3734 self._button.set_sensitive(False)
3735 self._forwardAllowed = False
3736
3737 self._active = True
3738
3739 def allowForward(self):
3740 """Allow going to the next page."""
3741 print "TakeoffPage.allowForward"
3742 self._forwardAllowed = True
3743 self._updateForwardButton()
3744
3745 def reset(self):
3746 """Reset the page if the wizard is reset."""
3747 print "TakeoffPage.reset"
3748
3749 super(TakeoffPage, self).reset()
3750 self._v1.reset()
3751 self._vr.reset()
3752 self._v2.reset()
3753 self._hasDerate = False
3754 self._antiIceOn.set_active(False)
3755 self._active = False
3756
3757 def setRTOEnabled(self, enabled):
3758 """Set the RTO checkbox enabled or disabled."""
3759 if not enabled:
3760 self._rto.set_active(False)
3761 self._rto.set_sensitive(enabled)
3762
3763 def changeMETAR(self, metar):
3764 """Change the METAR as a result of an edit on one of the other
3765 pages."""
3766 if self._active:
3767 print "TakeoffPage.changeMETAR"
3768 self._updatingMETAR = True
3769 self._metar.get_buffer().set_text(metar, -1)
3770 self._updatingMETAR = False
3771
3772 self._updateForwardButton()
3773
3774 def _updateForwardButton(self):
3775 """Update the sensitivity of the forward button based on some conditions."""
3776 sensitive = self._forwardAllowed and \
3777 self._metar.get_text()!="" and \
3778 self._runway.get_text()!="" and \
3779 self.sid is not None and \
3780 self.v1 is not None and \
3781 self.vr is not None and \
3782 self.v2 is not None and \
3783 self.v1 <= self.vr and \
3784 self.vr <= self.v2 and \
3785 (self._derateType==acft.DERATE_NONE or
3786 self.derate is not None)
3787
3788 print "TakeoffPage._updateForwardButton: forwardAllowed:", self._forwardAllowed, ", sensitive:", sensitive
3789 if self._forwardAllowed:
3790 print " METAR: ", self._metar.get_text()
3791 print " runway: ", self._runway.get_text()
3792 print " SID:", self.sid
3793 print " V1:", self.v1
3794 print " VR:", self.vr
3795 print " V2:", self.v2
3796 print " derateType:", self._derateType
3797 print " derate:", self.derate
3798
3799 self._button.set_sensitive(sensitive)
3800
3801 def _valueChanged(self, widget, arg = None):
3802 """Called when the value of some widget has changed."""
3803 print "TakeoffPage._valueChanged"
3804
3805 self._updateForwardButton()
3806
3807 def _upperChanged(self, entry, arg = None):
3808 """Called when the value of some entry widget has changed and the value
3809 should be converted to uppercase."""
3810 print "TakeoffPage._upperChanged"
3811 entry.set_text(entry.get_text().upper())
3812 self._valueChanged(entry, arg)
3813
3814 def _upperChangedComboBox(self, comboBox):
3815 """Called for combo box widgets that must be converted to uppercase."""
3816 entry = comboBox.get_child()
3817 if comboBox.get_active()==-1:
3818 entry.set_text(entry.get_text().upper())
3819 self._valueChanged(entry)
3820
3821 def _derateChanged(self, entry):
3822 """Called when the value of the derate is changed."""
3823 print "TakeoffPage._derateChanged"
3824 self._updateForwardButton()
3825
3826 def _rtoToggled(self, button):
3827 """Called when the RTO check button is toggled."""
3828 self._wizard.rtoToggled(button.get_active())
3829
3830 def _backClicked(self, button):
3831 """Called when the Back button is pressed."""
3832 self.goBack()
3833
3834 def _forwardClicked(self, button):
3835 """Called when the forward button is clicked."""
3836 aircraft = self._wizard.gui.flight.aircraft
3837 aircraft.updateV1R2()
3838 if self.derate is not None:
3839 aircraft.updateDerate()
3840 aircraft.updateTakeoffAntiIce()
3841 self._wizard.nextPage()
3842
3843 def _setupDerateWidget(self):
3844 """Setup the derate widget."""
3845 if self._derateWidget is not None:
3846 self._derate.remove(self._derateWidget)
3847
3848 if self._derateType==acft.DERATE_BOEING:
3849 self._derateLabel.set_text(xstr("takeoff_derate_boeing"))
3850 self._derateLabel.set_use_underline(True)
3851 self._derateLabel.set_sensitive(True)
3852
3853 self._derateEntry = gtk.Entry()
3854 self._derateEntry.set_width_chars(7)
3855 self._derateEntry.set_tooltip_text(xstr("takeoff_derate_boeing_tooltip"))
3856 self._derateEntry.set_alignment(1.0)
3857 self._derateEntry.connect("changed", self._derateChanged)
3858 self._derateLabel.set_mnemonic_widget(self._derateEntry)
3859
3860 self._derateUnit = gtk.Label("%")
3861 self._derateUnit.set_alignment(0.0, 0.5)
3862
3863 self._derateWidget = gtk.Table(3, 1)
3864 self._derateWidget.set_row_spacings(4)
3865 self._derateWidget.set_col_spacings(16)
3866 self._derateWidget.set_homogeneous(False)
3867
3868 self._derateWidget.attach(self._derateEntry, 0, 2, 0, 1)
3869 self._derateWidget.attach(self._derateUnit, 2, 3, 0, 1)
3870
3871 self._derate.add(self._derateWidget)
3872 elif self._derateType==acft.DERATE_EPR:
3873 self._derateLabel.set_text("_EPR:")
3874 self._derateLabel.set_use_underline(True)
3875 self._derateLabel.set_sensitive(True)
3876
3877 self._derateWidget = gtk.Entry()
3878 self._derateWidget.set_width_chars(7)
3879 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_epr_tooltip"))
3880 self._derateWidget.set_alignment(1.0)
3881 self._derateWidget.connect("changed", self._derateChanged)
3882 self._derateLabel.set_mnemonic_widget(self._derateWidget)
3883
3884 self._derate.add(self._derateWidget)
3885 elif self._derateType==acft.DERATE_TUPOLEV:
3886 self._derateLabel.set_text(xstr("takeoff_derate_tupolev"))
3887 self._derateLabel.set_use_underline(True)
3888 self._derateLabel.set_sensitive(True)
3889
3890 if pygobject:
3891 nominal = gtk.RadioButton.\
3892 new_with_label_from_widget(None,
3893 xstr("takeoff_derate_tupolev_nominal"))
3894 else:
3895 nominal = gtk.RadioButton(None,
3896 xstr("takeoff_derate_tupolev_nominal"))
3897 nominal.set_use_underline(True)
3898 nominal.set_tooltip_text(xstr("takeoff_derate_tupolev_nominal_tooltip"))
3899 nominal.connect("toggled", self._derateChanged)
3900
3901 if pygobject:
3902 takeoff = gtk.RadioButton.\
3903 new_with_label_from_widget(nominal,
3904 xstr("takeoff_derate_tupolev_takeoff"))
3905 else:
3906 takeoff = gtk.RadioButton(nominal,
3907 xstr("takeoff_derate_tupolev_takeoff"))
3908
3909 takeoff.set_use_underline(True)
3910 takeoff.set_tooltip_text(xstr("takeoff_derate_tupolev_takeoff_tooltip"))
3911 takeoff.connect("toggled", self._derateChanged)
3912
3913 self._derateButtons = [nominal, takeoff]
3914
3915 self._derateWidget = gtk.HBox()
3916 self._derateWidget.pack_start(nominal, False, False, 4)
3917 self._derateWidget.pack_start(takeoff, False, False, 4)
3918
3919 self._derate.add(self._derateWidget)
3920 elif self._derateType==acft.DERATE_B462:
3921 self._derateLabel.set_text("")
3922
3923 self._derateWidget = gtk.CheckButton(xstr("takeoff_derate_b462"))
3924 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_b462_tooltip"))
3925 self._derateWidget.set_use_underline(True)
3926 self._derate.add(self._derateWidget)
3927 else:
3928 self._derateWidget = None
3929 self._derateLabel.set_text("")
3930 self._derateLabel.set_sensitive(False)
3931
3932 def _metarChanged(self, entry):
3933 """Called when the METAR has changed."""
3934 print "TakeoffPage.metarChanged", self._updatingMETAR
3935 if not self._updatingMETAR:
3936 self._updateForwardButton()
3937 self._wizard.metarChanged(entry.get_text(), self)
3938
3939 def _metarInserted(self, buffer, position, text, length):
3940 """Called when new characters are inserted into the METAR.
3941
3942 It uppercases all characters."""
3943 print "TakeoffPage.metarInserted", self._updatingMETAR
3944 if not self._updatingMETAR:
3945 self._updatingMETAR = True
3946
3947 buffer.delete_text(position, length)
3948 buffer.insert_text(position, text.upper(), length)
3949
3950 self._updatingMETAR = False
3951
3952#-----------------------------------------------------------------------------
3953
3954class CruisePage(Page):
3955 """The page containing the flight level that might change during flight."""
3956 def __init__(self, wizard):
3957 """Construct the page."""
3958 super(CruisePage, self).__init__(wizard, "cruise",
3959 xstr("cruise_title"),
3960 xstr("cruise_help"))
3961
3962 self._loggable = False
3963 self._loggedCruiseLevel = 240
3964 self._activated = False
3965
3966 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
3967 xscale = 0.0, yscale = 1.0)
3968
3969 mainBox = gtk.VBox()
3970 alignment.add(mainBox)
3971 self.setMainWidget(alignment)
3972
3973 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
3974 xscale = 0.0, yscale = 0.0)
3975 mainBox.pack_start(alignment, False, False, 16)
3976
3977 levelBox = gtk.HBox()
3978
3979 label = gtk.Label(xstr("route_level"))
3980 label.set_use_underline(True)
3981 levelBox.pack_start(label, True, True, 0)
3982
3983 self._cruiseLevel = gtk.SpinButton()
3984 self._cruiseLevel.set_increments(step = 10, page = 100)
3985 self._cruiseLevel.set_range(min = 50, max = 500)
3986 self._cruiseLevel.set_tooltip_text(xstr("cruise_route_level_tooltip"))
3987 self._cruiseLevel.set_numeric(True)
3988 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
3989 label.set_mnemonic_widget(self._cruiseLevel)
3990
3991 levelBox.pack_start(self._cruiseLevel, False, False, 8)
3992
3993 self._updateButton = gtk.Button(xstr("cruise_route_level_update"));
3994 self._updateButton.set_use_underline(True)
3995 self._updateButton.set_tooltip_text(xstr("cruise_route_level_update_tooltip"))
3996 self._updateButton.connect("clicked", self._updateButtonClicked)
3997
3998 levelBox.pack_start(self._updateButton, False, False, 16)
3999
4000 mainBox.pack_start(levelBox, False, False, 0)
4001
4002 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
4003 xscale = 0.0, yscale = 1.0)
4004 mainBox.pack_start(alignment, True, True, 0)
4005
4006 self.addCancelFlightButton()
4007
4008 self._backButton = self.addPreviousButton(clicked = self._backClicked)
4009 self._button = self.addNextButton(clicked = self._forwardClicked)
4010
4011 @property
4012 def activated(self):
4013 """Determine if the page is already activated or not."""
4014 return self._activated
4015
4016 @property
4017 def cruiseLevel(self):
4018 """Get the cruise level."""
4019 return self._loggedCruiseLevel
4020
4021 @property
4022 def loggableCruiseLevel(self):
4023 """Get the cruise level which should be logged."""
4024 return self._cruiseLevel.get_value_as_int()
4025
4026 def setLoggable(self, loggable):
4027 """Set whether the cruise altitude can be logged."""
4028 self._loggable = loggable
4029 self._updateButtons()
4030
4031 def activate(self):
4032 """Setup the route from the booked flight."""
4033 self._loggedCruiseLevel = self._wizard.filedCruiseLevel
4034 self._cruiseLevel.set_value(self._loggedCruiseLevel)
4035 self._activated = True
4036
4037 def reset(self):
4038 """Reset the page."""
4039 self._loggable = False
4040 self._activated = False
4041 super(CruisePage, self).reset()
4042
4043 def _updateButtons(self):
4044 """Update the sensitivity of the buttons."""
4045 self._updateButton.set_sensitive(self._loggable and
4046 self.loggableCruiseLevel!=
4047 self._loggedCruiseLevel)
4048
4049 def _cruiseLevelChanged(self, spinButton):
4050 """Called when the cruise level has changed."""
4051 self._updateButtons()
4052
4053 def _updateButtonClicked(self, button):
4054 """Called when the update button is clicked."""
4055 if self._wizard.cruiseLevelChanged():
4056 self._loggedCruiseLevel = self.loggableCruiseLevel
4057 self._updateButtons()
4058
4059 def _backClicked(self, button):
4060 """Called when the Back button is pressed."""
4061 self.goBack()
4062
4063 def _forwardClicked(self, button):
4064 """Called when the Forward button is clicked."""
4065 self._wizard.nextPage()
4066
4067#-----------------------------------------------------------------------------
4068
4069class LandingPage(Page):
4070 """Page for entering landing data."""
4071 def __init__(self, wizard):
4072 """Construct the landing page."""
4073 super(LandingPage, self).__init__(wizard, "landing",
4074 xstr("landing_title"),
4075 xstr("landing_help"),
4076 completedHelp = xstr("landing_chelp"))
4077
4078 self._flightEnded = False
4079
4080 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
4081 xscale = 0.0, yscale = 0.0)
4082
4083 table = gtk.Table(7, 24)
4084 table.set_row_spacings(4)
4085 table.set_col_spacings(16)
4086 table.set_homogeneous(False)
4087 alignment.add(table)
4088 self.setMainWidget(alignment)
4089
4090 row = 0
4091
4092 label = gtk.Label(xstr("landing_metar"))
4093 label.set_use_underline(True)
4094 label.set_alignment(0.0, 0.5)
4095 table.attach(label, 1, 2, row, row+1)
4096
4097 self._metar = gtk.Entry()
4098 self._metar.set_width_chars(40)
4099 self._metar.set_tooltip_text(xstr("landing_metar_tooltip"))
4100 self._metar.connect("changed", self._metarChanged)
4101 self._metar.get_buffer().connect_after("inserted-text", self._metarInserted)
4102 table.attach(self._metar, 2, 24, row, row+1)
4103 label.set_mnemonic_widget(self._metar)
4104
4105 self._updatingMETAR = False
4106
4107 row += 1
4108
4109 label = gtk.Label(xstr("landing_star"))
4110 label.set_use_underline(True)
4111 label.set_alignment(0.0, 0.5)
4112 table.attach(label, 1, 2, row, row + 1)
4113
4114 if pygobject:
4115 self._star = gtk.ComboBox.new_with_model_and_entry(comboModel)
4116 else:
4117 self._star = gtk.ComboBoxEntry(comboModel)
4118
4119 self._star.set_entry_text_column(0)
4120 self._star.get_child().set_width_chars(10)
4121 self._star.set_tooltip_text(xstr("landing_star_tooltip"))
4122 self._star.connect("changed", self._upperChangedComboBox)
4123 self._star.set_sensitive(False)
4124 table.attach(self._star, 2, 4, row, row + 1)
4125 label.set_mnemonic_widget(self._star)
4126
4127 row += 1
4128
4129 label = gtk.Label(xstr("landing_transition"))
4130 label.set_use_underline(True)
4131 label.set_alignment(0.0, 0.5)
4132 table.attach(label, 1, 2, row, row + 1)
4133
4134 if pygobject:
4135 self._transition = gtk.ComboBox.new_with_model_and_entry(comboModel)
4136 else:
4137 self._transition = gtk.ComboBoxEntry(comboModel)
4138
4139 self._transition.set_entry_text_column(0)
4140 self._transition.get_child().set_width_chars(10)
4141 self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
4142 self._transition.connect("changed", self._upperChangedComboBox)
4143 self._transition.set_sensitive(False)
4144 table.attach(self._transition, 2, 4, row, row + 1)
4145 label.set_mnemonic_widget(self._transition)
4146
4147 row += 1
4148
4149 label = gtk.Label(xstr("landing_runway"))
4150 label.set_use_underline(True)
4151 label.set_alignment(0.0, 0.5)
4152 table.attach(label, 1, 2, row, row + 1)
4153
4154 self._runway = gtk.Entry()
4155 self._runway.set_width_chars(10)
4156 self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
4157 self._runway.connect("changed", self._upperChanged)
4158 table.attach(self._runway, 2, 4, row, row + 1)
4159 label.set_mnemonic_widget(self._runway)
4160
4161 row += 1
4162
4163 label = gtk.Label(xstr("landing_approach"))
4164 label.set_use_underline(True)
4165 label.set_alignment(0.0, 0.5)
4166 table.attach(label, 1, 2, row, row + 1)
4167
4168 self._approachType = gtk.Entry()
4169 self._approachType.set_width_chars(10)
4170 self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
4171 self._approachType.connect("changed", self._upperChanged)
4172 table.attach(self._approachType, 2, 4, row, row + 1)
4173 label.set_mnemonic_widget(self._approachType)
4174
4175 row += 1
4176
4177 label = gtk.Label(xstr("landing_vref"))
4178 label.set_use_markup(True)
4179 label.set_use_underline(True)
4180 label.set_alignment(0.0, 0.5)
4181 table.attach(label, 1, 2, row, row + 1)
4182
4183 self._vref = IntegerEntry()
4184 self._vref.set_width_chars(5)
4185 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
4186 self._vref.connect("integer-changed", self._vrefChanged)
4187 table.attach(self._vref, 3, 4, row, row + 1)
4188 label.set_mnemonic_widget(self._vref)
4189
4190 self._vrefUnit = gtk.Label(xstr("label_knots"))
4191 table.attach(self._vrefUnit, 4, 5, row, row + 1)
4192
4193 row += 1
4194
4195 self._antiIceOn = gtk.CheckButton(xstr("landing_antiice"))
4196 self._antiIceOn.set_use_underline(True)
4197 self._antiIceOn.set_tooltip_text(xstr("landing_antiice_tooltip"))
4198 table.attach(self._antiIceOn, 3, 5, row, row + 1)
4199
4200 self.addCancelFlightButton()
4201
4202 self.addPreviousButton(clicked = self._backClicked)
4203
4204 self._button = self.addNextButton(clicked = self._forwardClicked)
4205
4206 self._active = False
4207
4208 @property
4209 def star(self):
4210 """Get the STAR or None if none entered."""
4211 text = self._star.get_child().get_text()
4212 return text if self._star.get_active()!=0 and text and text!="N/A" \
4213 else None
4214
4215 @property
4216 def transition(self):
4217 """Get the transition or None if none entered."""
4218 text = self._transition.get_child().get_text()
4219 return text if self._transition.get_active()!=0 and text and text!="N/A" \
4220 else None
4221
4222 @property
4223 def approachType(self):
4224 """Get the approach type."""
4225 return self._approachType.get_text()
4226
4227 @property
4228 def runway(self):
4229 """Get the runway."""
4230 return self._runway.get_text()
4231
4232 @property
4233 def vref(self):
4234 """Return the landing reference speed."""
4235 return self._vref.get_int()
4236
4237 @property
4238 def antiIceOn(self):
4239 """Get whether the anti-ice system has been turned on."""
4240 return self._antiIceOn.get_active()
4241
4242 @antiIceOn.setter
4243 def antiIceOn(self, value):
4244 """Set the anti-ice indicator."""
4245 self._antiIceOn.set_active(value)
4246
4247 def reset(self):
4248 """Reset the page if the wizard is reset."""
4249 super(LandingPage, self).reset()
4250 self._vref.reset()
4251 self._antiIceOn.set_active(False)
4252 self._flightEnded = False
4253 self._active = False
4254
4255 def activate(self):
4256 """Called when the page is activated."""
4257 self._updatingMETAR = True
4258 self._metar.get_buffer().set_text(self._wizard.arrivalMETAR, -1)
4259 self._updatingMETAR = False
4260
4261 self._star.set_active(0)
4262 self._star.set_sensitive(True)
4263
4264 self._transition.set_active(0)
4265 self._transition.set_sensitive(True)
4266
4267 if self._wizard.landingRunway is None:
4268 self._runway.set_text("")
4269 else:
4270 self._runway.set_text(self._wizard.landingRunway)
4271 self._runway.set_sensitive(True)
4272
4273 self._approachType.set_text("")
4274 self._approachType.set_sensitive(True)
4275
4276 self._vref.set_int(None)
4277 self._vref.set_sensitive(True)
4278
4279 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
4280 speedUnit = xstr("label" + i18nSpeedUnit)
4281 self._vrefUnit.set_text(speedUnit)
4282
4283 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
4284 i18nSpeedUnit))
4285
4286 self._updateForwardButton()
4287
4288 self._active = True
4289
4290 def flightEnded(self):
4291 """Called when the flight has ended."""
4292 super(LandingPage, self).flightEnded()
4293 self._flightEnded = True
4294 self._updateForwardButton()
4295
4296 def changeMETAR(self, metar):
4297 """Change the METAR as a result of an edit on one of the other
4298 pages."""
4299 if self._active:
4300 print "LandingPage.changeMETAR"
4301 self._updatingMETAR = True
4302 self._metar.get_buffer().set_text(metar, -1)
4303 self._updatingMETAR = False
4304
4305 self._updateForwardButton()
4306
4307 def _updateForwardButton(self):
4308 """Update the sensitivity of the forward button."""
4309 sensitive = self._flightEnded and \
4310 self._metar.get_text()!="" and \
4311 (self.star is not None or
4312 self.transition is not None) and \
4313 self._runway.get_text()!="" and \
4314 self._approachType.get_text()!="" and \
4315 self.vref is not None
4316 self._button.set_sensitive(sensitive)
4317
4318 def _upperChanged(self, entry):
4319 """Called for entry widgets that must be converted to uppercase."""
4320 entry.set_text(entry.get_text().upper())
4321 self._updateForwardButton()
4322
4323 def _upperChangedComboBox(self, comboBox):
4324 """Called for combo box widgets that must be converted to uppercase."""
4325 if comboBox.get_active()==-1:
4326 entry = comboBox.get_child()
4327 entry.set_text(entry.get_text().upper())
4328 self._updateForwardButton()
4329
4330 def _vrefChanged(self, widget, value):
4331 """Called when the Vref has changed."""
4332 self._updateForwardButton()
4333
4334 def _backClicked(self, button):
4335 """Called when the Back button is pressed."""
4336 self.goBack()
4337
4338 def _forwardClicked(self, button):
4339 """Called when the forward button is clicked."""
4340 wizard = self._wizard
4341
4342 aircraft = wizard.gui.flight.aircraft
4343 aircraft.updateVRef()
4344 aircraft.updateLandingAntiIce()
4345 if wizard.gui.config.onlineGateSystem and \
4346 wizard.loggedIn and not self._completed and \
4347 wizard.bookedFlight.arrivalICAO=="LHBP" and \
4348 not wizard.entranceExam:
4349 wizard.getFleet(callback = self._fleetRetrieved, force = True)
4350 elif wizard.entranceExam:
4351 self._handleEntranceExamDone()
4352 else:
4353 wizard.nextPage()
4354
4355 def _fleetRetrieved(self, fleet):
4356 """Callback for the fleet retrieval."""
4357 self._wizard.nextPage()
4358
4359 def _metarChanged(self, entry):
4360 """Called when the METAR has changed."""
4361 print "LandingPage.metarChanged", self._updatingMETAR
4362 if not self._updatingMETAR:
4363 self._updateForwardButton()
4364 self._wizard.metarChanged(entry.get_text(), self)
4365
4366 def _metarInserted(self, buffer, position, text, length):
4367 """Called when new characters are inserted into the METAR.
4368
4369 It uppercases all characters."""
4370 print "LandingPage.metarInserted", self._updatingMETAR
4371 if not self._updatingMETAR:
4372 self._updatingMETAR = True
4373
4374 buffer.delete_text(position, length)
4375 buffer.insert_text(position, text.upper(), length)
4376
4377 self._updatingMETAR = False
4378
4379 def _handleEntranceExamDone(self):
4380 """Handle the end of the entrance exam.
4381
4382 If the there was a NO-GO fault, notify the user that exam is a failure
4383 and take them back to the student page. Otherwise congratulate, update
4384 the database to reflect that the exam has been taken and go back to the
4385 student page."""
4386 self._wizard.jumpPage("chkfinish")
4387
4388#-----------------------------------------------------------------------------
4389
4390class PIREPSaveHelper(object):
4391 """A helper to use for saving PIREPs."""
4392 def __init__(self, wizard):
4393 """Construct the helper."""
4394 super(PIREPSaveHelper, self).__init__()
4395
4396 self._wizard = wizard
4397
4398 self._lastSavePath = None
4399 self._savePIREPDialog = None
4400
4401 def addButton(self, page):
4402 """Add a button to save the PIREP to the given page."""
4403 return page.addButton(xstr("finish_save"), sensitive = False,
4404 clicked = self._saveClicked,
4405 tooltip = xstr("finish_save_tooltip"),
4406 clickedArg = page)
4407
4408 def autoSavePIREP(self, page):
4409 """Perform the automatic saving of the PIREP."""
4410 self._lastSavePath = os.path.join(self._wizard.gui.config.pirepDirectory,
4411 self._getDefaultPIREPName())
4412 self._lastSavePath = text2unicode(self._lastSavePath)
4413 self._savePIREP(page, automatic = True)
4414
4415 def _getDefaultPIREPName(self):
4416 """Get the default name of the PIREP."""
4417 gui = self._wizard.gui
4418
4419 bookedFlight = gui.bookedFlight
4420 tm = time.gmtime()
4421
4422 pilotID = self._wizard.pilotID
4423 if pilotID: pilotID += " "
4424 return "%s%s %02d%02d %s-%s.pirep" % \
4425 (pilotID, str(bookedFlight.departureTime.date()),
4426 tm.tm_hour, tm.tm_min,
4427 bookedFlight.departureICAO, bookedFlight.arrivalICAO)
4428
4429 def _saveClicked(self, button, page):
4430 """Called when the Save PIREP button is clicked."""
4431 gui = self._wizard.gui
4432
4433 fileName = self._getDefaultPIREPName()
4434
4435 dialog = self._getSaveDialog()
4436
4437 if self._lastSavePath is None:
4438 pirepDirectory = gui.config.pirepDirectory
4439 if pirepDirectory is not None:
4440 dialog.set_current_folder(pirepDirectory)
4441 else:
4442 dialog.set_current_folder(os.path.dirname(self._lastSavePath))
4443
4444 dialog.set_current_name(fileName)
4445 result = dialog.run()
4446 dialog.hide()
4447
4448 if result==RESPONSETYPE_OK:
4449 self._lastSavePath = text2unicode(dialog.get_filename())
4450 self._savePIREP(page)
4451
4452 def _savePIREP(self, page, automatic = False):
4453 """Perform the saving of the PIREP."""
4454
4455 gui = self._wizard.gui
4456
4457 if automatic:
4458 gui.beginBusy(xstr("finish_autosave_busy"))
4459
4460 pirep = PIREP(gui.flight)
4461 error = pirep.save(self._lastSavePath)
4462
4463 if automatic:
4464 gui.endBusy()
4465
4466 if error:
4467 type = MESSAGETYPE_ERROR
4468 message = xstr("finish_save_failed")
4469 secondary = xstr("finish_save_failed_sec") % (text2unicode(error),)
4470 else:
4471 type = MESSAGETYPE_INFO
4472 message = xstr("finish_save_done")
4473 if automatic:
4474 secondary = xstr("finish_save_done_sec") % (self._lastSavePath,)
4475 else:
4476 secondary = None
4477 page.setPIREPSaved()
4478
4479 dialog = gtk.MessageDialog(parent = gui.mainWindow,
4480 type = type, message_format = message)
4481 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
4482 dialog.set_title(WINDOW_TITLE_BASE)
4483 if secondary is not None:
4484 dialog.format_secondary_markup(secondary)
4485
4486 dialog.run()
4487 dialog.hide()
4488
4489 def _getSaveDialog(self):
4490 """Get the PIREP saving dialog.
4491
4492 If it does not exist yet, create it."""
4493 if self._savePIREPDialog is None:
4494 gui = self._wizard.gui
4495 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
4496 xstr("finish_save_title"),
4497 action = FILE_CHOOSER_ACTION_SAVE,
4498 buttons = (gtk.STOCK_CANCEL,
4499 RESPONSETYPE_CANCEL,
4500 gtk.STOCK_OK, RESPONSETYPE_OK),
4501 parent = gui.mainWindow)
4502 dialog.set_modal(True)
4503 dialog.set_do_overwrite_confirmation(True)
4504
4505 filter = gtk.FileFilter()
4506 filter.set_name(xstr("file_filter_pireps"))
4507 filter.add_pattern("*.pirep")
4508 dialog.add_filter(filter)
4509
4510 filter = gtk.FileFilter()
4511 filter.set_name(xstr("file_filter_all"))
4512 filter.add_pattern("*.*")
4513 dialog.add_filter(filter)
4514
4515 self._savePIREPDialog = dialog
4516
4517 return self._savePIREPDialog
4518
4519#-----------------------------------------------------------------------------
4520
4521class FinishPage(Page):
4522 """Flight finish page."""
4523 def __init__(self, wizard, saveHelper):
4524 """Construct the finish page."""
4525 help = xstr("finish_help") + xstr("finish_help_goodtime")
4526 super(FinishPage, self).__init__(wizard, "finish",
4527 xstr("finish_title"), help)
4528
4529 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
4530 xscale = 0.0, yscale = 0.0)
4531
4532 table = gtk.Table(10, 2)
4533 table.set_row_spacings(4)
4534 table.set_col_spacings(16)
4535 table.set_homogeneous(False)
4536 alignment.add(table)
4537 self.setMainWidget(alignment)
4538
4539 row = 0
4540
4541 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4542 label = gtk.Label(xstr("finish_rating"))
4543 labelAlignment.add(label)
4544 table.attach(labelAlignment, 0, 1, row, row+1)
4545
4546 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4547 self._flightRating = gtk.Label()
4548 self._flightRating.set_width_chars(8)
4549 self._flightRating.set_alignment(0.0, 0.5)
4550 self._flightRating.set_use_markup(True)
4551 labelAlignment.add(self._flightRating)
4552 table.attach(labelAlignment, 1, 2, row, row+1)
4553
4554 row += 1
4555
4556 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4557 label = gtk.Label(xstr("finish_dep_time"))
4558 labelAlignment.add(label)
4559 table.attach(labelAlignment, 0, 1, row, row+1)
4560
4561 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4562 self._depTime = gtk.Label()
4563 self._depTime.set_width_chars(13)
4564 self._depTime.set_alignment(0.0, 0.5)
4565 self._depTime.set_use_markup(True)
4566 self._depTime.set_tooltip_markup(xstr("finish_dep_time_tooltip"))
4567 labelAlignment.add(self._depTime)
4568 table.attach(labelAlignment, 1, 2, row, row+1)
4569
4570 row += 1
4571
4572 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4573 label = gtk.Label(xstr("finish_flight_time"))
4574 labelAlignment.add(label)
4575 table.attach(labelAlignment, 0, 1, row, row+1)
4576
4577 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4578 self._flightTime = gtk.Label()
4579 self._flightTime.set_width_chars(10)
4580 self._flightTime.set_alignment(0.0, 0.5)
4581 self._flightTime.set_use_markup(True)
4582 labelAlignment.add(self._flightTime)
4583 table.attach(labelAlignment, 1, 2, row, row+1)
4584
4585 row += 1
4586
4587 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4588 label = gtk.Label(xstr("finish_block_time"))
4589 labelAlignment.add(label)
4590 table.attach(labelAlignment, 0, 1, row, row+1)
4591
4592 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4593 self._blockTime = gtk.Label()
4594 self._blockTime.set_width_chars(10)
4595 self._blockTime.set_alignment(0.0, 0.5)
4596 self._blockTime.set_use_markup(True)
4597 labelAlignment.add(self._blockTime)
4598 table.attach(labelAlignment, 1, 2, row, row+1)
4599
4600 row += 1
4601
4602 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4603 label = gtk.Label(xstr("finish_arr_time"))
4604 labelAlignment.add(label)
4605 table.attach(labelAlignment, 0, 1, row, row+1)
4606
4607 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4608 self._arrTime = gtk.Label()
4609 self._arrTime.set_width_chars(13)
4610 self._arrTime.set_alignment(0.0, 0.5)
4611 self._arrTime.set_use_markup(True)
4612 self._arrTime.set_tooltip_markup(xstr("finish_arr_time_tooltip"))
4613 labelAlignment.add(self._arrTime)
4614 table.attach(labelAlignment, 1, 2, row, row+1)
4615
4616 row += 1
4617
4618 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4619 label = gtk.Label(xstr("finish_distance"))
4620 labelAlignment.add(label)
4621 table.attach(labelAlignment, 0, 1, row, row+1)
4622
4623 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4624 self._distanceFlown = gtk.Label()
4625 self._distanceFlown.set_width_chars(10)
4626 self._distanceFlown.set_alignment(0.0, 0.5)
4627 self._distanceFlown.set_use_markup(True)
4628 labelAlignment.add(self._distanceFlown)
4629 table.attach(labelAlignment, 1, 2, row, row+1)
4630
4631 row += 1
4632
4633 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4634 label = gtk.Label(xstr("finish_fuel"))
4635 labelAlignment.add(label)
4636 table.attach(labelAlignment, 0, 1, row, row+1)
4637
4638 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4639 self._fuelUsed = gtk.Label()
4640 self._fuelUsed.set_width_chars(10)
4641 self._fuelUsed.set_alignment(0.0, 0.5)
4642 self._fuelUsed.set_use_markup(True)
4643 labelAlignment.add(self._fuelUsed)
4644 table.attach(labelAlignment, 1, 2, row, row+1)
4645
4646 row += 1
4647
4648 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
4649 yalign = 0.5, yscale = 0.0)
4650 label = gtk.Label(xstr("finish_type"))
4651 label.set_use_underline(True)
4652 labelAlignment.add(label)
4653 table.attach(labelAlignment, 0, 1, row, row+1)
4654
4655 self._flightType = createFlightTypeComboBox()
4656 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
4657 self._flightType.set_active(0)
4658 self._flightType.connect("changed", self._flightTypeChanged)
4659 flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4660 flightTypeAlignment.add(self._flightType)
4661 table.attach(flightTypeAlignment, 1, 2, row, row+1)
4662 label.set_mnemonic_widget(self._flightType)
4663
4664 row += 1
4665
4666 self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
4667 self._onlineFlight.set_use_underline(True)
4668 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
4669 onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4670 onlineFlightAlignment.add(self._onlineFlight)
4671 table.attach(onlineFlightAlignment, 1, 2, row, row + 1)
4672
4673 row += 1
4674
4675 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
4676 yalign = 0.5, yscale = 0.0)
4677 self._gateLabel = gtk.Label(xstr("finish_gate"))
4678 self._gateLabel.set_use_underline(True)
4679 labelAlignment.add(self._gateLabel)
4680 table.attach(labelAlignment, 0, 1, row, row+1)
4681
4682 self._gatesModel = gtk.ListStore(str)
4683
4684 self._gate = gtk.ComboBox(model = self._gatesModel)
4685 renderer = gtk.CellRendererText()
4686 self._gate.pack_start(renderer, True)
4687 self._gate.add_attribute(renderer, "text", 0)
4688 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
4689 self._gate.connect("changed", self._gateChanged)
4690 gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
4691 gateAlignment.add(self._gate)
4692 table.attach(gateAlignment, 1, 2, row, row+1)
4693 self._gateLabel.set_mnemonic_widget(self._gate)
4694
4695 self.addButton(xstr("finish_newFlight"),
4696 sensitive = True,
4697 clicked = self._newFlightClicked,
4698 tooltip = xstr("finish_newFlight_tooltip"),
4699 padding = 16)
4700
4701 self.addPreviousButton(clicked = self._backClicked)
4702
4703 self._saveHelper = saveHelper
4704 self._saveButton = saveHelper.addButton(self)
4705
4706 self._tooBigTimeDifference = False
4707 self._deferredAutoSave = False
4708 self._pirepSaved = False
4709 self._pirepSent = False
4710
4711 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
4712 sensitive = False,
4713 clicked = self._sendClicked,
4714 tooltip = xstr("sendPIREP_tooltip"))
4715
4716 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 10*60.0)
4717 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 20*60.0)
4718 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 0*60.0)
4719 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), (23*60.0+50)*60.0)
4720 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (1*60.0+5)*60.0)
4721 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (0*60.0+50)*60.0)
4722 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (0*60.0+5)*60.0)
4723 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (23*60.0+45)*60.0)
4724
4725 @property
4726 def flightType(self):
4727 """Get the flight type."""
4728 index = self._flightType.get_active()
4729 return None if index<0 else self._flightType.get_model()[index][1]
4730
4731 @property
4732 def online(self):
4733 """Get whether the flight was an online flight or not."""
4734 return self._onlineFlight.get_active()
4735
4736 def activate(self):
4737 """Activate the page."""
4738 self._deferredAutoSave = False
4739 self._pirepSaved = False
4740 self._pirepSent = False
4741
4742 flight = self._wizard.gui._flight
4743 rating = flight.logger.getRating()
4744 if rating<0:
4745 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
4746 else:
4747 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
4748
4749 flightLength = flight.flightTimeEnd - flight.flightTimeStart
4750 self._flightTime.set_markup("<b>%s</b>" % \
4751 (util.getTimeIntervalString(flightLength),))
4752
4753 blockLength = flight.blockTimeEnd - flight.blockTimeStart
4754 self._blockTime.set_markup("<b>%s</b>" % \
4755 (util.getTimeIntervalString(blockLength),))
4756
4757 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
4758 (flight.flownDistance,))
4759
4760 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
4761 (flight.startFuel - flight.endFuel,))
4762
4763 self._flightType.set_active(-1)
4764 self._onlineFlight.set_active(self._wizard.loggedIn)
4765
4766 self._gatesModel.clear()
4767 if self._wizard.gui.config.onlineGateSystem and \
4768 self._wizard.loggedIn and \
4769 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
4770 not self._wizard.entranceExam:
4771 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
4772 for gate in lhbpGates.gates:
4773 if gate.isAvailable(lhbpGates, occupiedGateNumbers):
4774 self._gatesModel.append([gate.number])
4775 self._gateLabel.set_sensitive(True)
4776 self._gate.set_sensitive(True)
4777 self._gate.set_active(-1)
4778 else:
4779 self._gateLabel.set_sensitive(False)
4780 self._gate.set_sensitive(False)
4781
4782 self._updateTimes()
4783
4784 def updateButtons(self):
4785 """Update the sensitivity state of the buttons."""
4786 gui = self._wizard.gui
4787 faultsExplained = gui.faultsFullyExplained
4788 timesCorrect = self.flightType is None or \
4789 not self._tooBigTimeDifference or \
4790 gui.hasComments or gui.hasDelayCode
4791 sensitive = gui.flight is not None and \
4792 gui.flight.stage==const.STAGE_END and \
4793 self._flightType.get_active()>=0 and \
4794 (self._gatesModel.get_iter_first() is None or
4795 self._gate.get_active()>=0) and \
4796 faultsExplained and timesCorrect
4797
4798 self._updateHelp(faultsExplained, timesCorrect)
4799
4800 wasSensitive = self._saveButton.get_sensitive()
4801
4802 if gui.config.pirepAutoSave and sensitive and not wasSensitive:
4803 if gui.isWizardActive():
4804 self._saveHelper.autoSavePIREP(self)
4805 else:
4806 self._deferredAutoSave = True
4807
4808 if not sensitive:
4809 self._deferredAutoSave = False
4810
4811 self._saveButton.set_sensitive(sensitive)
4812 self._sendButton.set_sensitive(sensitive and
4813 self._wizard.bookedFlight.id is not None)
4814
4815 def grabDefault(self):
4816 """If the page has a default button, make it the default one."""
4817 super(FinishPage, self).grabDefault()
4818 if self._deferredAutoSave:
4819 self._saveHelper.autoSavePIREP(self)
4820 self._deferredAutoSave = False
4821
4822 def setPIREPSaved(self):
4823 """Mark the PIREP as saved."""
4824 self._pirepSaved = True
4825
4826 def _backClicked(self, button):
4827 """Called when the Back button is pressed."""
4828 self.goBack()
4829
4830 def _flightTypeChanged(self, comboBox):
4831 """Called when the flight type has changed."""
4832 self._updateTimes()
4833
4834 def _gateChanged(self, comboBox):
4835 """Called when the arrival gate has changed."""
4836 self.updateButtons()
4837
4838 def _newFlightClicked(self, button):
4839 """Called when the new flight button is clicked."""
4840 gui = self._wizard.gui
4841 if not self._pirepSent and not self._pirepSaved:
4842 dialog = gtk.MessageDialog(parent = gui.mainWindow,
4843 type = MESSAGETYPE_QUESTION,
4844 message_format = xstr("finish_newFlight_question"))
4845
4846 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
4847 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
4848
4849 dialog.set_title(WINDOW_TITLE_BASE)
4850 result = dialog.run()
4851 dialog.hide()
4852 if result!=RESPONSETYPE_YES:
4853 return
4854
4855 gui.reset()
4856
4857 def _sendClicked(self, button):
4858 """Called when the Send button is clicked."""
4859 pirep = PIREP(self._wizard.gui.flight)
4860 self._wizard.gui.sendPIREP(pirep,
4861 callback = self._handlePIREPSent)
4862
4863 def _handlePIREPSent(self, returned, result):
4864 """Callback for the PIREP sending result."""
4865 self._pirepSent = returned and result.success
4866 if self._wizard.gui.config.onlineGateSystem and \
4867 self._wizard.loggedIn and not self._wizard.entranceExam and \
4868 returned and result.success:
4869 bookedFlight = self._wizard.bookedFlight
4870 if bookedFlight.arrivalICAO=="LHBP":
4871 iter = self._gate.get_active_iter()
4872 gateNumber = None if iter is None \
4873 else self._gatesModel.get_value(iter, 0)
4874
4875 status = const.PLANE_PARKING if gateNumber is None \
4876 else const.PLANE_HOME
4877 else:
4878 gateNumber = None
4879 status = const.PLANE_AWAY
4880
4881 self._wizard.updatePlane(self._planeUpdated,
4882 bookedFlight.tailNumber,
4883 status, gateNumber = gateNumber)
4884
4885 def _planeUpdated(self, success):
4886 """Callback for the plane updating."""
4887 pass
4888
4889 def _formatTime(self, scheduledTime, realTimestamp, (warning, error)):
4890 """Format the departure or arrival time based on the given data as a
4891 markup for a label."""
4892 realTime = time.gmtime(realTimestamp)
4893
4894 if warning:
4895 colour = "red" if error else "orange"
4896 markupBegin = '<span foreground="%s">' % (colour,)
4897 markupEnd = '</span>'
4898 else:
4899 markupBegin = markupEnd = ""
4900
4901 markup = "<b>%s%02d:%02d [%02d:%02d]%s</b>" % \
4902 (markupBegin,
4903 realTime.tm_hour, realTime.tm_min,
4904 scheduledTime.hour, scheduledTime.minute,
4905 markupEnd)
4906
4907 return markup
4908
4909 def _updateTimes(self):
4910 """Format the flight times and the help text according to the flight
4911 type.
4912
4913 The buttons are also updated.
4914 """
4915 flight = self._wizard.gui._flight
4916 bookedFlight = flight.bookedFlight
4917
4918 (departureWarning, departureError) = flight.blockTimeStartWrong
4919 (arrivalWarning, arrivalError) = flight.blockTimeEndWrong
4920
4921 if self.flightType==const.FLIGHTTYPE_VIP:
4922 departureError = arrivalError = False
4923
4924 self._tooBigTimeDifference = departureError or arrivalError
4925
4926 self._depTime.set_markup(self._formatTime(bookedFlight.departureTime,
4927 flight.blockTimeStart,
4928 (departureWarning,
4929 departureError)))
4930
4931 self._arrTime.set_markup(self._formatTime(bookedFlight.arrivalTime,
4932 flight.blockTimeEnd,
4933 (arrivalWarning,
4934 arrivalError)))
4935
4936 self.updateButtons()
4937
4938 def _updateHelp(self, faultsExplained, timesCorrect):
4939 """Update the help text according to the actual situation."""
4940 if not faultsExplained:
4941 self.setHelp(xstr("finish_help") + xstr("finish_help_faults"))
4942 elif not timesCorrect:
4943 self.setHelp(xstr("finish_help") + xstr("finish_help_wrongtime"))
4944 else:
4945 self.setHelp(xstr("finish_help") + xstr("finish_help_goodtime"))
4946
4947
4948#-----------------------------------------------------------------------------
4949
4950class CheckFlightFinishPage(Page):
4951 """Finish page for a check flight."""
4952 def __init__(self, wizard, saveHelper):
4953 """Construct the check flight finish page."""
4954 super(CheckFlightFinishPage, self).__init__(wizard,
4955 "chkfinish",
4956 xstr("chkfinish_title"),
4957 "")
4958
4959 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
4960 xscale = 1.0, yscale = 1.0)
4961 self._label = gtk.Label()
4962 alignment.add(self._label)
4963
4964 self.setMainWidget(alignment)
4965
4966 self._saveHelper = saveHelper
4967 self._saveButton = saveHelper.addButton(self)
4968
4969 self._button = self.addNextButton(sensitive = False,
4970 clicked = self._forwardClicked)
4971
4972 def activate(self):
4973 """Activate the page."""
4974 wizard = self._wizard
4975 loginResult = wizard.loginResult
4976 gui = wizard.gui
4977 rating = gui.flight.logger.getRating()
4978
4979 if rating>=0:
4980 loginResult.checkFlightStatus = True
4981
4982 firstOfficer = \
4983 loginResult.entryExamPassed and loginResult.checkFlightStatus
4984
4985 if firstOfficer:
4986 loginResult.rank = "FO"
4987
4988 if rating<0:
4989 mainMessage = xstr("chkfinish_failed")
4990 else:
4991 mainMessage = xstr("chkfinish_passed_begin")
4992 if firstOfficer:
4993 mainMessage += xstr("chkfinish_passed_fo")
4994 mainMessage += xstr("chkfinish_passed_end")
4995
4996 if firstOfficer:
4997 nextMessage = xstr("chkfinish_next")
4998 else:
4999 nextMessage = xstr("chkfinish_next_student_begin")
5000 if not loginResult.entryExamPassed and \
5001 not loginResult.checkFlightStatus:
5002 nextMessage += xstr("chkfinish_next_student_nothing")
5003 elif loginResult.entryExamPassed and \
5004 not loginResult.checkFlightStatus:
5005 nextMessage += xstr("chkfinish_next_student_no_flight")
5006 elif not loginResult.entryExamPassed and \
5007 loginResult.checkFlightStatus:
5008 nextMessage += xstr("chkfinish_next_student_no_exam")
5009
5010 self._label.set_text(mainMessage +
5011 xstr("chkfinish_savepirep") +
5012 nextMessage)
5013 self._label.set_use_markup(True)
5014 self._label.set_alignment(0.5, 0.0)
5015
5016 self._saveButton.set_sensitive(True)
5017 self._button.set_sensitive(True)
5018
5019 def _forwardClicked(self, button):
5020 """Jump to the student page if there are some tasks to do,
5021 or to the flight selection page, if the pilot is allowed to perform
5022 MAVA flights."""
5023 wizard = self._wizard
5024 gui = wizard.gui
5025
5026 loginResult = wizard.loginResult
5027 if loginResult.checkFlightStatus:
5028 gui.beginBusy(xstr("chkfinish_updateweb_busy"))
5029 gui.webHandler.setCheckFlightPassed(self._checkFlightPassedSetCallback,
5030 wizard.checkFlightAircraftType)
5031 else:
5032 self._resetGUI()
5033
5034 def _checkFlightPassedSetCallback(self, returned, result):
5035 """Called when the check flight status has been set."""
5036 gobject.idle_add(self._checkFlightPassedSet, returned, result)
5037
5038 def _checkFlightPassedSet(self, returned, result):
5039 """Handle the result of an attempt to set the check flight status."""
5040 gui = self._wizard.gui
5041
5042 gui.endBusy()
5043
5044 if returned:
5045 self._resetGUI()
5046 else:
5047 dialog = gtk.MessageDialog(parent = gui.mainWindow,
5048 type = MESSAGETYPE_ERROR,
5049 message_format =
5050 xstr("chkfinish_passedset_failed"))
5051 dialog.set_title(WINDOW_TITLE_BASE + " - " +
5052 xstr("chkfinish_passedset_failed_title"))
5053 dialog.format_secondary_markup(xstr("chkfinish_passedset_failed_secondary"))
5054
5055 dialog.add_button(xstr("button_ok"), 0)
5056
5057 dialog.run()
5058 dialog.hide()
5059
5060 def _resetGUI(self):
5061 """Reset the GUI."""
5062 gui = self._wizard.gui
5063 gui.reset()
5064
5065#-----------------------------------------------------------------------------
5066
5067class Wizard(gtk.VBox):
5068 """The flight wizard."""
5069 def __init__(self, gui):
5070 """Construct the wizard."""
5071 super(Wizard, self).__init__()
5072
5073 self.gui = gui
5074
5075 self._pages = []
5076 self._currentPage = None
5077
5078 self._loginPage = LoginPage(self)
5079 self._pages.append(self._loginPage)
5080 self._flightSelectionPage = FlightSelectionPage(self)
5081 self._pages.append(self._flightSelectionPage)
5082 self._pages.append(GateSelectionPage(self))
5083 self._pages.append(RegisterPage(self))
5084 self._studentPage = StudentPage(self)
5085 self._pages.append(self._studentPage)
5086 self._pages.append(ConnectPage(self))
5087 self._payloadPage = PayloadPage(self)
5088 self._pages.append(self._payloadPage)
5089 self._payloadIndex = len(self._pages)
5090 self._pages.append(TimePage(self))
5091 self._routePage = RoutePage(self)
5092 self._pages.append(self._routePage)
5093 self._simBriefSetupPage = SimBriefSetupPage(self)
5094 self._pages.append(self._simBriefSetupPage)
5095 self._simBriefingPage = SimBriefingPage(self)
5096 self._pages.append(self._simBriefingPage)
5097 self._pages.append(FuelPage(self))
5098 self._departureBriefingPage = BriefingPage(self, True)
5099 self._pages.append(self._departureBriefingPage)
5100 self._arrivalBriefingPage = BriefingPage(self, False)
5101 self._pages.append(self._arrivalBriefingPage)
5102 self._arrivalBriefingIndex = len(self._pages)
5103 self._takeoffPage = TakeoffPage(self)
5104 self._pages.append(self._takeoffPage)
5105 self._cruisePage = CruisePage(self)
5106 self._pages.append(self._cruisePage)
5107 self._landingPage = LandingPage(self)
5108 self._pages.append(self._landingPage)
5109
5110 pirepSaveHelper = PIREPSaveHelper(self)
5111
5112 self._finishPage = FinishPage(self, pirepSaveHelper)
5113 self._pages.append(self._finishPage)
5114 self._pages.append(CheckFlightFinishPage(self, pirepSaveHelper))
5115
5116 self._requestedWidth = None
5117 self._requestedHeight = None
5118
5119 self.connect("size-allocate", self._sizeAllocate)
5120
5121 for page in self._pages:
5122 page.show_all()
5123 page.setStyle()
5124
5125 self._initialize()
5126
5127 def _sizeAllocate(self, widget, allocation):
5128 if self._requestedWidth is not None and \
5129 self._requestedHeight is not None:
5130 return
5131
5132 if self._currentPage is not None:
5133 self.remove(self._pages[self._currentPage])
5134
5135 maxWidth = 0
5136 maxHeight = 0
5137 for page in self._pages:
5138 self.add(page)
5139 self.show_all()
5140 pageSizeRequest = page.size_request()
5141 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
5142 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
5143 maxWidth = max(maxWidth, width)
5144 maxHeight = max(maxHeight, height)
5145 self.remove(page)
5146
5147 if self._currentPage is not None:
5148 self.add(self._pages[self._currentPage])
5149
5150 self._requestedWidth = maxWidth
5151 self._requestedHeight = maxHeight
5152 self.set_size_request(maxWidth, maxHeight)
5153
5154 @property
5155 def pilotID(self):
5156 """Get the pilot ID, if given."""
5157 return self._loginPage.pilotID
5158
5159 @property
5160 def entranceExam(self):
5161 """Get whether an entrance exam is about to be taken."""
5162 return self._loginResult is not None and self._loginResult.rank=="STU"
5163
5164 @property
5165 def loggedIn(self):
5166 """Indicate if there was a successful login."""
5167 return self._loginResult is not None
5168
5169 @property
5170 def loginResult(self):
5171 """Get the login result."""
5172 return self._loginResult
5173
5174 @property
5175 def checkFlightAircraftType(self):
5176 """Get the type of the aircraft used to perform the check flight."""
5177 return self._studentPage.aircraftType
5178
5179 def setCurrentPage(self, index, finalize = False, fromPageShift = None):
5180 """Set the current page to the one with the given index.
5181
5182 @param fromPageShift if given, the relative index of one of the
5183 previous pages that should be used as the from-page of the next
5184 page. E.g. if fromPageShift is 1, the previous page will be the
5185 from-page."""
5186 assert index < len(self._pages)
5187
5188 fromPage = self._currentPage
5189 if fromPage is not None:
5190 page = self._pages[fromPage]
5191 if finalize and not page._completed:
5192 page.complete()
5193 self.remove(page)
5194 if fromPageShift is not None:
5195 fromPage -= fromPageShift
5196
5197 self._currentPage = index
5198 page = self._pages[index]
5199 self.add(page)
5200 if page._fromPage is None:
5201 page._fromPage = fromPage
5202 page.initialize()
5203 self.show_all()
5204 if fromPage is not None:
5205 self.grabDefault()
5206
5207 @property
5208 def bookedFlight(self):
5209 """Get the booked flight selected."""
5210 return self._bookedFlight
5211
5212 @property
5213 def numCrew(self):
5214 """Get the number of crew members."""
5215 return self._payloadPage.numCrew
5216
5217 @property
5218 def numPassengers(self):
5219 """Get the number of passengers."""
5220 return self._payloadPage.numPassengers
5221
5222 @property
5223 def bagWeight(self):
5224 """Get the baggage weight."""
5225 return self._payloadPage.bagWeight
5226
5227 @property
5228 def cargoWeight(self):
5229 """Get the cargo weight."""
5230 return self._payloadPage.cargoWeight
5231
5232 @property
5233 def mailWeight(self):
5234 """Get the mail weight."""
5235 return self._payloadPage.mailWeight
5236
5237 @property
5238 def zfw(self):
5239 """Get the calculated ZFW value."""
5240 return 0 if self._bookedFlight is None \
5241 else self._payloadPage.calculateZFW()
5242
5243 @property
5244 def filedCruiseLevel(self):
5245 """Get the filed cruise level."""
5246 return self._routePage.filedCruiseLevel
5247
5248 @property
5249 def filedCruiseAltitude(self):
5250 """Get the filed cruise altitude."""
5251 return self._routePage.filedCruiseLevel * 100
5252
5253 @property
5254 def cruiseAltitude(self):
5255 """Get the cruise altitude."""
5256 level = self._cruisePage.cruiseLevel if self._cruisePage.activated \
5257 else self._routePage.filedCruiseLevel
5258 return level * 100
5259
5260 @property
5261 def loggableCruiseAltitude(self):
5262 """Get the cruise altitude that can be logged."""
5263 if self._cruisePage.activated:
5264 return self._cruisePage.loggableCruiseLevel * 100
5265 else:
5266 return 0
5267
5268 @property
5269 def route(self):
5270 """Get the route."""
5271 return self._routePage.route
5272
5273 @property
5274 def alternate(self):
5275 """Get the ICAO code of the alternate airport."""
5276 return self._routePage.alternate
5277
5278 @property
5279 def departureMETAR(self):
5280 """Get the METAR of the departure airport."""
5281 return self._departureBriefingPage.metar
5282
5283 @property
5284 def arrivalMETAR(self):
5285 """Get the METAR of the arrival airport."""
5286 return self._arrivalBriefingPage.metar
5287
5288 @property
5289 def departureRunway(self):
5290 """Get the departure runway."""
5291 return self._takeoffPage.runway
5292
5293 @property
5294 def sid(self):
5295 """Get the SID."""
5296 return self._takeoffPage.sid
5297
5298 @property
5299 def v1(self):
5300 """Get the V1 speed."""
5301 return self._takeoffPage.v1
5302
5303 @property
5304 def vr(self):
5305 """Get the Vr speed."""
5306 return self._takeoffPage.vr
5307
5308 @property
5309 def v2(self):
5310 """Get the V2 speed."""
5311 return self._takeoffPage.v2
5312
5313 @property
5314 def derate(self):
5315 """Get the derate value."""
5316 return self._takeoffPage.derate
5317
5318 @property
5319 def takeoffAntiIceOn(self):
5320 """Get whether the anti-ice system was on during take-off."""
5321 return self._takeoffPage.antiIceOn
5322
5323 @takeoffAntiIceOn.setter
5324 def takeoffAntiIceOn(self, value):
5325 """Set anti-ice on indicator."""
5326 self._takeoffPage.antiIceOn = value
5327
5328 @property
5329 def rtoIndicated(self):
5330 """Get whether the pilot has indicated that an RTO has occured."""
5331 return self._takeoffPage.rtoIndicated
5332
5333 @property
5334 def arrivalRunway(self):
5335 """Get the arrival runway."""
5336 return self._landingPage.runway
5337
5338 @property
5339 def star(self):
5340 """Get the STAR."""
5341 return self._landingPage.star
5342
5343 @property
5344 def transition(self):
5345 """Get the transition."""
5346 return self._landingPage.transition
5347
5348 @property
5349 def approachType(self):
5350 """Get the approach type."""
5351 return self._landingPage.approachType
5352
5353 @property
5354 def vref(self):
5355 """Get the Vref speed."""
5356 return self._landingPage.vref
5357
5358 @property
5359 def landingAntiIceOn(self):
5360 """Get whether the anti-ice system was on during landing."""
5361 return self._landingPage.antiIceOn
5362
5363 @landingAntiIceOn.setter
5364 def landingAntiIceOn(self, value):
5365 """Set anti-ice on indicator."""
5366 self._landingPage.antiIceOn = value
5367
5368 @property
5369 def flightType(self):
5370 """Get the flight type."""
5371 return self._finishPage.flightType
5372
5373 @property
5374 def online(self):
5375 """Get whether the flight was online or not."""
5376 return self._finishPage.online
5377
5378 @property
5379 def usingSimBrief(self):
5380 """Indicate if we are using a SimBrief briefing or not."""
5381 return self._usingSimBrief
5382
5383 @usingSimBrief.setter
5384 def usingSimBrief(self, x):
5385 """Set whether we are using a SimBrief briefing or not."""
5386 self._usingSimBrief = x
5387
5388 def nextPage(self, finalize = True):
5389 """Go to the next page."""
5390 nextPageID = self._pages[self._currentPage].nextPageID
5391 self.jumpPage(1 if nextPageID is None else nextPageID, finalize)
5392
5393 def jumpPage(self, countOrID, finalize = True, fromPageShift = None):
5394 """Go to the page which is 'count' pages after the current one."""
5395 if isinstance(countOrID, str):
5396 targetIndex = self._getIndexOf(countOrID)
5397 else:
5398 targetIndex = self._currentPage + countOrID
5399 self.setCurrentPage(targetIndex,
5400 finalize = finalize, fromPageShift = fromPageShift)
5401
5402 def grabDefault(self):
5403 """Make the default button of the current page the default."""
5404 self._pages[self._currentPage].grabDefault()
5405
5406 def connected(self, fsType, descriptor):
5407 """Called when the connection could be made to the simulator."""
5408 self.nextPage()
5409
5410 def reset(self, loginResult):
5411 """Resets the wizard to go back to the login page."""
5412 self._initialize(keepLoginResult = loginResult is None,
5413 loginResult = loginResult)
5414
5415 def setStage(self, stage):
5416 """Set the flight stage to the given one."""
5417 if stage!=const.STAGE_END:
5418 self._cruisePage.setLoggable(Flight.canLogCruiseAltitude(stage))
5419
5420 if stage==const.STAGE_TAKEOFF:
5421 self._takeoffPage.allowForward()
5422 elif stage==const.STAGE_LANDING:
5423 if not self._arrivalBriefingPage.metarEdited:
5424 print "Downloading arrival METAR again"
5425 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
5426 [self._bookedFlight.arrivalICAO])
5427
5428 elif stage==const.STAGE_END:
5429 for page in self._pages:
5430 page.flightEnded()
5431
5432 def _initialize(self, keepLoginResult = False, loginResult = None):
5433 """Initialize the wizard."""
5434 if not keepLoginResult:
5435 self._loginResult = loginResult
5436
5437 self._loginCallback = None
5438
5439 self._fleet = None
5440 self._fleetCallback = None
5441
5442 self._bookedFlight = None
5443 self._departureGate = "-"
5444 self._fuelData = None
5445 self._departureNOTAMs = None
5446 self._departureMETAR = None
5447 self._arrivalNOTAMs = None
5448 self._arrivalMETAR = None
5449 self._usingSimBrief = None
5450 self.takeoffRunway = None
5451 self.landingRunway = None
5452
5453 firstPage = 0 if self._loginResult is None else 1
5454 for page in self._pages[firstPage:]:
5455 page.reset()
5456
5457 self.setCurrentPage(firstPage)
5458 #self.setCurrentPage(10)
5459
5460 def login(self, callback, pilotID, password):
5461 """Called when the login button was clicked."""
5462 self._loginCallback = callback
5463 if pilotID is None:
5464 loginResult = self._loginResult
5465 assert loginResult is not None and loginResult.loggedIn
5466 pilotID = loginResult.pilotID
5467 password = loginResult.password
5468 busyMessage = xstr("reload_busy")
5469 else:
5470 self._loginResult = None
5471 busyMessage = xstr("login_busy")
5472
5473 self.gui.beginBusy(busyMessage)
5474
5475 self.gui.webHandler.login(self._loginResultCallback,
5476 pilotID, password)
5477
5478 def reloadFlights(self, callback):
5479 """Reload the flights from the MAVA server."""
5480 self.login(callback, None, None)
5481
5482 def reflyFlight(self, bookedFlight):
5483 """Add the given booked flight to the flight selection page."""
5484 self._removePendingFlight(bookedFlight)
5485 self._flightSelectionPage._reflyFlight(bookedFlight)
5486
5487 def deleteFlight(self, bookedFlight):
5488 """Remove the given flight from the pending flight list."""
5489 self._removePendingFlight(bookedFlight)
5490 self._flightSelectionPage._updatePending()
5491
5492 def cancelFlight(self, reloadCallback):
5493 """Cancel the flight.
5494
5495 If it is an entry exam flight, we go back to the student page.
5496 Otherwise we reload the flights."""
5497 if self.entranceExam:
5498 self.reset(None)
5499 self.jumpPage("student")
5500 else:
5501 self.reloadFlights(reloadCallback)
5502
5503 def cruiseLevelChanged(self):
5504 """Called when the cruise level is changed."""
5505 return self.gui.cruiseLevelChanged()
5506
5507 def metarChanged(self, metar, originator):
5508 """Called when a METER is changed on on of the pages.
5509
5510 originator is the page that originated the changed. It will be used to
5511 determine which METAR (departure or arrival) has changed."""
5512 metar = metar.upper()
5513 if originator in [self._departureBriefingPage, self._takeoffPage]:
5514 self.departureMETARChanged(metar, originator)
5515 else:
5516 self.arrivalMETARChanged(metar, originator)
5517
5518 def departureMETARChanged(self, metar, originator):
5519 """Called when the departure METAR has been edited on one of the
5520 pages.
5521
5522 originator is the page that originated the change. It will not be
5523 called to set the METAR, while others will be."""
5524 for page in [self._departureBriefingPage, self._takeoffPage]:
5525 if page is not originator:
5526 page.changeMETAR(metar)
5527
5528 def arrivalMETARChanged(self, metar, originator):
5529 """Called when the arrival METAR has been edited on one of the
5530 pages.
5531
5532 originator is the page that originated the change. It will not be
5533 called to set the METAR, while others will be."""
5534 for page in [self._arrivalBriefingPage, self._landingPage]:
5535 if page is not originator:
5536 page.changeMETAR(metar)
5537
5538 def _removePendingFlight(self, flight):
5539 """Remove the given pending flight from the login result."""
5540 for flights in [self._loginResult.reportedFlights,
5541 self._loginResult.rejectedFlights]:
5542 for f in flights:
5543 if f.id==flight.id:
5544 flights.remove(f)
5545 return
5546
5547 def _loginResultCallback(self, returned, result):
5548 """The login result callback, called in the web handler's thread."""
5549 gobject.idle_add(self._handleLoginResult, returned, result)
5550
5551 def _handleLoginResult(self, returned, result):
5552 """Handle the login result."""
5553 self.gui.endBusy()
5554 isReload = self._loginResult is not None
5555 if returned:
5556 if result.loggedIn:
5557 self._loginResult = result
5558 else:
5559 if isReload:
5560 message = xstr("reload_failed")
5561 else:
5562 message = xstr("login_entranceExam_invalid"
5563 if self.entranceExam else
5564 xstr("login_invalid"))
5565 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
5566 type = MESSAGETYPE_ERROR,
5567 message_format = message)
5568 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
5569 dialog.set_title(WINDOW_TITLE_BASE)
5570 if isReload:
5571 secondary = xstr("reload_failed_sec")
5572 else:
5573 secondary = xstr("login_entranceExam_invalid_sec"
5574 if self.entranceExam else
5575 xstr("login_invalid_sec"))
5576 dialog.format_secondary_markup(secondary)
5577 dialog.run()
5578 dialog.hide()
5579 else:
5580 message = xstr("reload_failconn") if isReload \
5581 else xstr("login_failconn")
5582 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
5583 type = MESSAGETYPE_ERROR,
5584 message_format = message)
5585 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
5586 dialog.set_title(WINDOW_TITLE_BASE)
5587 secondary = xstr("reload_failconn_sec") if isReload \
5588 else xstr("login_failconn_sec")
5589 dialog.format_secondary_markup(secondary)
5590
5591 dialog.run()
5592 dialog.hide()
5593
5594 callback = self._loginCallback
5595 self._loginCallback = None
5596 callback(returned, result)
5597
5598 def getFleet(self, callback, force = False):
5599 """Get the fleet via the GUI and call the given callback."""
5600 self._fleetCallback = callback
5601 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
5602
5603 def _fleetRetrieved(self, fleet):
5604 """Callback for the fleet retrieval."""
5605 self._fleet = fleet
5606 if self._fleetCallback is not None:
5607 self._fleetCallback(fleet)
5608 self._fleetCallback = None
5609
5610 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
5611 """Update the given plane's gate information."""
5612 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
5613 callback = callback)
5614
5615 def updateRTO(self):
5616 """Update the RTO state.
5617
5618 The RTO checkbox will be enabled if the flight has an RTO state and the
5619 comments field contains some text."""
5620 flight = self.gui.flight
5621 rtoEnabled = flight is not None and flight.hasRTO and \
5622 self.gui.hasComments
5623 self._takeoffPage.setRTOEnabled(rtoEnabled)
5624
5625 def commentsChanged(self):
5626 """Called when the comments have changed."""
5627 self.updateRTO()
5628 self._finishPage.updateButtons()
5629
5630 def delayCodesChanged(self):
5631 """Called when the delay codes have changed."""
5632 self._finishPage.updateButtons()
5633
5634 def faultExplanationsChanged(self):
5635 """Called when the faults and their explanations have changed."""
5636 self._finishPage.updateButtons()
5637
5638 def rtoToggled(self, indicated):
5639 """Called when the RTO indication has changed."""
5640 self.gui.rtoToggled(indicated)
5641
5642 def _connectSimulator(self, simulatorType):
5643 """Connect to the simulator."""
5644 self.gui.connectSimulator(self._bookedFlight, simulatorType)
5645
5646 def _arrivalMETARCallback(self, returned, result):
5647 """Called when the METAR of the arrival airport is retrieved."""
5648 gobject.idle_add(self._handleArrivalMETAR, returned, result)
5649
5650 def _handleArrivalMETAR(self, returned, result):
5651 """Called when the METAR of the arrival airport is retrieved."""
5652 icao = self._bookedFlight.arrivalICAO
5653 if returned and icao in result.metars:
5654 metar = result.metars[icao]
5655 if metar!="":
5656 self._arrivalBriefingPage.setMETAR(metar)
5657
5658 def _getIndexOf(self, pageID):
5659 """Get the index for the given page ID.
5660
5661 It is an assertion failure if the ID is not found."""
5662 for index in range(0, len(self._pages)):
5663 page = self._pages[index]
5664 if page.id==pageID:
5665 return index
5666 assert False
5667
5668#-----------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.