source: src/mlx/gui/flight.py@ 753:c4b335324b86

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

Implemented the registration form (re #285)

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