source: src/mlx/gui/flight.py@ 759:cf8bdfef70dd

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

The surname and first name are to be entered separately (re #285)

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