source: src/mlx/gui/flight.py@ 775:60bd73d51935

Last change on this file since 775:60bd73d51935 was 772:9106f5dbd4c0, checked in by István Váradi <ivaradi@…>, 9 years ago

The status of the entry exam is updated by the student page (re #285)

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