source: src/mlx/gui/flight.py@ 705:e9d73c721d33

cef
Last change on this file since 705:e9d73c721d33 was 705:e9d73c721d33, checked in by István Váradi <ivaradi@…>, 7 years ago

The weights are recomputed to tons (re #279).

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