source: src/mlx/gui/flight.py@ 790:e69a08004f40

Last change on this file since 790:e69a08004f40 was 777:16847856b207, checked in by István Váradi <ivaradi@…>, 9 years ago

Fixed the PIREP auto-saving (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, page):
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(page, 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 def _getSaveDialog(self):
4502 """Get the PIREP saving dialog.
4503
4504 If it does not exist yet, create it."""
4505 if self._savePIREPDialog is None:
4506 gui = self._wizard.gui
4507 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
4508 xstr("finish_save_title"),
4509 action = FILE_CHOOSER_ACTION_SAVE,
4510 buttons = (gtk.STOCK_CANCEL,
4511 RESPONSETYPE_CANCEL,
4512 gtk.STOCK_OK, RESPONSETYPE_OK),
4513 parent = gui.mainWindow)
4514 dialog.set_modal(True)
4515 dialog.set_do_overwrite_confirmation(True)
4516
4517 filter = gtk.FileFilter()
4518 filter.set_name(xstr("file_filter_pireps"))
4519 filter.add_pattern("*.pirep")
4520 dialog.add_filter(filter)
4521
4522 filter = gtk.FileFilter()
4523 filter.set_name(xstr("file_filter_all"))
4524 filter.add_pattern("*.*")
4525 dialog.add_filter(filter)
4526
4527 self._savePIREPDialog = dialog
4528
4529 return self._savePIREPDialog
4530
4531#-----------------------------------------------------------------------------
4532
4533class FinishPage(Page):
4534 """Flight finish page."""
4535 _flightTypes = [ ("flighttype_scheduled", const.FLIGHTTYPE_SCHEDULED),
4536 ("flighttype_ot", const.FLIGHTTYPE_OLDTIMER),
4537 ("flighttype_vip", const.FLIGHTTYPE_VIP),
4538 ("flighttype_charter", const.FLIGHTTYPE_CHARTER) ]
4539
4540 def __init__(self, wizard, saveHelper):
4541 """Construct the finish page."""
4542 help = xstr("finish_help") + xstr("finish_help_goodtime")
4543 super(FinishPage, self).__init__(wizard, "finish",
4544 xstr("finish_title"), help)
4545
4546 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
4547 xscale = 0.0, yscale = 0.0)
4548
4549 table = gtk.Table(10, 2)
4550 table.set_row_spacings(4)
4551 table.set_col_spacings(16)
4552 table.set_homogeneous(False)
4553 alignment.add(table)
4554 self.setMainWidget(alignment)
4555
4556 row = 0
4557
4558 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4559 label = gtk.Label(xstr("finish_rating"))
4560 labelAlignment.add(label)
4561 table.attach(labelAlignment, 0, 1, row, row+1)
4562
4563 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4564 self._flightRating = gtk.Label()
4565 self._flightRating.set_width_chars(8)
4566 self._flightRating.set_alignment(0.0, 0.5)
4567 self._flightRating.set_use_markup(True)
4568 labelAlignment.add(self._flightRating)
4569 table.attach(labelAlignment, 1, 2, row, row+1)
4570
4571 row += 1
4572
4573 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4574 label = gtk.Label(xstr("finish_dep_time"))
4575 labelAlignment.add(label)
4576 table.attach(labelAlignment, 0, 1, row, row+1)
4577
4578 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4579 self._depTime = gtk.Label()
4580 self._depTime.set_width_chars(13)
4581 self._depTime.set_alignment(0.0, 0.5)
4582 self._depTime.set_use_markup(True)
4583 self._depTime.set_tooltip_markup(xstr("finish_dep_time_tooltip"))
4584 labelAlignment.add(self._depTime)
4585 table.attach(labelAlignment, 1, 2, row, row+1)
4586
4587 row += 1
4588
4589 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4590 label = gtk.Label(xstr("finish_flight_time"))
4591 labelAlignment.add(label)
4592 table.attach(labelAlignment, 0, 1, row, row+1)
4593
4594 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4595 self._flightTime = gtk.Label()
4596 self._flightTime.set_width_chars(10)
4597 self._flightTime.set_alignment(0.0, 0.5)
4598 self._flightTime.set_use_markup(True)
4599 labelAlignment.add(self._flightTime)
4600 table.attach(labelAlignment, 1, 2, row, row+1)
4601
4602 row += 1
4603
4604 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4605 label = gtk.Label(xstr("finish_block_time"))
4606 labelAlignment.add(label)
4607 table.attach(labelAlignment, 0, 1, row, row+1)
4608
4609 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4610 self._blockTime = gtk.Label()
4611 self._blockTime.set_width_chars(10)
4612 self._blockTime.set_alignment(0.0, 0.5)
4613 self._blockTime.set_use_markup(True)
4614 labelAlignment.add(self._blockTime)
4615 table.attach(labelAlignment, 1, 2, row, row+1)
4616
4617 row += 1
4618
4619 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4620 label = gtk.Label(xstr("finish_arr_time"))
4621 labelAlignment.add(label)
4622 table.attach(labelAlignment, 0, 1, row, row+1)
4623
4624 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4625 self._arrTime = gtk.Label()
4626 self._arrTime.set_width_chars(13)
4627 self._arrTime.set_alignment(0.0, 0.5)
4628 self._arrTime.set_use_markup(True)
4629 self._arrTime.set_tooltip_markup(xstr("finish_arr_time_tooltip"))
4630 labelAlignment.add(self._arrTime)
4631 table.attach(labelAlignment, 1, 2, row, row+1)
4632
4633 row += 1
4634
4635 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4636 label = gtk.Label(xstr("finish_distance"))
4637 labelAlignment.add(label)
4638 table.attach(labelAlignment, 0, 1, row, row+1)
4639
4640 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4641 self._distanceFlown = gtk.Label()
4642 self._distanceFlown.set_width_chars(10)
4643 self._distanceFlown.set_alignment(0.0, 0.5)
4644 self._distanceFlown.set_use_markup(True)
4645 labelAlignment.add(self._distanceFlown)
4646 table.attach(labelAlignment, 1, 2, row, row+1)
4647
4648 row += 1
4649
4650 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
4651 label = gtk.Label(xstr("finish_fuel"))
4652 labelAlignment.add(label)
4653 table.attach(labelAlignment, 0, 1, row, row+1)
4654
4655 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4656 self._fuelUsed = gtk.Label()
4657 self._fuelUsed.set_width_chars(10)
4658 self._fuelUsed.set_alignment(0.0, 0.5)
4659 self._fuelUsed.set_use_markup(True)
4660 labelAlignment.add(self._fuelUsed)
4661 table.attach(labelAlignment, 1, 2, row, row+1)
4662
4663 row += 1
4664
4665 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
4666 yalign = 0.5, yscale = 0.0)
4667 label = gtk.Label(xstr("finish_type"))
4668 label.set_use_underline(True)
4669 labelAlignment.add(label)
4670 table.attach(labelAlignment, 0, 1, row, row+1)
4671
4672 flightTypeModel = gtk.ListStore(str, int)
4673 for (name, type) in FinishPage._flightTypes:
4674 flightTypeModel.append([xstr(name), type])
4675
4676 self._flightType = gtk.ComboBox(model = flightTypeModel)
4677 renderer = gtk.CellRendererText()
4678 self._flightType.pack_start(renderer, True)
4679 self._flightType.add_attribute(renderer, "text", 0)
4680 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
4681 self._flightType.set_active(0)
4682 self._flightType.connect("changed", self._flightTypeChanged)
4683 flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4684 flightTypeAlignment.add(self._flightType)
4685 table.attach(flightTypeAlignment, 1, 2, row, row+1)
4686 label.set_mnemonic_widget(self._flightType)
4687
4688 row += 1
4689
4690 self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
4691 self._onlineFlight.set_use_underline(True)
4692 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
4693 onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
4694 onlineFlightAlignment.add(self._onlineFlight)
4695 table.attach(onlineFlightAlignment, 1, 2, row, row + 1)
4696
4697 row += 1
4698
4699 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
4700 yalign = 0.5, yscale = 0.0)
4701 self._gateLabel = gtk.Label(xstr("finish_gate"))
4702 self._gateLabel.set_use_underline(True)
4703 labelAlignment.add(self._gateLabel)
4704 table.attach(labelAlignment, 0, 1, row, row+1)
4705
4706 self._gatesModel = gtk.ListStore(str)
4707
4708 self._gate = gtk.ComboBox(model = self._gatesModel)
4709 renderer = gtk.CellRendererText()
4710 self._gate.pack_start(renderer, True)
4711 self._gate.add_attribute(renderer, "text", 0)
4712 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
4713 self._gate.connect("changed", self._gateChanged)
4714 gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
4715 gateAlignment.add(self._gate)
4716 table.attach(gateAlignment, 1, 2, row, row+1)
4717 self._gateLabel.set_mnemonic_widget(self._gate)
4718
4719 self.addButton(xstr("finish_newFlight"),
4720 sensitive = True,
4721 clicked = self._newFlightClicked,
4722 tooltip = xstr("finish_newFlight_tooltip"),
4723 padding = 16)
4724
4725 self.addPreviousButton(clicked = self._backClicked)
4726
4727 self._saveHelper = saveHelper
4728 self._saveButton = saveHelper.addButton(self)
4729
4730 self._tooBigTimeDifference = False
4731 self._deferredAutoSave = False
4732 self._pirepSaved = False
4733 self._pirepSent = False
4734
4735 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
4736 sensitive = False,
4737 clicked = self._sendClicked,
4738 tooltip = xstr("sendPIREP_tooltip"))
4739
4740 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 10*60.0)
4741 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 20*60.0)
4742 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), 0*60.0)
4743 # self._formatTime(datetime.datetime(1970, 1, 1, 0, 10), (23*60.0+50)*60.0)
4744 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (1*60.0+5)*60.0)
4745 # self._formatTime(datetime.datetime(1970, 1, 1, 1, 0), (0*60.0+50)*60.0)
4746 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (0*60.0+5)*60.0)
4747 # self._formatTime(datetime.datetime(1970, 1, 1, 23, 55), (23*60.0+45)*60.0)
4748
4749 @property
4750 def flightType(self):
4751 """Get the flight type."""
4752 index = self._flightType.get_active()
4753 return None if index<0 else self._flightType.get_model()[index][1]
4754
4755 @property
4756 def online(self):
4757 """Get whether the flight was an online flight or not."""
4758 return self._onlineFlight.get_active()
4759
4760 def activate(self):
4761 """Activate the page."""
4762 self._deferredAutoSave = False
4763 self._pirepSaved = False
4764 self._pirepSent = False
4765
4766 flight = self._wizard.gui._flight
4767 rating = flight.logger.getRating()
4768 if rating<0:
4769 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
4770 else:
4771 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
4772
4773 flightLength = flight.flightTimeEnd - flight.flightTimeStart
4774 self._flightTime.set_markup("<b>%s</b>" % \
4775 (util.getTimeIntervalString(flightLength),))
4776
4777 blockLength = flight.blockTimeEnd - flight.blockTimeStart
4778 self._blockTime.set_markup("<b>%s</b>" % \
4779 (util.getTimeIntervalString(blockLength),))
4780
4781 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
4782 (flight.flownDistance,))
4783
4784 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
4785 (flight.startFuel - flight.endFuel,))
4786
4787 self._flightType.set_active(-1)
4788 self._onlineFlight.set_active(self._wizard.loggedIn)
4789
4790 self._gatesModel.clear()
4791 if self._wizard.gui.config.onlineGateSystem and \
4792 self._wizard.loggedIn and \
4793 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
4794 not self._wizard.entranceExam:
4795 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
4796 for gate in lhbpGates.gates:
4797 if gate.isAvailable(lhbpGates, occupiedGateNumbers):
4798 self._gatesModel.append([gate.number])
4799 self._gateLabel.set_sensitive(True)
4800 self._gate.set_sensitive(True)
4801 self._gate.set_active(-1)
4802 else:
4803 self._gateLabel.set_sensitive(False)
4804 self._gate.set_sensitive(False)
4805
4806 self._updateTimes()
4807
4808 def updateButtons(self):
4809 """Update the sensitivity state of the buttons."""
4810 gui = self._wizard.gui
4811 faultsExplained = gui.faultsFullyExplained
4812 timesCorrect = self.flightType is None or \
4813 not self._tooBigTimeDifference or \
4814 gui.hasComments or gui.hasDelayCode
4815 sensitive = gui.flight is not None and \
4816 gui.flight.stage==const.STAGE_END and \
4817 self._flightType.get_active()>=0 and \
4818 (self._gatesModel.get_iter_first() is None or
4819 self._gate.get_active()>=0) and \
4820 faultsExplained and timesCorrect
4821
4822 self._updateHelp(faultsExplained, timesCorrect)
4823
4824 wasSensitive = self._saveButton.get_sensitive()
4825
4826 if gui.config.pirepAutoSave and sensitive and not wasSensitive:
4827 if gui.isWizardActive():
4828 self._saveHelper.autoSavePIREP(self)
4829 else:
4830 self._deferredAutoSave = True
4831
4832 if not sensitive:
4833 self._deferredAutoSave = False
4834
4835 self._saveButton.set_sensitive(sensitive)
4836 self._sendButton.set_sensitive(sensitive and
4837 self._wizard.bookedFlight.id is not None)
4838
4839 def grabDefault(self):
4840 """If the page has a default button, make it the default one."""
4841 super(FinishPage, self).grabDefault()
4842 if self._deferredAutoSave:
4843 self._saveHelper.autoSavePIREP(self)
4844 self._deferredAutoSave = False
4845
4846 def setPIREPSaved(self):
4847 """Mark the PIREP as saved."""
4848 self._pirepSaved = True
4849
4850 def _backClicked(self, button):
4851 """Called when the Back button is pressed."""
4852 self.goBack()
4853
4854 def _flightTypeChanged(self, comboBox):
4855 """Called when the flight type has changed."""
4856 self._updateTimes()
4857
4858 def _gateChanged(self, comboBox):
4859 """Called when the arrival gate has changed."""
4860 self.updateButtons()
4861
4862 def _newFlightClicked(self, button):
4863 """Called when the new flight button is clicked."""
4864 gui = self._wizard.gui
4865 if not self._pirepSent and not self._pirepSaved:
4866 dialog = gtk.MessageDialog(parent = gui.mainWindow,
4867 type = MESSAGETYPE_QUESTION,
4868 message_format = xstr("finish_newFlight_question"))
4869
4870 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
4871 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
4872
4873 dialog.set_title(WINDOW_TITLE_BASE)
4874 result = dialog.run()
4875 dialog.hide()
4876 if result!=RESPONSETYPE_YES:
4877 return
4878
4879 gui.reset()
4880
4881 def _sendClicked(self, button):
4882 """Called when the Send button is clicked."""
4883 pirep = PIREP(self._wizard.gui.flight)
4884 self._wizard.gui.sendPIREP(pirep,
4885 callback = self._handlePIREPSent)
4886
4887 def _handlePIREPSent(self, returned, result):
4888 """Callback for the PIREP sending result."""
4889 self._pirepSent = returned and result.success
4890 if self._wizard.gui.config.onlineGateSystem and \
4891 self._wizard.loggedIn and not self._wizard.entranceExam and \
4892 returned and result.success:
4893 bookedFlight = self._wizard.bookedFlight
4894 if bookedFlight.arrivalICAO=="LHBP":
4895 iter = self._gate.get_active_iter()
4896 gateNumber = None if iter is None \
4897 else self._gatesModel.get_value(iter, 0)
4898
4899 status = const.PLANE_PARKING if gateNumber is None \
4900 else const.PLANE_HOME
4901 else:
4902 gateNumber = None
4903 status = const.PLANE_AWAY
4904
4905 self._wizard.updatePlane(self._planeUpdated,
4906 bookedFlight.tailNumber,
4907 status, gateNumber = gateNumber)
4908
4909 def _planeUpdated(self, success):
4910 """Callback for the plane updating."""
4911 pass
4912
4913 def _formatTime(self, scheduledTime, realTimestamp, (warning, error)):
4914 """Format the departure or arrival time based on the given data as a
4915 markup for a label."""
4916 realTime = time.gmtime(realTimestamp)
4917
4918 if warning:
4919 colour = "red" if error else "orange"
4920 markupBegin = '<span foreground="%s">' % (colour,)
4921 markupEnd = '</span>'
4922 else:
4923 markupBegin = markupEnd = ""
4924
4925 markup = "<b>%s%02d:%02d [%02d:%02d]%s</b>" % \
4926 (markupBegin,
4927 realTime.tm_hour, realTime.tm_min,
4928 scheduledTime.hour, scheduledTime.minute,
4929 markupEnd)
4930
4931 return markup
4932
4933 def _updateTimes(self):
4934 """Format the flight times and the help text according to the flight
4935 type.
4936
4937 The buttons are also updated.
4938 """
4939 flight = self._wizard.gui._flight
4940 bookedFlight = flight.bookedFlight
4941
4942 (departureWarning, departureError) = flight.blockTimeStartWrong
4943 (arrivalWarning, arrivalError) = flight.blockTimeEndWrong
4944
4945 if self.flightType==const.FLIGHTTYPE_VIP:
4946 departureError = arrivalError = False
4947
4948 self._tooBigTimeDifference = departureError or arrivalError
4949
4950 self._depTime.set_markup(self._formatTime(bookedFlight.departureTime,
4951 flight.blockTimeStart,
4952 (departureWarning,
4953 departureError)))
4954
4955 self._arrTime.set_markup(self._formatTime(bookedFlight.arrivalTime,
4956 flight.blockTimeEnd,
4957 (arrivalWarning,
4958 arrivalError)))
4959
4960 self.updateButtons()
4961
4962 def _updateHelp(self, faultsExplained, timesCorrect):
4963 """Update the help text according to the actual situation."""
4964 if not faultsExplained:
4965 self.setHelp(xstr("finish_help") + xstr("finish_help_faults"))
4966 elif not timesCorrect:
4967 self.setHelp(xstr("finish_help") + xstr("finish_help_wrongtime"))
4968 else:
4969 self.setHelp(xstr("finish_help") + xstr("finish_help_goodtime"))
4970
4971
4972#-----------------------------------------------------------------------------
4973
4974class CheckFlightFinishPage(Page):
4975 """Finish page for a check flight."""
4976 def __init__(self, wizard, saveHelper):
4977 """Construct the check flight finish page."""
4978 super(CheckFlightFinishPage, self).__init__(wizard,
4979 "chkfinish",
4980 xstr("chkfinish_title"),
4981 "")
4982
4983 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
4984 xscale = 1.0, yscale = 1.0)
4985 self._label = gtk.Label()
4986 alignment.add(self._label)
4987
4988 self.setMainWidget(alignment)
4989
4990 self._saveHelper = saveHelper
4991 self._saveButton = saveHelper.addButton(self)
4992
4993 self._button = self.addNextButton(sensitive = False,
4994 clicked = self._forwardClicked)
4995
4996 def activate(self):
4997 """Activate the page."""
4998 wizard = self._wizard
4999 loginResult = wizard.loginResult
5000 gui = wizard.gui
5001 rating = gui.flight.logger.getRating()
5002
5003 if rating>=0:
5004 loginResult.checkFlightStatus = True
5005
5006 firstOfficer = \
5007 loginResult.entryExamPassed and loginResult.checkFlightStatus
5008
5009 if firstOfficer:
5010 loginResult.rank = "FO"
5011
5012 if rating<0:
5013 mainMessage = xstr("chkfinish_failed")
5014 else:
5015 mainMessage = xstr("chkfinish_passed_begin")
5016 if firstOfficer:
5017 mainMessage += xstr("chkfinish_passed_fo")
5018 mainMessage += xstr("chkfinish_passed_end")
5019
5020 if firstOfficer:
5021 nextMessage = xstr("chkfinish_next")
5022 else:
5023 nextMessage = xstr("chkfinish_next_student_begin")
5024 if not loginResult.entryExamPassed and \
5025 not loginResult.checkFlightStatus:
5026 nextMessage += xstr("chkfinish_next_student_nothing")
5027 elif loginResult.entryExamPassed and \
5028 not loginResult.checkFlightStatus:
5029 nextMessage += xstr("chkfinish_next_student_no_flight")
5030 elif not loginResult.entryExamPassed and \
5031 loginResult.checkFlightStatus:
5032 nextMessage += xstr("chkfinish_next_student_no_exam")
5033
5034 self._label.set_text(mainMessage +
5035 xstr("chkfinish_savepirep") +
5036 nextMessage)
5037 self._label.set_use_markup(True)
5038 self._label.set_alignment(0.5, 0.0)
5039
5040 self._saveButton.set_sensitive(True)
5041 self._button.set_sensitive(True)
5042
5043 def _forwardClicked(self, button):
5044 """Jump to the student page if there are some tasks to do,
5045 or to the flight selection page, if the pilot is allowed to perform
5046 MAVA flights."""
5047 wizard = self._wizard
5048 gui = wizard.gui
5049
5050 loginResult = wizard.loginResult
5051 if loginResult.checkFlightStatus:
5052 gui.beginBusy(xstr("chkfinish_updateweb_busy"))
5053 gui.webHandler.setCheckFlightPassed(self._checkFlightPassedSetCallback,
5054 wizard.checkFlightAircraftType)
5055 else:
5056 self._resetGUI()
5057
5058 def _checkFlightPassedSetCallback(self, returned, result):
5059 """Called when the check flight status has been set."""
5060 gobject.idle_add(self._checkFlightPassedSet, returned, result)
5061
5062 def _checkFlightPassedSet(self, returned, result):
5063 """Handle the result of an attempt to set the check flight status."""
5064 gui = self._wizard.gui
5065
5066 gui.endBusy()
5067
5068 if returned:
5069 self._resetGUI()
5070 else:
5071 dialog = gtk.MessageDialog(parent = gui.mainWindow,
5072 type = MESSAGETYPE_ERROR,
5073 message_format =
5074 xstr("chkfinish_passedset_failed"))
5075 dialog.set_title(WINDOW_TITLE_BASE + " - " +
5076 xstr("chkfinish_passedset_failed_title"))
5077 dialog.format_secondary_markup(xstr("chkfinish_passedset_failed_secondary"))
5078
5079 dialog.add_button(xstr("button_ok"), 0)
5080
5081 dialog.run()
5082 dialog.hide()
5083
5084 def _resetGUI(self):
5085 """Reset the GUI."""
5086 gui = self._wizard.gui
5087 gui.reset()
5088
5089#-----------------------------------------------------------------------------
5090
5091class Wizard(gtk.VBox):
5092 """The flight wizard."""
5093 def __init__(self, gui):
5094 """Construct the wizard."""
5095 super(Wizard, self).__init__()
5096
5097 self.gui = gui
5098
5099 self._pages = []
5100 self._currentPage = None
5101
5102 self._loginPage = LoginPage(self)
5103 self._pages.append(self._loginPage)
5104 self._pages.append(FlightSelectionPage(self))
5105 self._pages.append(GateSelectionPage(self))
5106 self._pages.append(RegisterPage(self))
5107 self._studentPage = StudentPage(self)
5108 self._pages.append(self._studentPage)
5109 self._pages.append(ConnectPage(self))
5110 self._payloadPage = PayloadPage(self)
5111 self._pages.append(self._payloadPage)
5112 self._payloadIndex = len(self._pages)
5113 self._pages.append(TimePage(self))
5114 self._routePage = RoutePage(self)
5115 self._pages.append(self._routePage)
5116 self._simBriefSetupPage = SimBriefSetupPage(self)
5117 self._pages.append(self._simBriefSetupPage)
5118 self._simBriefingPage = SimBriefingPage(self)
5119 self._pages.append(self._simBriefingPage)
5120 self._pages.append(FuelPage(self))
5121 self._departureBriefingPage = BriefingPage(self, True)
5122 self._pages.append(self._departureBriefingPage)
5123 self._arrivalBriefingPage = BriefingPage(self, False)
5124 self._pages.append(self._arrivalBriefingPage)
5125 self._arrivalBriefingIndex = len(self._pages)
5126 self._takeoffPage = TakeoffPage(self)
5127 self._pages.append(self._takeoffPage)
5128 self._cruisePage = CruisePage(self)
5129 self._pages.append(self._cruisePage)
5130 self._landingPage = LandingPage(self)
5131 self._pages.append(self._landingPage)
5132
5133 pirepSaveHelper = PIREPSaveHelper(self)
5134
5135 self._finishPage = FinishPage(self, pirepSaveHelper)
5136 self._pages.append(self._finishPage)
5137 self._pages.append(CheckFlightFinishPage(self, pirepSaveHelper))
5138
5139 self._requestedWidth = None
5140 self._requestedHeight = None
5141
5142 self.connect("size-allocate", self._sizeAllocate)
5143
5144 for page in self._pages:
5145 page.show_all()
5146 page.setStyle()
5147
5148 self._initialize()
5149
5150 def _sizeAllocate(self, widget, allocation):
5151 if self._requestedWidth is not None and \
5152 self._requestedHeight is not None:
5153 return
5154
5155 if self._currentPage is not None:
5156 self.remove(self._pages[self._currentPage])
5157
5158 maxWidth = 0
5159 maxHeight = 0
5160 for page in self._pages:
5161 self.add(page)
5162 self.show_all()
5163 pageSizeRequest = page.size_request()
5164 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
5165 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
5166 maxWidth = max(maxWidth, width)
5167 maxHeight = max(maxHeight, height)
5168 self.remove(page)
5169
5170 if self._currentPage is not None:
5171 self.add(self._pages[self._currentPage])
5172
5173 self._requestedWidth = maxWidth
5174 self._requestedHeight = maxHeight
5175 self.set_size_request(maxWidth, maxHeight)
5176
5177 @property
5178 def pilotID(self):
5179 """Get the pilot ID, if given."""
5180 return self._loginPage.pilotID
5181
5182 @property
5183 def entranceExam(self):
5184 """Get whether an entrance exam is about to be taken."""
5185 return self._loginResult is not None and self._loginResult.rank=="STU"
5186
5187 @property
5188 def loggedIn(self):
5189 """Indicate if there was a successful login."""
5190 return self._loginResult is not None
5191
5192 @property
5193 def loginResult(self):
5194 """Get the login result."""
5195 return self._loginResult
5196
5197 @property
5198 def checkFlightAircraftType(self):
5199 """Get the type of the aircraft used to perform the check flight."""
5200 return self._studentPage.aircraftType
5201
5202 def setCurrentPage(self, index, finalize = False, fromPageShift = None):
5203 """Set the current page to the one with the given index.
5204
5205 @param fromPageShift if given, the relative index of one of the
5206 previous pages that should be used as the from-page of the next
5207 page. E.g. if fromPageShift is 1, the previous page will be the
5208 from-page."""
5209 assert index < len(self._pages)
5210
5211 fromPage = self._currentPage
5212 if fromPage is not None:
5213 page = self._pages[fromPage]
5214 if finalize and not page._completed:
5215 page.complete()
5216 self.remove(page)
5217 if fromPageShift is not None:
5218 fromPage -= fromPageShift
5219
5220 self._currentPage = index
5221 page = self._pages[index]
5222 self.add(page)
5223 if page._fromPage is None:
5224 page._fromPage = fromPage
5225 page.initialize()
5226 self.show_all()
5227 if fromPage is not None:
5228 self.grabDefault()
5229
5230 @property
5231 def bookedFlight(self):
5232 """Get the booked flight selected."""
5233 return self._bookedFlight
5234
5235 @property
5236 def numCrew(self):
5237 """Get the number of crew members."""
5238 return self._payloadPage.numCrew
5239
5240 @property
5241 def numPassengers(self):
5242 """Get the number of passengers."""
5243 return self._payloadPage.numPassengers
5244
5245 @property
5246 def bagWeight(self):
5247 """Get the baggage weight."""
5248 return self._payloadPage.bagWeight
5249
5250 @property
5251 def cargoWeight(self):
5252 """Get the cargo weight."""
5253 return self._payloadPage.cargoWeight
5254
5255 @property
5256 def mailWeight(self):
5257 """Get the mail weight."""
5258 return self._payloadPage.mailWeight
5259
5260 @property
5261 def zfw(self):
5262 """Get the calculated ZFW value."""
5263 return 0 if self._bookedFlight is None \
5264 else self._payloadPage.calculateZFW()
5265
5266 @property
5267 def filedCruiseLevel(self):
5268 """Get the filed cruise level."""
5269 return self._routePage.filedCruiseLevel
5270
5271 @property
5272 def filedCruiseAltitude(self):
5273 """Get the filed cruise altitude."""
5274 return self._routePage.filedCruiseLevel * 100
5275
5276 @property
5277 def cruiseAltitude(self):
5278 """Get the cruise altitude."""
5279 level = self._cruisePage.cruiseLevel if self._cruisePage.activated \
5280 else self._routePage.filedCruiseLevel
5281 return level * 100
5282
5283 @property
5284 def loggableCruiseAltitude(self):
5285 """Get the cruise altitude that can be logged."""
5286 if self._cruisePage.activated:
5287 return self._cruisePage.loggableCruiseLevel * 100
5288 else:
5289 return 0
5290
5291 @property
5292 def route(self):
5293 """Get the route."""
5294 return self._routePage.route
5295
5296 @property
5297 def alternate(self):
5298 """Get the ICAO code of the alternate airport."""
5299 return self._routePage.alternate
5300
5301 @property
5302 def departureMETAR(self):
5303 """Get the METAR of the departure airport."""
5304 return self._departureBriefingPage.metar
5305
5306 @property
5307 def arrivalMETAR(self):
5308 """Get the METAR of the arrival airport."""
5309 return self._arrivalBriefingPage.metar
5310
5311 @property
5312 def departureRunway(self):
5313 """Get the departure runway."""
5314 return self._takeoffPage.runway
5315
5316 @property
5317 def sid(self):
5318 """Get the SID."""
5319 return self._takeoffPage.sid
5320
5321 @property
5322 def v1(self):
5323 """Get the V1 speed."""
5324 return self._takeoffPage.v1
5325
5326 @property
5327 def vr(self):
5328 """Get the Vr speed."""
5329 return self._takeoffPage.vr
5330
5331 @property
5332 def v2(self):
5333 """Get the V2 speed."""
5334 return self._takeoffPage.v2
5335
5336 @property
5337 def derate(self):
5338 """Get the derate value."""
5339 return self._takeoffPage.derate
5340
5341 @property
5342 def takeoffAntiIceOn(self):
5343 """Get whether the anti-ice system was on during take-off."""
5344 return self._takeoffPage.antiIceOn
5345
5346 @takeoffAntiIceOn.setter
5347 def takeoffAntiIceOn(self, value):
5348 """Set anti-ice on indicator."""
5349 self._takeoffPage.antiIceOn = value
5350
5351 @property
5352 def rtoIndicated(self):
5353 """Get whether the pilot has indicated that an RTO has occured."""
5354 return self._takeoffPage.rtoIndicated
5355
5356 @property
5357 def arrivalRunway(self):
5358 """Get the arrival runway."""
5359 return self._landingPage.runway
5360
5361 @property
5362 def star(self):
5363 """Get the STAR."""
5364 return self._landingPage.star
5365
5366 @property
5367 def transition(self):
5368 """Get the transition."""
5369 return self._landingPage.transition
5370
5371 @property
5372 def approachType(self):
5373 """Get the approach type."""
5374 return self._landingPage.approachType
5375
5376 @property
5377 def vref(self):
5378 """Get the Vref speed."""
5379 return self._landingPage.vref
5380
5381 @property
5382 def landingAntiIceOn(self):
5383 """Get whether the anti-ice system was on during landing."""
5384 return self._landingPage.antiIceOn
5385
5386 @landingAntiIceOn.setter
5387 def landingAntiIceOn(self, value):
5388 """Set anti-ice on indicator."""
5389 self._landingPage.antiIceOn = value
5390
5391 @property
5392 def flightType(self):
5393 """Get the flight type."""
5394 return self._finishPage.flightType
5395
5396 @property
5397 def online(self):
5398 """Get whether the flight was online or not."""
5399 return self._finishPage.online
5400
5401 @property
5402 def usingSimBrief(self):
5403 """Indicate if we are using a SimBrief briefing or not."""
5404 return self._usingSimBrief
5405
5406 @usingSimBrief.setter
5407 def usingSimBrief(self, x):
5408 """Set whether we are using a SimBrief briefing or not."""
5409 self._usingSimBrief = x
5410
5411 def nextPage(self, finalize = True):
5412 """Go to the next page."""
5413 nextPageID = self._pages[self._currentPage].nextPageID
5414 self.jumpPage(1 if nextPageID is None else nextPageID, finalize)
5415
5416 def jumpPage(self, countOrID, finalize = True, fromPageShift = None):
5417 """Go to the page which is 'count' pages after the current one."""
5418 if isinstance(countOrID, str):
5419 targetIndex = self._getIndexOf(countOrID)
5420 else:
5421 targetIndex = self._currentPage + countOrID
5422 self.setCurrentPage(targetIndex,
5423 finalize = finalize, fromPageShift = fromPageShift)
5424
5425 def grabDefault(self):
5426 """Make the default button of the current page the default."""
5427 self._pages[self._currentPage].grabDefault()
5428
5429 def connected(self, fsType, descriptor):
5430 """Called when the connection could be made to the simulator."""
5431 self.nextPage()
5432
5433 def reset(self, loginResult):
5434 """Resets the wizard to go back to the login page."""
5435 self._initialize(keepLoginResult = loginResult is None,
5436 loginResult = loginResult)
5437
5438 def setStage(self, stage):
5439 """Set the flight stage to the given one."""
5440 if stage!=const.STAGE_END:
5441 self._cruisePage.setLoggable(Flight.canLogCruiseAltitude(stage))
5442
5443 if stage==const.STAGE_TAKEOFF:
5444 self._takeoffPage.allowForward()
5445 elif stage==const.STAGE_LANDING:
5446 if not self._arrivalBriefingPage.metarEdited:
5447 print "Downloading arrival METAR again"
5448 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
5449 [self._bookedFlight.arrivalICAO])
5450
5451 elif stage==const.STAGE_END:
5452 for page in self._pages:
5453 page.flightEnded()
5454
5455 def _initialize(self, keepLoginResult = False, loginResult = None):
5456 """Initialize the wizard."""
5457 if not keepLoginResult:
5458 self._loginResult = loginResult
5459
5460 self._loginCallback = None
5461
5462 self._fleet = None
5463 self._fleetCallback = None
5464
5465 self._bookedFlight = None
5466 self._departureGate = "-"
5467 self._fuelData = None
5468 self._departureNOTAMs = None
5469 self._departureMETAR = None
5470 self._arrivalNOTAMs = None
5471 self._arrivalMETAR = None
5472 self._usingSimBrief = None
5473 self.takeoffRunway = None
5474 self.landingRunway = None
5475
5476 firstPage = 0 if self._loginResult is None else 1
5477 for page in self._pages[firstPage:]:
5478 page.reset()
5479
5480 self.setCurrentPage(firstPage)
5481 #self.setCurrentPage(10)
5482
5483 def login(self, callback, pilotID, password):
5484 """Called when the login button was clicked."""
5485 self._loginCallback = callback
5486 if pilotID is None:
5487 loginResult = self._loginResult
5488 assert loginResult is not None and loginResult.loggedIn
5489 pilotID = loginResult.pilotID
5490 password = loginResult.password
5491 busyMessage = xstr("reload_busy")
5492 else:
5493 self._loginResult = None
5494 busyMessage = xstr("login_busy")
5495
5496 self.gui.beginBusy(busyMessage)
5497
5498 self.gui.webHandler.login(self._loginResultCallback,
5499 pilotID, password)
5500
5501 def reloadFlights(self, callback):
5502 """Reload the flights from the MAVA server."""
5503 self.login(callback, None, None)
5504
5505 def cancelFlight(self, reloadCallback):
5506 """Cancel the flight.
5507
5508 If it is an entry exam flight, we go back to the student page.
5509 Otherwise we reload the flights."""
5510 if self.entranceExam:
5511 self.reset(None)
5512 self.jumpPage("student")
5513 else:
5514 self.reloadFlights(reloadCallback)
5515
5516 def cruiseLevelChanged(self):
5517 """Called when the cruise level is changed."""
5518 return self.gui.cruiseLevelChanged()
5519
5520 def metarChanged(self, metar, originator):
5521 """Called when a METER is changed on on of the pages.
5522
5523 originator is the page that originated the changed. It will be used to
5524 determine which METAR (departure or arrival) has changed."""
5525 metar = metar.upper()
5526 if originator in [self._departureBriefingPage, self._takeoffPage]:
5527 self.departureMETARChanged(metar, originator)
5528 else:
5529 self.arrivalMETARChanged(metar, originator)
5530
5531 def departureMETARChanged(self, metar, originator):
5532 """Called when the departure METAR has been edited on one of the
5533 pages.
5534
5535 originator is the page that originated the change. It will not be
5536 called to set the METAR, while others will be."""
5537 for page in [self._departureBriefingPage, self._takeoffPage]:
5538 if page is not originator:
5539 page.changeMETAR(metar)
5540
5541 def arrivalMETARChanged(self, metar, originator):
5542 """Called when the arrival METAR has been edited on one of the
5543 pages.
5544
5545 originator is the page that originated the change. It will not be
5546 called to set the METAR, while others will be."""
5547 for page in [self._arrivalBriefingPage, self._landingPage]:
5548 if page is not originator:
5549 page.changeMETAR(metar)
5550
5551 def _loginResultCallback(self, returned, result):
5552 """The login result callback, called in the web handler's thread."""
5553 gobject.idle_add(self._handleLoginResult, returned, result)
5554
5555 def _handleLoginResult(self, returned, result):
5556 """Handle the login result."""
5557 self.gui.endBusy()
5558 isReload = self._loginResult is not None
5559 if returned:
5560 if result.loggedIn:
5561 self._loginResult = result
5562 else:
5563 if isReload:
5564 message = xstr("reload_failed")
5565 else:
5566 message = xstr("login_entranceExam_invalid"
5567 if self.entranceExam else
5568 xstr("login_invalid"))
5569 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
5570 type = MESSAGETYPE_ERROR,
5571 message_format = message)
5572 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
5573 dialog.set_title(WINDOW_TITLE_BASE)
5574 if isReload:
5575 secondary = xstr("reload_failed_sec")
5576 else:
5577 secondary = xstr("login_entranceExam_invalid_sec"
5578 if self.entranceExam else
5579 xstr("login_invalid_sec"))
5580 dialog.format_secondary_markup(secondary)
5581 dialog.run()
5582 dialog.hide()
5583 else:
5584 message = xstr("reload_failconn") if isReload \
5585 else xstr("login_failconn")
5586 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
5587 type = MESSAGETYPE_ERROR,
5588 message_format = message)
5589 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
5590 dialog.set_title(WINDOW_TITLE_BASE)
5591 secondary = xstr("reload_failconn_sec") if isReload \
5592 else xstr("login_failconn_sec")
5593 dialog.format_secondary_markup(secondary)
5594
5595 dialog.run()
5596 dialog.hide()
5597
5598 callback = self._loginCallback
5599 self._loginCallback = None
5600 callback(returned, result)
5601
5602 def getFleet(self, callback, force = False):
5603 """Get the fleet via the GUI and call the given callback."""
5604 self._fleetCallback = callback
5605 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
5606
5607 def _fleetRetrieved(self, fleet):
5608 """Callback for the fleet retrieval."""
5609 self._fleet = fleet
5610 if self._fleetCallback is not None:
5611 self._fleetCallback(fleet)
5612 self._fleetCallback = None
5613
5614 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
5615 """Update the given plane's gate information."""
5616 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
5617 callback = callback)
5618
5619 def updateRTO(self):
5620 """Update the RTO state.
5621
5622 The RTO checkbox will be enabled if the flight has an RTO state and the
5623 comments field contains some text."""
5624 flight = self.gui.flight
5625 rtoEnabled = flight is not None and flight.hasRTO and \
5626 self.gui.hasComments
5627 self._takeoffPage.setRTOEnabled(rtoEnabled)
5628
5629 def commentsChanged(self):
5630 """Called when the comments have changed."""
5631 self.updateRTO()
5632 self._finishPage.updateButtons()
5633
5634 def delayCodesChanged(self):
5635 """Called when the delay codes have changed."""
5636 self._finishPage.updateButtons()
5637
5638 def faultExplanationsChanged(self):
5639 """Called when the faults and their explanations have changed."""
5640 self._finishPage.updateButtons()
5641
5642 def rtoToggled(self, indicated):
5643 """Called when the RTO indication has changed."""
5644 self.gui.rtoToggled(indicated)
5645
5646 def _connectSimulator(self, simulatorType):
5647 """Connect to the simulator."""
5648 self.gui.connectSimulator(self._bookedFlight.aircraftType,
5649 simulatorType)
5650
5651 def _arrivalMETARCallback(self, returned, result):
5652 """Called when the METAR of the arrival airport is retrieved."""
5653 gobject.idle_add(self._handleArrivalMETAR, returned, result)
5654
5655 def _handleArrivalMETAR(self, returned, result):
5656 """Called when the METAR of the arrival airport is retrieved."""
5657 icao = self._bookedFlight.arrivalICAO
5658 if returned and icao in result.metars:
5659 metar = result.metars[icao]
5660 if metar!="":
5661 self._arrivalBriefingPage.setMETAR(metar)
5662
5663 def _getIndexOf(self, pageID):
5664 """Get the index for the given page ID.
5665
5666 It is an assertion failure if the ID is not found."""
5667 for index in range(0, len(self._pages)):
5668 page = self._pages[index]
5669 if page.id==pageID:
5670 return index
5671 assert False
5672
5673#-----------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.