source: src/mlx/gui/flight.py@ 820:7d1110d3a802

Last change on this file since 820:7d1110d3a802 was 819:024e8697000e, checked in by István Váradi <ivaradi@…>, 8 years ago

The basics of the pending flight window work (re #307).

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