source: src/mlx/gui/flight.py@ 811:e84ebe7c6497

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

Extracted the flight list widget into its own class (re #307)

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