source: src/mlx/gui/flight.py@ 861:5fa8e92b19b2

Last change on this file since 861:5fa8e92b19b2 was 860:4598546ff6b8, checked in by István Váradi <ivaradi@…>, 7 years ago

The Save flight button is to the right of the flight list (re #308)

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