source: src/mlx/gui/flight.py@ 818:e58b0fa0408a

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

The default column descriptors are moved to the FlightList (re #307).

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