source: src/mlx/gui/flight.py@ 770:7b14dd1f67ec

Last change on this file since 770:7b14dd1f67ec was 769:d10b450fff75, checked in by István Váradi <ivaradi@…>, 9 years ago

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