source: src/mlx/gui/flight.py@ 828:b5013b5bffb1

Last change on this file since 828:b5013b5bffb1 was 824:599a3e9cb025, checked in by István Váradi <ivaradi@…>, 8 years ago

Pending flights can be deleted (re #307).

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