source: src/mlx/gui/flight.py@ 760:733c148959e9

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

The rank is returned when logging in, and in case of a student, we jump to the student page

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