source: src/mlx/gui/flight.py@ 762:c49eee53b7c5

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

The student page is displayed with proper title, help and the entrance exam button updated based on the completion state of the exam (re #285)

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