source: src/mlx/gui/flight.py@ 771:76aae72ccaba

Last change on this file since 771:76aae72ccaba was 771:76aae72ccaba, checked in by István Váradi <ivaradi@…>, 9 years ago

Fixed the handling of the login information after registering (re #285)

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