source: src/mlx/gui/flight.py@ 805:41c3bffe8a25

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

Simplified SimBrief handling by using the CEF API directly (re #310)

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