source: src/mlx/gui/flight.py@ 756:673a9809df08

Last change on this file since 756:673a9809df08 was 756:673a9809df08, checked in by István Váradi <ivaradi@…>, 9 years ago

Implemented the basic GUI logic of the registration (re #285)

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