source: src/mlx/gui/flight.py@ 512:46bd71540346

Last change on this file since 512:46bd71540346 was 512:46bd71540346, checked in by István Váradi <ivaradi@…>, 11 years ago

Implemented the new derate handling method (re #202)

File size: 134.5 KB
Line 
1
2from mlx.gui.common import *
3
4import mlx.const as const
5import mlx.fs as fs
6import mlx.acft as acft
7from mlx.flight import Flight
8from mlx.checks import PayloadChecker
9import mlx.util as util
10from mlx.pirep import PIREP
11from mlx.i18n import xstr
12from mlx.sound import startSound
13import mlx.web as web
14
15import datetime
16import time
17import os
18
19#-----------------------------------------------------------------------------
20
21## @package mlx.gui.flight
22#
23# The flight "wizard".
24#
25# This module implements the main tab of the application, the flight
26# wizard. The wizard consists of \ref Page "pages", that come one after the
27# other. As some pages might be skipped, the pages dynamically store the index
28# of the previous page so that going back to it is simpler. The \ref
29# Page.activate "activate" function is called before a page is first shown
30# during a flight. This function should initialize the page's controls and fill
31# it with initial data. When a page is left for the first time, its \ref
32# Page.finalize "finalize" function is called. It should set those controls
33# insensitive, that will not be available if the user comes back to this page.
34#
35# Each page has a title at the top displayed in inverted colors and a big
36# font. There is a help text below it centered, that shortly describes what is
37# expected on the page. There can be two help texts: one shown when the page is
38# first displayed during a flight, another shown when the user goes back to the
39# page. The main content area is below this, also centered. Finally, there are
40# some buttons at the bottom on the right. As some buttons occur very
41# frequently, there are functions to add them (\ref Page.addCancelFlightButton
42# "addCancelFlightButton", \ref Page.addPreviousButton "addPreviousButton" and
43# \ref Page.addNextButton "addNextButton".
44#
45# The \ref Wizard class is the main class to collect the pages. It also stores
46# some data passed from one page to another and provides properties to access
47# data items set via the wizard pages.
48
49#-----------------------------------------------------------------------------
50
51class Page(gtk.Alignment):
52 """A page in the flight wizard."""
53 def __init__(self, wizard, title, help, completedHelp = None):
54 """Construct the page."""
55 super(Page, self).__init__(xalign = 0.0, yalign = 0.0,
56 xscale = 1.0, yscale = 1.0)
57 self.set_padding(padding_top = 4, padding_bottom = 4,
58 padding_left = 12, padding_right = 12)
59
60 frame = gtk.Frame()
61 self.add(frame)
62
63 self._vbox = gtk.VBox()
64 self._vbox.set_homogeneous(False)
65 frame.add(self._vbox)
66
67 eventBox = gtk.EventBox()
68
69 alignment = gtk.Alignment(xalign = 0.0, xscale = 0.0)
70
71 titleLabel = gtk.Label(title)
72 titleLabel.modify_font(pango.FontDescription("bold 24"))
73 alignment.set_padding(padding_top = 4, padding_bottom = 4,
74 padding_left = 6, padding_right = 0)
75
76 alignment.add(titleLabel)
77 eventBox.add(alignment)
78
79 self._vbox.pack_start(eventBox, False, False, 0)
80
81 self._titleEventBox = eventBox
82 self._titleLabel = titleLabel
83
84 mainBox = gtk.VBox()
85
86 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
87 xscale = 1.0, yscale = 1.0)
88 alignment.set_padding(padding_top = 16, padding_bottom = 16,
89 padding_left = 16, padding_right = 16)
90 alignment.add(mainBox)
91 self._vbox.pack_start(alignment, True, True, 0)
92
93 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
94 xscale = 0.0, yscale = 0.0)
95 alignment.set_padding(padding_top = 0, padding_bottom = 16,
96 padding_left = 0, padding_right = 0)
97
98 self._help = help
99 self._completedHelp = completedHelp
100
101 if self._completedHelp is None or \
102 len(help.splitlines())>=len(completedHelp.splitlines()):
103 longerHelp = help
104 else:
105 longerHelp = completedHelp
106
107 self._helpLabel = gtk.Label(completedHelp)
108 # FIXME: should be a constant in common
109 self._helpLabel.set_justify(gtk.Justification.CENTER if pygobject
110 else gtk.JUSTIFY_CENTER)
111 self._helpLabel.set_use_markup(True)
112 alignment.add(self._helpLabel)
113 mainBox.pack_start(alignment, False, False, 0)
114
115 self._mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
116 xscale = 1.0, yscale = 1.0)
117 mainBox.pack_start(self._mainAlignment, True, True, 0)
118
119 buttonAlignment = gtk.Alignment(xalign = 1.0, xscale=0.0, yscale = 0.0)
120 buttonAlignment.set_padding(padding_top = 4, padding_bottom = 10,
121 padding_left = 16, padding_right = 16)
122
123 self._buttonBox = gtk.HBox()
124 self._buttonBox.set_homogeneous(False)
125 self._defaultButton = None
126 buttonAlignment.add(self._buttonBox)
127
128 self._vbox.pack_start(buttonAlignment, False, False, 0)
129
130 self._wizard = wizard
131
132 self._cancelFlightButton = None
133
134 self._completed = False
135 self._fromPage = None
136
137 def setMainWidget(self, widget):
138 """Set the given widget as the main one."""
139 self._mainAlignment.add(widget)
140
141 def addButton(self, label, default = False, sensitive = True,
142 tooltip = None, clicked = None, padding = 4):
143 """Add a button with the given label.
144
145 Return the button object created."""
146 button = gtk.Button(label)
147 self._buttonBox.pack_start(button, False, False, padding)
148 button.set_use_underline(True)
149 if default:
150 button.set_can_default(True)
151 self._defaultButton = button
152 button.set_sensitive(sensitive)
153 if tooltip is not None:
154 button.set_tooltip_text(tooltip)
155 if clicked is not None:
156 button.connect("clicked", clicked)
157 return button
158
159 def addCancelFlightButton(self):
160 """Add the 'Cancel flight' button to the page."""
161 self._cancelFlightButton = \
162 self.addButton(xstr("button_cancelFlight"),
163 sensitive = True,
164 tooltip = xstr("button_cancelFlight_tooltip"),
165 clicked = self._cancelFlight,
166 padding = 16)
167 return self._cancelFlightButton
168
169 def addPreviousButton(self, sensitive = True, clicked = None):
170 """Add the 'Next' button to the page."""
171 return self.addButton(xstr("button_previous"),
172 sensitive = sensitive,
173 tooltip = xstr("button_previous_tooltip"),
174 clicked = clicked)
175
176 def addNextButton(self, default = True, sensitive = True,
177 clicked = None):
178 """Add the 'Next' button to the page."""
179 return self.addButton(xstr("button_next"),
180 default = default,
181 sensitive = sensitive,
182 tooltip = xstr("button_next_tooltip"),
183 clicked = clicked)
184
185 def setStyle(self):
186 """Set the styles of some of the items on the page."""
187 if pygobject:
188 context = self.get_style_context()
189 color = context.get_background_color(gtk.StateFlags.SELECTED)
190 self._titleEventBox.modify_bg(0, color.to_color())
191 color = context.get_color(gtk.StateFlags.SELECTED)
192 self._titleLabel.modify_fg(0, color.to_color())
193 else:
194 style = self.rc_get_style()
195 self._titleEventBox.modify_bg(0, style.bg[3])
196 self._titleLabel.modify_fg(0, style.fg[3])
197
198 def initialize(self):
199 """Initialize the page.
200
201 It sets up the primary help, and calls the activate() function."""
202 self._helpLabel.set_markup(self._help)
203 self._helpLabel.set_sensitive(True)
204 self.activate()
205
206 def activate(self):
207 """Called when this page becomes active.
208
209 This default implementation does nothing."""
210 pass
211
212 def complete(self):
213 """Called when the page is completed.
214
215 It greys out/changes the help text and then calls finalize()."""
216 self.finalize()
217 if self._completedHelp is None:
218 self._helpLabel.set_sensitive(False)
219 else:
220 self._helpLabel.set_markup(self._completedHelp)
221 self._completed = True
222
223 def finalize(self):
224 """Called when the page is finalized."""
225 pass
226
227 def grabDefault(self):
228 """If the page has a default button, make it the default one."""
229 if self._defaultButton is not None:
230 self._defaultButton.grab_default()
231
232 def reset(self):
233 """Reset the page if the wizard is reset."""
234 self._completed = False
235 self._fromPage = None
236 if self._cancelFlightButton is not None:
237 self._cancelFlightButton.set_sensitive(True)
238
239 def goBack(self):
240 """Go to the page we were invoked from."""
241 assert self._fromPage is not None
242
243 self._wizard.setCurrentPage(self._fromPage, finalize = False)
244
245 def flightEnded(self):
246 """Called when the flight has ended.
247
248 This default implementation disables the cancel flight button."""
249 if self._cancelFlightButton is not None:
250 self._cancelFlightButton.set_sensitive(False)
251
252 def _cancelFlight(self, button):
253 """Called when the Cancel flight button is clicked."""
254 self._wizard.gui.cancelFlight()
255
256#-----------------------------------------------------------------------------
257
258class LoginPage(Page):
259 """The login page."""
260 def __init__(self, wizard):
261 """Construct the login page."""
262 super(LoginPage, self).__init__(wizard, xstr("login"),
263 xstr("loginHelp"))
264
265 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
266 xscale = 0.0, yscale = 0.0)
267
268 table = gtk.Table(4, 2)
269 table.set_row_spacings(4)
270 table.set_col_spacings(32)
271 alignment.add(table)
272 self.setMainWidget(alignment)
273
274 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
275 xscale = 0.0, yscale = 0.0)
276 label = gtk.Label(xstr("label_pilotID"))
277 label.set_use_underline(True)
278 labelAlignment.add(label)
279 table.attach(labelAlignment, 0, 1, 0, 1)
280
281 self._pilotID = gtk.Entry()
282 self._pilotID.connect("changed", self._setControls)
283 self._pilotID.set_tooltip_text(xstr("login_pilotID_tooltip"))
284 table.attach(self._pilotID, 1, 2, 0, 1)
285 label.set_mnemonic_widget(self._pilotID)
286
287 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
288 xscale = 0.0, yscale = 0.0)
289 label = gtk.Label(xstr("label_password"))
290 label.set_use_underline(True)
291 labelAlignment.add(label)
292 table.attach(labelAlignment, 0, 1, 1, 2)
293
294 self._password = gtk.Entry()
295 self._password.set_visibility(False)
296 self._password.connect("changed", self._setControls)
297 self._password.set_tooltip_text(xstr("login_password_tooltip"))
298 table.attach(self._password, 1, 2, 1, 2)
299 label.set_mnemonic_widget(self._password)
300
301 self._rememberButton = gtk.CheckButton(xstr("remember_password"))
302 self._rememberButton.set_use_underline(True)
303 self._rememberButton.set_tooltip_text(xstr("login_remember_tooltip"))
304 table.attach(self._rememberButton, 1, 2, 2, 3, ypadding = 8)
305
306 self._entranceExam = gtk.CheckButton(xstr("login_entranceExam"))
307 self._entranceExam.set_use_underline(True)
308 self._entranceExam.set_tooltip_text(xstr("login_entranceExam_tooltip"))
309 self._entranceExam.connect("toggled", self._setControls)
310 table.attach(self._entranceExam, 1, 2, 3, 4, ypadding = 12)
311
312 self.addButton(xstr("button_offline"),
313 clicked = self._offlineClicked,
314 tooltip = xstr("button_offline_tooltip"))
315
316 self._loginButton = self.addButton(xstr("button_login"), default = True)
317 self._loginButton.connect("clicked", self._loginClicked)
318 self._loginButton.set_tooltip_text(xstr("login_button_tooltip"))
319
320
321 @property
322 def entranceExam(self):
323 """Get whether an entrance exam is being performed."""
324 return self._entranceExam.get_active() and \
325 self._pilotID.get_text()!=""
326
327 @property
328 def pilotID(self):
329 """Get the pilot ID, if given."""
330 return self._pilotID.get_text()
331
332 def activate(self):
333 """Activate the page."""
334 config = self._wizard.gui.config
335 self._pilotID.set_text(config.pilotID)
336 self._password.set_text(config.password)
337 self._rememberButton.set_active(config.rememberPassword)
338 self._setControls(None)
339
340 def _setControls(self, entry = None):
341 """Set the sensitivity of the various controls.
342
343 The login button is sensitive only if both the pilot ID and the
344 password fields contain values.
345
346 The password field is sensitive only, if the entrance exam checkbox is
347 not selected.
348
349 The remember password checkbox is sensitive only, if the password field
350 contains text.
351
352 The entrance exam checkbox is sensitive only, if the pilot ID is not
353 empty."""
354 pilotID = self._pilotID.get_text()
355 password = self._password.get_text()
356 entranceExam = self._entranceExam.get_active()
357 self._password.set_sensitive(not entranceExam)
358 self._rememberButton.set_sensitive(password!="" and not entranceExam)
359 self._entranceExam.set_sensitive(pilotID!="")
360 self._loginButton.set_sensitive(pilotID!="" and
361 (password!="" or entranceExam))
362
363 def _offlineClicked(self, button):
364 """Called when the offline button was clicked."""
365 print "mlx.flight.LoginPage: offline flight selected"
366 self._wizard.nextPage()
367
368 def _loginClicked(self, button):
369 """Called when the login button was clicked."""
370 print "mlx.flight.LoginPage: logging in"
371 self._wizard.login(self._handleLoginResult,
372 self._pilotID.get_text(),
373 self._password.get_text(),
374 self.entranceExam)
375
376 def _handleLoginResult(self, returned, result):
377 """Handle the login result."""
378 self._loginButton.set_sensitive(True)
379 if returned and result.loggedIn:
380 config = self._wizard.gui.config
381
382 config.pilotID = self._pilotID.get_text()
383
384 rememberPassword = self._rememberButton.get_active()
385 config.password = result.password if rememberPassword else ""
386
387 config.rememberPassword = rememberPassword
388
389 config.save()
390 self._wizard.nextPage()
391
392#-----------------------------------------------------------------------------
393
394class FlightSelectionPage(Page):
395 """The page to select the flight."""
396 def __init__(self, wizard):
397 """Construct the flight selection page."""
398 help = xstr("flightsel_help")
399 completedHelp = xstr("flightsel_chelp")
400 super(FlightSelectionPage, self).__init__(wizard, xstr("flightsel_title"),
401 help, completedHelp = completedHelp)
402
403
404 self._listStore = gtk.ListStore(str, str, str, str)
405 self._flightList = gtk.TreeView(self._listStore)
406 column = gtk.TreeViewColumn(xstr("flightsel_no"), gtk.CellRendererText(),
407 text = 1)
408 column.set_expand(True)
409 self._flightList.append_column(column)
410 column = gtk.TreeViewColumn(xstr("flightsel_deptime"), gtk.CellRendererText(),
411 text = 0)
412 column.set_expand(True)
413 self._flightList.append_column(column)
414 column = gtk.TreeViewColumn(xstr("flightsel_from"), gtk.CellRendererText(),
415 text = 2)
416 column.set_expand(True)
417 self._flightList.append_column(column)
418 column = gtk.TreeViewColumn(xstr("flightsel_to"), gtk.CellRendererText(),
419 text = 3)
420 column.set_expand(True)
421 self._flightList.append_column(column)
422 self._flightList.connect("row-activated", self._rowActivated)
423 self._flightList.connect("button-press-event", self._listButtonPressed)
424
425 self._flightListPopupMenu = None
426
427 flightSelection = self._flightList.get_selection()
428 flightSelection.connect("changed", self._selectionChanged)
429
430 scrolledWindow = gtk.ScrolledWindow()
431 scrolledWindow.add(self._flightList)
432 scrolledWindow.set_size_request(400, -1)
433 # FIXME: these should be constants in common.py
434 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
435 else gtk.POLICY_AUTOMATIC,
436 gtk.PolicyType.AUTOMATIC if pygobject
437 else gtk.POLICY_AUTOMATIC)
438 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
439 else gtk.SHADOW_IN)
440
441 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
442 alignment.add(scrolledWindow)
443
444 self.setMainWidget(alignment)
445
446 self._saveButton = self.addButton(xstr("flightsel_save"),
447 sensitive = False,
448 clicked = self._saveClicked,
449 tooltip = xstr("flightsel_save_tooltip"))
450 self._saveDialog = None
451
452 self._refreshButton = self.addButton(xstr("flightsel_refresh"),
453 sensitive = True,
454 clicked = self._refreshClicked,
455 tooltip = xstr("flightsel_refresh_tooltip"))
456
457 self._loadButton = self.addButton(xstr("flightsel_load"),
458 sensitive = True,
459 tooltip = xstr("flightsel_load_tooltip"))
460 self._loadButton.connect("clicked", self._loadButtonClicked)
461 self._loadDialog = None
462
463 self._button = self.addNextButton(sensitive = False,
464 clicked = self._forwardClicked)
465
466 self._flights = []
467
468 def activate(self):
469 """Fill the flight list."""
470 self._flightList.set_sensitive(True)
471 self._loadButton.set_sensitive(True)
472 self._refreshButton.set_sensitive(self._wizard.loggedIn)
473 self._buildFlights()
474
475 def finalize(self):
476 """Finalize the page."""
477 self._flightList.set_sensitive(False)
478 self._loadButton.set_sensitive(False)
479 self._refreshButton.set_sensitive(False)
480
481 def _buildFlights(self):
482 """Rebuild the flights from the login result."""
483 self._flights = []
484 self._listStore.clear()
485 if self._wizard.loggedIn:
486 for flight in self._wizard.loginResult.flights:
487 self._addFlight(flight)
488
489 def _addFlight(self, flight):
490 """Add the given file to the list of flights."""
491 self._flights.append(flight)
492 self._listStore.append([str(flight.departureTime),
493 flight.callsign,
494 flight.departureICAO,
495 flight.arrivalICAO])
496
497 def _saveClicked(self, button):
498 """Called when the Save flight button is clicked."""
499 self._saveSelected()
500
501 def _saveSelected(self):
502 """Save the selected flight."""
503 flight = self._getSelectedFlight()
504 date = flight.departureTime.date()
505 name = "%04d-%02d-%02d %s %s-%s.vaflight" % \
506 (date.year, date.month, date.day, flight.callsign,
507 flight.departureICAO, flight.arrivalICAO)
508
509 dialog = self._getSaveDialog()
510 dialog.set_current_name(name)
511 dialog.show_all()
512 response = dialog.run()
513 dialog.hide()
514
515 if response==RESPONSETYPE_OK:
516 fileName = text2unicode(dialog.get_filename())
517 print "Saving", fileName
518 try:
519 with open(fileName, "wt") as f:
520 flight.writeIntoFile(f)
521 except Exception, e:
522 print "Failed to save flight:", util.utf2unicode(str(e))
523 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
524 type = MESSAGETYPE_ERROR,
525 message_format =
526 xstr("flightsel_save_failed"))
527 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
528 dialog.set_title(WINDOW_TITLE_BASE)
529 secondary = xstr("flightsel_save_failed_sec")
530 dialog.format_secondary_markup(secondary)
531 dialog.run()
532 dialog.hide()
533
534 def _refreshClicked(self, button):
535 """Called when the refresh button is clicked."""
536 self._wizard.reloadFlights(self._refreshCallback)
537
538 def _refreshCallback(self, returned, result):
539 """Callback for the refresh."""
540 if returned and result.loggedIn:
541 self._buildFlights()
542
543 def _selectionChanged(self, selection):
544 """Called when the selection is changed."""
545 selected = selection.count_selected_rows()==1
546 self._saveButton.set_sensitive(selected)
547 self._button.set_sensitive(selected)
548
549 def _loadButtonClicked(self, loadButton):
550 """Called when the load a flight button is clicked."""
551 dialog = self._getLoadDialog()
552 dialog.show_all()
553 response = dialog.run()
554 dialog.hide()
555
556 if response==RESPONSETYPE_OK:
557 fileName = text2unicode(dialog.get_filename())
558 print "Loading", fileName
559 bookedFlight = web.BookedFlight()
560 try:
561 with open(fileName, "rt") as f:
562 bookedFlight.readFromFile(f)
563 self._addFlight(bookedFlight)
564 except Exception, e:
565 print "Failed to load flight:", util.utf2unicode(str(e))
566 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
567 type = MESSAGETYPE_ERROR,
568 message_format =
569 xstr("flightsel_load_failed"))
570 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
571 dialog.set_title(WINDOW_TITLE_BASE)
572 secondary = xstr("flightsel_load_failed_sec")
573 dialog.format_secondary_markup(secondary)
574 dialog.run()
575 dialog.hide()
576
577 def _forwardClicked(self, button):
578 """Called when the forward button was clicked."""
579 if self._completed:
580 self._wizard.jumpPage(self._nextDistance, finalize = False)
581 else:
582 self._flightSelected()
583
584 def _rowActivated(self, flightList, path, column):
585 """Called when a row is activated."""
586 if not self._completed:
587 self._flightSelected()
588
589 def _flightSelected(self):
590 """Called when a flight has been selected."""
591 flight = self._getSelectedFlight()
592 self._wizard._bookedFlight = flight
593 self._wizard.gui.enableFlightInfo(flight.aircraftType)
594
595 self._updateDepartureGate()
596
597 def _getSelectedFlight(self):
598 """Get the currently selected flight."""
599 selection = self._flightList.get_selection()
600 (listStore, iter) = selection.get_selected()
601 path = listStore.get_path(iter)
602 [index] = path.get_indices() if pygobject else path
603
604 return self._flights[index]
605
606 def _listButtonPressed(self, widget, event):
607 """Called when a mouse button is pressed on the flight list."""
608 if event.type!=EVENT_BUTTON_PRESS or event.button!=3:
609 return
610
611 (path, _, _, _) = self._flightList.get_path_at_pos(int(event.x),
612 int(event.y))
613 selection = self._flightList.get_selection()
614 selection.unselect_all()
615 selection.select_path(path)
616
617 menu = self._getListPopupMenu()
618 if pygobject:
619 menu.popup(None, None, None, None, event.button, event.time)
620 else:
621 menu.popup(None, None, None, event.button, event.time)
622
623 def _updateDepartureGate(self):
624 """Update the departure gate for the booked flight."""
625 flight = self._wizard._bookedFlight
626 if self._wizard.gui.config.onlineGateSystem and \
627 self._wizard.loggedIn and not self._wizard.entranceExam:
628 if flight.departureICAO=="LHBP":
629 self._wizard.getFleet(self._fleetRetrieved)
630 else:
631 self._wizard.updatePlane(self._planeUpdated,
632 flight.tailNumber,
633 const.PLANE_AWAY)
634 else:
635 self._nextDistance = 2
636 self._wizard.jumpPage(2)
637
638 def _fleetRetrieved(self, fleet):
639 """Called when the fleet has been retrieved."""
640 if fleet is None:
641 self._nextDistance = 2
642 self._wizard.jumpPage(2)
643 else:
644 plane = fleet[self._wizard._bookedFlight.tailNumber]
645 if plane is None:
646 self._nextDistance = 2
647 self._wizard.jumpPage(2)
648 elif plane.gateNumber is not None and \
649 not fleet.isGateConflicting(plane):
650 self._wizard._departureGate = plane.gateNumber
651 self._nextDistance = 2
652 self._wizard.jumpPage(2)
653 else:
654 self._nextDistance = 1
655 self._wizard.nextPage()
656
657 def _planeUpdated(self, success):
658 """Callback for the plane updating."""
659 self._nextDistance = 2
660 self._wizard.jumpPage(2)
661
662 def _getSaveDialog(self):
663 """Get the dialog to load a flight file."""
664 if self._saveDialog is not None:
665 return self._saveDialog
666
667 gui = self._wizard.gui
668 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
669 xstr("flightsel_save_title"),
670 action = FILE_CHOOSER_ACTION_SAVE,
671 buttons = (gtk.STOCK_CANCEL,
672 RESPONSETYPE_CANCEL,
673 gtk.STOCK_OK, RESPONSETYPE_OK),
674 parent = gui.mainWindow)
675 dialog.set_modal(True)
676 dialog.set_do_overwrite_confirmation(True)
677
678 filter = gtk.FileFilter()
679 filter.set_name(xstr("flightsel_filter_flights"))
680 filter.add_pattern("*.vaflight")
681 dialog.add_filter(filter)
682
683 filter = gtk.FileFilter()
684 filter.set_name(xstr("file_filter_all"))
685 filter.add_pattern("*.*")
686 dialog.add_filter(filter)
687
688 self._saveDialog = dialog
689
690 return dialog
691
692 def _getLoadDialog(self):
693 """Get the dialog to load a flight file."""
694 if self._loadDialog is not None:
695 return self._loadDialog
696
697 gui = self._wizard.gui
698 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
699 xstr("flightsel_load_title"),
700 action = FILE_CHOOSER_ACTION_OPEN,
701 buttons = (gtk.STOCK_CANCEL,
702 RESPONSETYPE_CANCEL,
703 gtk.STOCK_OK, RESPONSETYPE_OK),
704 parent = gui.mainWindow)
705 dialog.set_modal(True)
706
707 filter = gtk.FileFilter()
708 filter.set_name(xstr("flightsel_filter_flights"))
709 filter.add_pattern("*.vaflight")
710 dialog.add_filter(filter)
711
712 filter = gtk.FileFilter()
713 filter.set_name(xstr("file_filter_all"))
714 filter.add_pattern("*.*")
715 dialog.add_filter(filter)
716
717 self._loadDialog = dialog
718
719 return dialog
720
721 def _getListPopupMenu(self):
722 """Get the flight list popup menu."""
723 if self._flightListPopupMenu is None:
724 menu = gtk.Menu()
725
726 menuItem = gtk.MenuItem()
727 menuItem.set_label(xstr("flightsel_popup_select"))
728 menuItem.set_use_underline(True)
729 menuItem.connect("activate", self._popupSelect)
730 menuItem.show()
731
732 menu.append(menuItem)
733
734 menuItem = gtk.MenuItem()
735 menuItem.set_label(xstr("flightsel_popup_save"))
736 menuItem.set_use_underline(True)
737 menuItem.connect("activate", self._popupSave)
738 menuItem.show()
739
740 menu.append(menuItem)
741
742 self._flightListPopupMenu = menu
743
744 return self._flightListPopupMenu
745
746 def _popupSelect(self, menuItem):
747 """Called when the Select menu item is activated in the popup menu."""
748 if not self._completed:
749 self._flightSelected()
750
751 def _popupSave(self, menuItem):
752 """Called when the Save menu item is activated in the popup menu."""
753 if not self._completed:
754 self._saveSelected()
755
756#-----------------------------------------------------------------------------
757
758class GateSelectionPage(Page):
759 """Page to select a free gate at LHBP.
760 This page should be displayed only if we have fleet information!."""
761 def __init__(self, wizard):
762 """Construct the gate selection page."""
763 super(GateSelectionPage, self).__init__(wizard, xstr("gatesel_title"),
764 xstr("gatesel_help"))
765
766 self._listStore = gtk.ListStore(str)
767 self._gateList = gtk.TreeView(self._listStore)
768 column = gtk.TreeViewColumn(None, gtk.CellRendererText(),
769 text = 0)
770 column.set_expand(True)
771 self._gateList.append_column(column)
772 self._gateList.set_headers_visible(False)
773 self._gateList.connect("row-activated", self._rowActivated)
774
775 gateSelection = self._gateList.get_selection()
776 gateSelection.connect("changed", self._selectionChanged)
777
778 scrolledWindow = gtk.ScrolledWindow()
779 scrolledWindow.add(self._gateList)
780 scrolledWindow.set_size_request(50, -1)
781 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
782 else gtk.POLICY_AUTOMATIC,
783 gtk.PolicyType.AUTOMATIC if pygobject
784 else gtk.POLICY_AUTOMATIC)
785 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
786 else gtk.SHADOW_IN)
787
788 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
789 alignment.add(scrolledWindow)
790
791 self.setMainWidget(alignment)
792
793 self.addCancelFlightButton()
794
795 self.addPreviousButton(clicked = self._backClicked)
796
797 self._button = self.addNextButton(sensitive = False,
798 clicked = self._forwardClicked)
799
800 def activate(self):
801 """Fill the gate list."""
802 self._listStore.clear()
803 self._gateList.set_sensitive(True)
804 occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
805 for gateNumber in const.lhbpGateNumbers:
806 if gateNumber not in occupiedGateNumbers:
807 self._listStore.append([gateNumber])
808
809 def finalize(self):
810 """Finalize the page."""
811 self._gateList.set_sensitive(False)
812
813 def _selectionChanged(self, selection):
814 """Called when the selection is changed."""
815 self._button.set_sensitive(selection.count_selected_rows()==1)
816
817 def _backClicked(self, button):
818 """Called when the Back button is pressed."""
819 self.goBack()
820
821 def _forwardClicked(self, button):
822 """Called when the forward button is clicked."""
823 if not self._completed:
824 self._gateSelected()
825 else:
826 self._wizard.nextPage()
827
828 def _rowActivated(self, flightList, path, column):
829 """Called when a row is activated."""
830 if not self._completed:
831 self._gateSelected()
832
833 def _gateSelected(self):
834 """Called when a gate has been selected."""
835 selection = self._gateList.get_selection()
836 (listStore, iter) = selection.get_selected()
837 (gateNumber,) = listStore.get(iter, 0)
838
839 self._wizard._departureGate = gateNumber
840
841 self._wizard.updatePlane(self._planeUpdated,
842 self._wizard._bookedFlight.tailNumber,
843 const.PLANE_HOME, gateNumber)
844
845 def _planeUpdated(self, success):
846 """Callback for the plane updating call."""
847 if success is None or success:
848 self._wizard.nextPage()
849 else:
850 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
851 type = MESSAGETYPE_ERROR,
852 message_format = xstr("gatesel_conflict"))
853 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
854 dialog.set_title(WINDOW_TITLE_BASE)
855 dialog.format_secondary_markup(xstr("gatesel_conflict_sec"))
856 dialog.run()
857 dialog.hide()
858
859 self._wizard.getFleet(self._fleetRetrieved)
860
861 def _fleetRetrieved(self, fleet):
862 """Called when the fleet has been retrieved."""
863 if fleet is None:
864 self._wizard.nextPage()
865 else:
866 self.activate()
867
868#-----------------------------------------------------------------------------
869
870class ConnectPage(Page):
871 """Page which displays the departure airport and gate (if at LHBP)."""
872 def __init__(self, wizard):
873 """Construct the connect page."""
874 help = "Load the aircraft below into the simulator and park it\n" \
875 "at the given airport, at the gate below, if present.\n\n" \
876 "Then press the Connect button to connect to the simulator."
877 completedHelp = "The basic data of your flight can be read below."
878 super(ConnectPage, self).__init__(wizard, xstr("connect_title"),
879 xstr("connect_help"),
880 completedHelp = xstr("connect_chelp"))
881
882 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
883 xscale = 0.0, yscale = 0.0)
884
885 table = gtk.Table(5, 2)
886 table.set_row_spacings(4)
887 table.set_col_spacings(16)
888 table.set_homogeneous(True)
889 alignment.add(table)
890 self.setMainWidget(alignment)
891
892 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
893 label = gtk.Label(xstr("connect_flightno"))
894 labelAlignment.add(label)
895 table.attach(labelAlignment, 0, 1, 0, 1)
896
897 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
898 self._flightNumber = gtk.Label()
899 self._flightNumber.set_width_chars(9)
900 self._flightNumber.set_alignment(0.0, 0.5)
901 labelAlignment.add(self._flightNumber)
902 table.attach(labelAlignment, 1, 2, 0, 1)
903
904 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
905 label = gtk.Label(xstr("connect_acft"))
906 labelAlignment.add(label)
907 table.attach(labelAlignment, 0, 1, 1, 2)
908
909 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
910 self._aircraft = gtk.Label()
911 self._aircraft.set_width_chars(25)
912 self._aircraft.set_alignment(0.0, 0.5)
913 labelAlignment.add(self._aircraft)
914 table.attach(labelAlignment, 1, 2, 1, 2)
915
916 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
917 label = gtk.Label(xstr("connect_tailno"))
918 labelAlignment.add(label)
919 table.attach(labelAlignment, 0, 1, 2, 3)
920
921 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
922 self._tailNumber = gtk.Label()
923 self._tailNumber.set_width_chars(10)
924 self._tailNumber.set_alignment(0.0, 0.5)
925 labelAlignment.add(self._tailNumber)
926 table.attach(labelAlignment, 1, 2, 2, 3)
927
928 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
929 label = gtk.Label(xstr("connect_airport"))
930 labelAlignment.add(label)
931 table.attach(labelAlignment, 0, 1, 3, 4)
932
933 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
934 self._departureICAO = gtk.Label()
935 self._departureICAO.set_width_chars(6)
936 self._departureICAO.set_alignment(0.0, 0.5)
937 labelAlignment.add(self._departureICAO)
938 table.attach(labelAlignment, 1, 2, 3, 4)
939
940 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
941 label = gtk.Label(xstr("connect_gate"))
942 labelAlignment.add(label)
943 table.attach(labelAlignment, 0, 1, 4, 5)
944
945 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
946 self._departureGate = gtk.Label()
947 self._departureGate.set_width_chars(5)
948 self._departureGate.set_alignment(0.0, 0.5)
949 labelAlignment.add(self._departureGate)
950 table.attach(labelAlignment, 1, 2, 4, 5)
951
952 self.addCancelFlightButton()
953
954 self.addPreviousButton(clicked = self._backClicked)
955
956 self._button = self.addButton(xstr("button_connect"), default = True,
957 tooltip = xstr("button_connect_tooltip"))
958 self._clickedID = self._button.connect("clicked", self._connectClicked)
959
960 def activate(self):
961 """Setup the departure information."""
962 self._button.set_label(xstr("button_connect"))
963 self._button.set_use_underline(True)
964 self._button.set_tooltip_text(xstr("button_connect_tooltip"))
965 self._button.disconnect(self._clickedID)
966 self._clickedID = self._button.connect("clicked", self._connectClicked)
967
968 bookedFlight = self._wizard._bookedFlight
969
970 self._flightNumber.set_markup("<b>" + bookedFlight.callsign + "</b>")
971
972 aircraftType = aircraftNames[bookedFlight.aircraftType]
973 self._aircraft.set_markup("<b>" + aircraftType + "</b>")
974
975 self._tailNumber.set_markup("<b>" + bookedFlight.tailNumber + "</b>")
976
977 icao = bookedFlight.departureICAO
978 self._departureICAO.set_markup("<b>" + icao + "</b>")
979 gate = self._wizard._departureGate
980 if gate!="-":
981 gate = "<b>" + gate + "</b>"
982 self._departureGate.set_markup(gate)
983
984 def finalize(self):
985 """Finalize the page."""
986 self._button.set_label(xstr("button_next"))
987 self._button.set_use_underline(True)
988 self._button.set_tooltip_text(xstr("button_next_tooltip"))
989 self._button.disconnect(self._clickedID)
990 self._clickedID = self._button.connect("clicked", self._forwardClicked)
991
992 def _backClicked(self, button):
993 """Called when the Back button is pressed."""
994 self.goBack()
995
996 def _connectClicked(self, button):
997 """Called when the Connect button is pressed."""
998 self._wizard._connectSimulator()
999
1000 def _forwardClicked(self, button):
1001 """Called when the Forward button is pressed."""
1002 self._wizard.nextPage()
1003
1004#-----------------------------------------------------------------------------
1005
1006class PayloadPage(Page):
1007 """Page to allow setting up the payload."""
1008 def __init__(self, wizard):
1009 """Construct the page."""
1010 super(PayloadPage, self).__init__(wizard, xstr("payload_title"),
1011 xstr("payload_help"),
1012 completedHelp = xstr("payload_chelp"))
1013
1014 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1015 xscale = 0.0, yscale = 0.0)
1016
1017 table = gtk.Table(7, 3)
1018 table.set_row_spacings(4)
1019 table.set_col_spacings(16)
1020 table.set_homogeneous(False)
1021 alignment.add(table)
1022 self.setMainWidget(alignment)
1023
1024 label = gtk.Label(xstr("payload_crew"))
1025 label.set_use_underline(True)
1026 label.set_alignment(0.0, 0.5)
1027 table.attach(label, 0, 1, 0, 1)
1028
1029 self._numCrew = IntegerEntry(defaultValue = 0)
1030 self._numCrew.set_width_chars(6)
1031 self._numCrew.connect("integer-changed", self._weightChanged)
1032 self._numCrew.set_tooltip_text(xstr("payload_crew_tooltip"))
1033 table.attach(self._numCrew, 1, 2, 0, 1)
1034 label.set_mnemonic_widget(self._numCrew)
1035
1036 label = gtk.Label(xstr("payload_pax"))
1037 label.set_use_underline(True)
1038 label.set_alignment(0.0, 0.5)
1039 table.attach(label, 0, 1, 1, 2)
1040
1041 self._numPassengers = IntegerEntry(defaultValue = 0)
1042 self._numPassengers.set_width_chars(6)
1043 self._numPassengers.connect("integer-changed", self._weightChanged)
1044 self._numPassengers.set_tooltip_text(xstr("payload_pax_tooltip"))
1045 table.attach(self._numPassengers, 1, 2, 1, 2)
1046 label.set_mnemonic_widget(self._numPassengers)
1047
1048 label = gtk.Label(xstr("payload_bag"))
1049 label.set_use_underline(True)
1050 label.set_alignment(0.0, 0.5)
1051 table.attach(label, 0, 1, 2, 3)
1052
1053 self._bagWeight = IntegerEntry(defaultValue = 0)
1054 self._bagWeight.set_width_chars(6)
1055 self._bagWeight.connect("integer-changed", self._weightChanged)
1056 self._bagWeight.set_tooltip_text(xstr("payload_bag_tooltip"))
1057 table.attach(self._bagWeight, 1, 2, 2, 3)
1058 label.set_mnemonic_widget(self._bagWeight)
1059
1060 table.attach(gtk.Label("kg"), 2, 3, 2, 3)
1061
1062 label = gtk.Label(xstr("payload_cargo"))
1063 label.set_use_underline(True)
1064 label.set_alignment(0.0, 0.5)
1065 table.attach(label, 0, 1, 3, 4)
1066
1067 self._cargoWeight = IntegerEntry(defaultValue = 0)
1068 self._cargoWeight.set_width_chars(6)
1069 self._cargoWeight.connect("integer-changed", self._weightChanged)
1070 self._cargoWeight.set_tooltip_text(xstr("payload_cargo_tooltip"))
1071 table.attach(self._cargoWeight, 1, 2, 3, 4)
1072 label.set_mnemonic_widget(self._cargoWeight)
1073
1074 table.attach(gtk.Label("kg"), 2, 3, 3, 4)
1075
1076 label = gtk.Label(xstr("payload_mail"))
1077 label.set_use_underline(True)
1078 label.set_alignment(0.0, 0.5)
1079 table.attach(label, 0, 1, 4, 5)
1080
1081 self._mailWeight = IntegerEntry(defaultValue = 0)
1082 self._mailWeight.set_width_chars(6)
1083 self._mailWeight.connect("integer-changed", self._weightChanged)
1084 self._mailWeight.set_tooltip_text(xstr("payload_mail_tooltip"))
1085 table.attach(self._mailWeight, 1, 2, 4, 5)
1086 label.set_mnemonic_widget(self._mailWeight)
1087
1088 table.attach(gtk.Label("kg"), 2, 3, 4, 5)
1089
1090 label = gtk.Label("<b>" + xstr("payload_zfw") + "</b>")
1091 label.set_alignment(0.0, 0.5)
1092 label.set_use_markup(True)
1093 table.attach(label, 0, 1, 5, 6)
1094
1095 self._calculatedZFW = gtk.Label()
1096 self._calculatedZFW.set_width_chars(6)
1097 self._calculatedZFW.set_alignment(1.0, 0.5)
1098 table.attach(self._calculatedZFW, 1, 2, 5, 6)
1099
1100 table.attach(gtk.Label("kg"), 2, 3, 5, 6)
1101
1102 self._zfwButton = gtk.Button(xstr("payload_fszfw"))
1103 self._zfwButton.set_use_underline(True)
1104 self._zfwButton.connect("clicked", self._zfwRequested)
1105 self._zfwButton.set_tooltip_text(xstr("payload_fszfw_tooltip"))
1106 table.attach(self._zfwButton, 0, 1, 6, 7)
1107
1108 self._simulatorZFW = gtk.Label("-")
1109 self._simulatorZFW.set_width_chars(6)
1110 self._simulatorZFW.set_alignment(1.0, 0.5)
1111 table.attach(self._simulatorZFW, 1, 2, 6, 7)
1112 self._simulatorZFWValue = None
1113
1114 table.attach(gtk.Label("kg"), 2, 3, 6, 7)
1115
1116 self.addCancelFlightButton()
1117 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1118 self._button = self.addNextButton(clicked = self._forwardClicked)
1119
1120 @property
1121 def numCrew(self):
1122 """The number of the crew members on the flight."""
1123 return self._numCrew.get_int()
1124
1125 @property
1126 def numPassengers(self):
1127 """The number of the passengers on the flight."""
1128 return self._numPassengers.get_int()
1129
1130 @property
1131 def bagWeight(self):
1132 """Get the bag weight entered."""
1133 return self._bagWeight.get_int()
1134
1135 @property
1136 def cargoWeight(self):
1137 """Get the cargo weight entered."""
1138 return self._cargoWeight.get_int()
1139
1140 @property
1141 def mailWeight(self):
1142 """Get the bag weight entered."""
1143 return self._mailWeight.get_int()
1144
1145 def activate(self):
1146 """Setup the information."""
1147 bookedFlight = self._wizard._bookedFlight
1148
1149 self._numCrew.set_int(bookedFlight.numCrew)
1150 self._numCrew.set_sensitive(True)
1151 self._numPassengers.set_int(bookedFlight.numPassengers)
1152 self._numPassengers.set_sensitive(True)
1153
1154 self._bagWeight.set_int(bookedFlight.bagWeight)
1155 self._bagWeight.set_sensitive(True)
1156 self._cargoWeight.set_int(bookedFlight.cargoWeight)
1157 self._cargoWeight.set_sensitive(True)
1158 self._mailWeight.set_int(bookedFlight.mailWeight)
1159 self._mailWeight.set_sensitive(True)
1160
1161 self._simulatorZFW.set_text("-")
1162 self._simulatorZFWValue = None
1163 self._zfwButton.set_sensitive(True)
1164 self._updateCalculatedZFW()
1165
1166 def finalize(self):
1167 """Finalize the payload page."""
1168 self._numCrew.set_sensitive(False)
1169 self._numPassengers.set_sensitive(False)
1170 self._bagWeight.set_sensitive(False)
1171 self._cargoWeight.set_sensitive(False)
1172 self._mailWeight.set_sensitive(False)
1173 self._wizard.gui.initializeWeightHelp()
1174
1175 def calculateZFW(self):
1176 """Calculate the ZFW value."""
1177 zfw = self._wizard.gui._flight.aircraft.dow
1178 zfw += (self._numCrew.get_int() + self._numPassengers.get_int()) * 82
1179 zfw += self._bagWeight.get_int()
1180 zfw += self._cargoWeight.get_int()
1181 zfw += self._mailWeight.get_int()
1182 return zfw
1183
1184 def _updateCalculatedZFW(self):
1185 """Update the calculated ZFW"""
1186 zfw = self.calculateZFW()
1187
1188 markupBegin = "<b>"
1189 markupEnd = "</b>"
1190 if self._simulatorZFWValue is not None and \
1191 PayloadChecker.isZFWFaulty(self._simulatorZFWValue, zfw):
1192 markupBegin += '<span foreground="red">'
1193 markupEnd = "</span>" + markupEnd
1194 self._calculatedZFW.set_markup(markupBegin + str(zfw) + markupEnd)
1195
1196 def _weightChanged(self, entry, weight):
1197 """Called when one of the weight values or humanm counts has changed."""
1198 self._updateCalculatedZFW()
1199
1200 def _zfwRequested(self, button):
1201 """Called when the ZFW is requested from the simulator."""
1202 self._zfwButton.set_sensitive(False)
1203 self._backButton.set_sensitive(False)
1204 self._button.set_sensitive(False)
1205 gui = self._wizard.gui
1206 gui.beginBusy(xstr("payload_zfw_busy"))
1207 gui.simulator.requestZFW(self._handleZFW)
1208
1209 def _handleZFW(self, zfw):
1210 """Called when the ZFW value is retrieved."""
1211 gobject.idle_add(self._processZFW, zfw)
1212
1213 def _processZFW(self, zfw):
1214 """Process the given ZFW value received from the simulator."""
1215 self._wizard.gui.endBusy()
1216 self._zfwButton.set_sensitive(True)
1217 self._backButton.set_sensitive(True)
1218 self._button.set_sensitive(True)
1219 self._simulatorZFWValue = zfw
1220 self._simulatorZFW.set_text("%.0f" % (zfw,))
1221 self._updateCalculatedZFW()
1222
1223 def _forwardClicked(self, button):
1224 """Called when the forward button is clicked."""
1225 self._wizard.nextPage()
1226
1227 def _backClicked(self, button):
1228 """Called when the Back button is pressed."""
1229 self.goBack()
1230
1231#-----------------------------------------------------------------------------
1232
1233class TimePage(Page):
1234 """Page displaying the departure and arrival times and allows querying the
1235 current time from the flight simulator."""
1236 def __init__(self, wizard):
1237 super(TimePage, self).__init__(wizard, xstr("time_title"),
1238 xstr("time_help"),
1239 completedHelp = xstr("time_chelp"))
1240
1241 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1242 xscale = 0.0, yscale = 0.0)
1243
1244 table = gtk.Table(3, 2)
1245 table.set_row_spacings(4)
1246 table.set_col_spacings(16)
1247 table.set_homogeneous(False)
1248 alignment.add(table)
1249 self.setMainWidget(alignment)
1250
1251 label = gtk.Label(xstr("time_departure"))
1252 label.set_alignment(0.0, 0.5)
1253 table.attach(label, 0, 1, 0, 1)
1254
1255 self._departure = gtk.Label()
1256 self._departure.set_alignment(0.0, 0.5)
1257 table.attach(self._departure, 1, 2, 0, 1)
1258
1259 label = gtk.Label(xstr("time_arrival"))
1260 label.set_alignment(0.0, 0.5)
1261 table.attach(label, 0, 1, 1, 2)
1262
1263 self._arrival = gtk.Label()
1264 self._arrival.set_alignment(0.0, 0.5)
1265 table.attach(self._arrival, 1, 2, 1, 2)
1266
1267 self._timeButton = gtk.Button(xstr("time_fs"))
1268 self._timeButton.set_use_underline(True)
1269 self._timeButton.set_tooltip_text(xstr("time_fs_tooltip"))
1270 self._timeButton.connect("clicked", self._timeRequested)
1271 table.attach(self._timeButton, 0, 1, 2, 3)
1272
1273 self._simulatorTime = gtk.Label("-")
1274 self._simulatorTime.set_alignment(0.0, 0.5)
1275 table.attach(self._simulatorTime, 1, 2, 2, 3)
1276
1277 self.addCancelFlightButton()
1278
1279 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1280 self._button = self.addNextButton(clicked = self._forwardClicked)
1281
1282 def activate(self):
1283 """Activate the page."""
1284 self._timeButton.set_sensitive(True)
1285 bookedFlight = self._wizard._bookedFlight
1286 self._departure.set_text(str(bookedFlight.departureTime.time()))
1287 self._arrival.set_text(str(bookedFlight.arrivalTime.time()))
1288 self._simulatorTime.set_text("-")
1289
1290 def _timeRequested(self, button):
1291 """Request the time from the simulator."""
1292 self._timeButton.set_sensitive(False)
1293 self._backButton.set_sensitive(False)
1294 self._button.set_sensitive(False)
1295 self._wizard.gui.beginBusy(xstr("time_busy"))
1296 self._wizard.gui.simulator.requestTime(self._handleTime)
1297
1298 def _handleTime(self, timestamp):
1299 """Handle the result of a time retrieval."""
1300 gobject.idle_add(self._processTime, timestamp)
1301
1302 def _processTime(self, timestamp):
1303 """Process the given time."""
1304 self._wizard.gui.endBusy()
1305 self._timeButton.set_sensitive(True)
1306 self._backButton.set_sensitive(True)
1307 self._button.set_sensitive(True)
1308 tm = time.gmtime(timestamp)
1309 t = datetime.time(tm.tm_hour, tm.tm_min, tm.tm_sec)
1310 self._simulatorTime.set_text(str(t))
1311
1312 ts = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec
1313 dt = self._wizard._bookedFlight.departureTime.time()
1314 dts = dt.hour * 3600 + dt.minute * 60 + dt.second
1315 diff = dts-ts
1316
1317 markupBegin = ""
1318 markupEnd = ""
1319 if diff < 0:
1320 markupBegin = '<b><span foreground="red">'
1321 markupEnd = '</span></b>'
1322 elif diff < 3*60 or diff > 30*60:
1323 markupBegin = '<b><span foreground="orange">'
1324 markupEnd = '</span></b>'
1325
1326 self._departure.set_markup(markupBegin + str(dt) + markupEnd)
1327
1328 def _backClicked(self, button):
1329 """Called when the Back button is pressed."""
1330 self.goBack()
1331
1332 def _forwardClicked(self, button):
1333 """Called when the forward button is clicked."""
1334 if not self._completed:
1335 gui = self._wizard.gui
1336 gui.beginBusy(xstr("fuel_get_busy"))
1337
1338 gui.simulator.getFuel(self._handleFuel)
1339 else:
1340 self._wizard.nextPage()
1341
1342 def _handleFuel(self, fuelData):
1343 """Callback for the fuel query operation."""
1344 gobject.idle_add(self._processFuel, fuelData)
1345
1346 def _processFuel(self, fuelData):
1347 """Process the given fuel data."""
1348 self._wizard.gui.endBusy()
1349 self._wizard._fuelData = fuelData
1350 self._wizard.nextPage()
1351
1352#-----------------------------------------------------------------------------
1353
1354class FuelTank(gtk.VBox):
1355 """Widget for the fuel tank."""
1356 def __init__(self, fuelTank, name, capacity, currentWeight):
1357 """Construct the widget for the tank with the given name."""
1358 super(FuelTank, self).__init__()
1359
1360 self._enabled = True
1361 self.fuelTank = fuelTank
1362 self.capacity = capacity
1363 self.currentWeight = currentWeight
1364 self.expectedWeight = currentWeight
1365
1366 label = gtk.Label("<b>" + name + "</b>")
1367 label.set_use_markup(True)
1368 label.set_use_underline(True)
1369 label.set_justify(JUSTIFY_CENTER)
1370 label.set_alignment(0.5, 1.0)
1371 self.pack_start(label, False, False, 4)
1372
1373 self._tankFigure = gtk.EventBox()
1374 self._tankFigure.set_size_request(38, -1)
1375 self._tankFigure.set_visible_window(False)
1376 self._tankFigure.set_tooltip_markup(xstr("fuel_tank_tooltip"))
1377
1378 if pygobject:
1379 self._tankFigure.connect("draw", self._drawTankFigure)
1380 else:
1381 self._tankFigure.connect("expose_event", self._drawTankFigure)
1382 self._tankFigure.connect("button_press_event", self._buttonPressed)
1383 self._tankFigure.connect("motion_notify_event", self._motionNotify)
1384 self._tankFigure.connect("scroll-event", self._scrolled)
1385
1386 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1387 xscale = 0.0, yscale = 1.0)
1388 alignment.add(self._tankFigure)
1389
1390 self.pack_start(alignment, True, True, 4)
1391
1392 self._expectedButton = gtk.SpinButton()
1393 self._expectedButton.set_numeric(True)
1394 self._expectedButton.set_range(0, self.capacity)
1395 self._expectedButton.set_increments(10, 100)
1396 self._expectedButton.set_value(currentWeight)
1397 self._expectedButton.set_alignment(1.0)
1398 self._expectedButton.set_width_chars(5)
1399 self._expectedButton.connect("value-changed", self._expectedChanged)
1400
1401 label.set_mnemonic_widget(self._expectedButton)
1402
1403 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1404 xscale = 0.0, yscale = 1.0)
1405 alignment.add(self._expectedButton)
1406 self.pack_start(alignment, False, False, 4)
1407
1408 def setCurrent(self, currentWeight):
1409 """Set the current weight."""
1410 self.currentWeight = currentWeight
1411 self._redraw()
1412
1413 def isCorrect(self):
1414 """Determine if the contents of the fuel tank are as expected"""
1415 return abs(self.expectedWeight - self.currentWeight)<=1
1416
1417 def disable(self):
1418 """Disable the fuel tank."""
1419 self._expectedButton.set_sensitive(False)
1420 self._enabled = False
1421
1422 def _redraw(self):
1423 """Redraw the tank figure."""
1424 self._tankFigure.queue_draw()
1425
1426 def _drawTankFigure(self, tankFigure, eventOrContext):
1427 """Draw the tank figure."""
1428 triangleSize = 5
1429
1430 context = eventOrContext if pygobject else tankFigure.window.cairo_create()
1431 (xOffset, yOffset) = (0, 0) if pygobject \
1432 else (tankFigure.allocation.x, tankFigure.allocation.y)
1433
1434 width = tankFigure.get_allocated_width() if pygobject \
1435 else tankFigure.allocation.width
1436 height = tankFigure.get_allocated_height() if pygobject \
1437 else tankFigure.allocation.height
1438
1439 rectangleX0 = triangleSize
1440 rectangleY0 = triangleSize
1441 rectangleX1 = width - 1 - triangleSize
1442 rectangleY1 = height - 1 - triangleSize
1443 rectangleLineWidth = 2.0
1444
1445 context.set_source_rgb(0.0, 0.0, 0.0)
1446 context.set_line_width(rectangleLineWidth)
1447 context.rectangle(xOffset + rectangleX0 + rectangleLineWidth/2,
1448 yOffset + rectangleY0 + rectangleLineWidth/2,
1449 rectangleX1 - rectangleX0 - rectangleLineWidth,
1450 rectangleY1 - rectangleY0 - rectangleLineWidth)
1451 context.stroke()
1452
1453 rectangleInnerLeft = rectangleX0 + rectangleLineWidth
1454 rectangleInnerRight = rectangleX1 - rectangleLineWidth
1455 self._rectangleInnerTop = rectangleInnerTop = rectangleY0 + rectangleLineWidth
1456 self._rectangleInnerBottom = rectangleInnerBottom = rectangleY1 - rectangleLineWidth
1457
1458 rectangleInnerWidth = rectangleInnerRight - rectangleInnerLeft
1459 rectangleInnerHeight = rectangleInnerBottom - rectangleInnerTop
1460
1461 context.set_source_rgb(1.0, 0.9, 0.6)
1462 currentHeight = self.currentWeight * rectangleInnerHeight / self.capacity
1463 currentX = rectangleInnerTop + rectangleInnerHeight - currentHeight
1464 context.rectangle(xOffset + rectangleInnerLeft,
1465 yOffset + rectangleInnerTop +
1466 rectangleInnerHeight - currentHeight,
1467 rectangleInnerWidth, currentHeight)
1468 context.fill()
1469
1470 expectedHeight = self.expectedWeight * rectangleInnerHeight / self.capacity
1471 expectedY = rectangleInnerTop + rectangleInnerHeight - expectedHeight
1472
1473 context.set_line_width(1.5)
1474 context.set_source_rgb(0.0, 0.85, 0.85)
1475 context.move_to(xOffset + rectangleX0, yOffset + expectedY)
1476 context.line_to(xOffset + rectangleX1, yOffset + expectedY)
1477 context.stroke()
1478
1479 context.set_line_width(0.0)
1480 context.move_to(xOffset + 0, yOffset + expectedY - triangleSize)
1481 context.line_to(xOffset + 0, yOffset + expectedY + triangleSize)
1482 context.line_to(xOffset + rectangleX0 + 1, yOffset + expectedY)
1483 context.line_to(xOffset + 0, yOffset + expectedY - triangleSize)
1484 context.fill()
1485
1486 context.set_line_width(0.0)
1487 context.move_to(xOffset + width, yOffset + expectedY - triangleSize)
1488 context.line_to(xOffset + width, yOffset + expectedY + triangleSize)
1489 context.line_to(xOffset + rectangleX1 - 1, yOffset + expectedY)
1490 context.line_to(xOffset + width, yOffset + expectedY - triangleSize)
1491 context.fill()
1492
1493 return True
1494
1495 def _setExpectedFromY(self, y):
1496 """Set the expected weight from the given Y-coordinate."""
1497 level = (self._rectangleInnerBottom - y) / \
1498 (self._rectangleInnerBottom - self._rectangleInnerTop)
1499 level = min(1.0, max(0.0, level))
1500 self._expectedButton.set_value(level * self.capacity)
1501
1502 def _buttonPressed(self, tankFigure, event):
1503 """Called when a button is pressed in the figure.
1504
1505 The expected level will be set there."""
1506 if self._enabled and event.button==1:
1507 self._setExpectedFromY(event.y)
1508
1509 def _motionNotify(self, tankFigure, event):
1510 """Called when the mouse pointer moves within the area of a tank figure."""
1511 if self._enabled and event.state==BUTTON1_MASK:
1512 self._setExpectedFromY(event.y)
1513
1514 def _scrolled(self, tankFigure, event):
1515 """Called when a scroll event is received."""
1516 if self._enabled:
1517 increment = 1 if event.state==CONTROL_MASK \
1518 else 100 if event.state==SHIFT_MASK \
1519 else 10 if event.state==0 else 0
1520 if increment!=0:
1521 if event.direction==SCROLL_DOWN:
1522 increment *= -1
1523 self._expectedButton.spin(SPIN_USER_DEFINED, increment)
1524
1525 def _expectedChanged(self, spinButton):
1526 """Called when the expected value has changed."""
1527 self.expectedWeight = spinButton.get_value_as_int()
1528 self._redraw()
1529
1530#-----------------------------------------------------------------------------
1531
1532class FuelPage(Page):
1533 """The page containing the fuel tank filling."""
1534 _pumpStep = 0.02
1535
1536 def __init__(self, wizard):
1537 """Construct the page."""
1538 super(FuelPage, self).__init__(wizard, xstr("fuel_title"),
1539 xstr("fuel_help"),
1540 completedHelp = xstr("fuel_chelp"))
1541
1542 self._fuelTanks = []
1543 self._fuelTable = None
1544 self._fuelAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1545 xscale = 0.0, yscale = 1.0)
1546 self.setMainWidget(self._fuelAlignment)
1547
1548 tankData = [(tank, 2500, 3900) for tank in acft.mostFuelTanks]
1549 self._setupTanks(tankData)
1550
1551 self.addCancelFlightButton()
1552
1553 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1554 self._button = self.addNextButton(clicked = self._forwardClicked)
1555
1556 self._pumpIndex = 0
1557
1558 def activate(self):
1559 """Activate the page."""
1560 self._setupTanks(self._wizard._fuelData)
1561
1562 def finalize(self):
1563 """Finalize the page."""
1564 for fuelTank in self._fuelTanks:
1565 fuelTank.disable()
1566
1567 def _backClicked(self, button):
1568 """Called when the Back button is pressed."""
1569 self.goBack()
1570
1571 def _forwardClicked(self, button):
1572 """Called when the forward button is clicked."""
1573 if not self._completed:
1574 self._pumpIndex = 0
1575 self._wizard.gui.beginBusy(xstr("fuel_pump_busy"))
1576 self._pump()
1577 else:
1578 self._wizard.nextPage()
1579
1580 def _setupTanks(self, tankData):
1581 """Setup the tanks for the given data."""
1582 numTanks = len(tankData)
1583 if self._fuelTable is not None:
1584 self._fuelAlignment.remove(self._fuelTable)
1585
1586 self._fuelTanks = []
1587 self._fuelTable = gtk.Table(numTanks, 1)
1588 self._fuelTable.set_col_spacings(16)
1589 index = 0
1590 for (tank, current, capacity) in tankData:
1591 fuelTank = FuelTank(tank,
1592 xstr("fuel_tank_" +
1593 const.fuelTank2string(tank)),
1594 capacity, current)
1595 self._fuelTable.attach(fuelTank, index, index+1, 0, 1)
1596 self._fuelTanks.append(fuelTank)
1597 index += 1
1598
1599 self._fuelAlignment.add(self._fuelTable)
1600 self.show_all()
1601
1602 def _pump(self):
1603 """Perform one step of pumping.
1604
1605 It is checked, if the current tank's contents are of the right
1606 quantity. If not, it is filled one step further to the desired
1607 contents. Otherwise the next tank is started. If all tanks are are
1608 filled, the next page is selected."""
1609 numTanks = len(self._fuelTanks)
1610
1611 fuelTank = None
1612 while self._pumpIndex < numTanks:
1613 fuelTank = self._fuelTanks[self._pumpIndex]
1614 if fuelTank.isCorrect():
1615 self._pumpIndex += 1
1616 fuelTank = None
1617 else:
1618 break
1619
1620 if fuelTank is None:
1621 self._wizard.gui.endBusy()
1622 self._wizard.nextPage()
1623 else:
1624 currentLevel = fuelTank.currentWeight / fuelTank.capacity
1625 expectedLevel = fuelTank.expectedWeight / fuelTank.capacity
1626 if currentLevel<expectedLevel:
1627 currentLevel += FuelPage._pumpStep
1628 if currentLevel>expectedLevel: currentLevel = expectedLevel
1629 else:
1630 currentLevel -= FuelPage._pumpStep
1631 if currentLevel<expectedLevel: currentLevel = expectedLevel
1632 fuelTank.setCurrent(currentLevel * fuelTank.capacity)
1633 self._wizard.gui.simulator.setFuelLevel([(fuelTank.fuelTank,
1634 currentLevel)])
1635 gobject.timeout_add(50, self._pump)
1636
1637#-----------------------------------------------------------------------------
1638
1639class RoutePage(Page):
1640 """The page containing the route and the flight level."""
1641 def __init__(self, wizard):
1642 """Construct the page."""
1643 super(RoutePage, self).__init__(wizard, xstr("route_title"),
1644 xstr("route_help"),
1645 completedHelp = xstr("route_chelp"))
1646
1647 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1648 xscale = 0.0, yscale = 0.0)
1649
1650 mainBox = gtk.VBox()
1651 alignment.add(mainBox)
1652 self.setMainWidget(alignment)
1653
1654 levelBox = gtk.HBox()
1655
1656 label = gtk.Label(xstr("route_level"))
1657 label.set_use_underline(True)
1658 levelBox.pack_start(label, True, True, 0)
1659
1660 self._cruiseLevel = gtk.SpinButton()
1661 self._cruiseLevel.set_increments(step = 10, page = 100)
1662 self._cruiseLevel.set_range(min = 50, max = 500)
1663 self._cruiseLevel.set_tooltip_text(xstr("route_level_tooltip"))
1664 self._cruiseLevel.set_numeric(True)
1665 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
1666 label.set_mnemonic_widget(self._cruiseLevel)
1667
1668 levelBox.pack_start(self._cruiseLevel, False, False, 8)
1669
1670 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1671 xscale = 0.0, yscale = 0.0)
1672 alignment.add(levelBox)
1673
1674 mainBox.pack_start(alignment, False, False, 0)
1675
1676
1677 routeBox = gtk.VBox()
1678
1679 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1680 xscale = 0.0, yscale = 0.0)
1681 label = gtk.Label(xstr("route_route"))
1682 label.set_use_underline(True)
1683 alignment.add(label)
1684 routeBox.pack_start(alignment, True, True, 0)
1685
1686 routeWindow = gtk.ScrolledWindow()
1687 routeWindow.set_size_request(400, 80)
1688 routeWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
1689 else gtk.SHADOW_IN)
1690 routeWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1691 else gtk.POLICY_AUTOMATIC,
1692 gtk.PolicyType.AUTOMATIC if pygobject
1693 else gtk.POLICY_AUTOMATIC)
1694
1695 self._uppercasingRoute = False
1696
1697 self._route = gtk.TextView()
1698 self._route.set_tooltip_text(xstr("route_route_tooltip"))
1699 self._route.set_wrap_mode(WRAP_WORD)
1700 self._route.get_buffer().connect("changed", self._routeChanged)
1701 self._route.get_buffer().connect_after("insert-text", self._routeInserted)
1702 routeWindow.add(self._route)
1703
1704 label.set_mnemonic_widget(self._route)
1705 routeBox.pack_start(routeWindow, True, True, 0)
1706
1707 mainBox.pack_start(routeBox, True, True, 8)
1708
1709 self.addCancelFlightButton()
1710
1711 self._backButton = self.addPreviousButton(clicked = self._backClicked)
1712 self._button = self.addNextButton(clicked = self._forwardClicked)
1713
1714 @property
1715 def filedCruiseLevel(self):
1716 """Get the filed cruise level."""
1717 return self._cruiseLevel.get_value_as_int()
1718
1719 @property
1720 def route(self):
1721 """Get the route."""
1722 return self._getRoute()
1723
1724 def activate(self):
1725 """Setup the route from the booked flight."""
1726 self._cruiseLevel.set_value(240)
1727 self._route.get_buffer().set_text(self._wizard._bookedFlight.route)
1728 self._updateForwardButton()
1729
1730 def _getRoute(self):
1731 """Get the text of the route."""
1732 buffer = self._route.get_buffer()
1733 return buffer.get_text(buffer.get_start_iter(),
1734 buffer.get_end_iter(), True)
1735
1736 def _updateForwardButton(self):
1737 """Update the sensitivity of the forward button."""
1738 self._button.set_sensitive(self._cruiseLevel.get_value_as_int()>=50 and \
1739 self._getRoute()!="")
1740
1741 def _cruiseLevelChanged(self, spinButton):
1742 """Called when the cruise level has changed."""
1743 self._updateForwardButton()
1744
1745 def _routeChanged(self, textBuffer):
1746 """Called when the route has changed."""
1747 if not self._uppercasingRoute:
1748 self._updateForwardButton()
1749
1750 def _routeInserted(self, textBuffer, iter, text, length):
1751 """Called when new characters are inserted into the route.
1752
1753 It uppercases all characters."""
1754 if not self._uppercasingRoute:
1755 self._uppercasingRoute = True
1756
1757 iter1 = iter.copy()
1758 iter1.backward_chars(length)
1759 textBuffer.delete(iter, iter1)
1760
1761 textBuffer.insert(iter, text.upper())
1762
1763 self._uppercasingRoute = False
1764
1765 def _backClicked(self, button):
1766 """Called when the Back button is pressed."""
1767 self.goBack()
1768
1769 def _forwardClicked(self, button):
1770 """Called when the Forward button is clicked."""
1771 if self._completed:
1772 self._wizard.nextPage()
1773 else:
1774 bookedFlight = self._wizard._bookedFlight
1775 self._wizard.gui.beginBusy(xstr("route_down_notams"))
1776 self._wizard.gui.webHandler.getNOTAMs(self._notamsCallback,
1777 bookedFlight.departureICAO,
1778 bookedFlight.arrivalICAO)
1779 startSound(const.SOUND_NOTAM)
1780
1781 def _notamsCallback(self, returned, result):
1782 """Callback for the NOTAMs."""
1783 gobject.idle_add(self._handleNOTAMs, returned, result)
1784
1785 def _handleNOTAMs(self, returned, result):
1786 """Handle the NOTAMs."""
1787 if returned:
1788 self._wizard._departureNOTAMs = result.departureNOTAMs
1789 self._wizard._arrivalNOTAMs = result.arrivalNOTAMs
1790 else:
1791 self._wizard._departureNOTAMs = None
1792 self._wizard._arrivalNOTAMs = None
1793
1794 bookedFlight = self._wizard._bookedFlight
1795 self._wizard.gui.beginBusy(xstr("route_down_metars"))
1796 self._wizard.gui.webHandler.getMETARs(self._metarsCallback,
1797 [bookedFlight.departureICAO,
1798 bookedFlight.arrivalICAO])
1799
1800 def _metarsCallback(self, returned, result):
1801 """Callback for the METARs."""
1802 gobject.idle_add(self._handleMETARs, returned, result)
1803
1804 def _handleMETARs(self, returned, result):
1805 """Handle the METARs."""
1806 self._wizard._departureMETAR = None
1807 self._wizard._arrivalMETAR = None
1808 bookedFlight = self._wizard._bookedFlight
1809 if returned:
1810 if bookedFlight.departureICAO in result.metars:
1811 self._wizard._departureMETAR = result.metars[bookedFlight.departureICAO]
1812 if bookedFlight.arrivalICAO in result.metars:
1813 self._wizard._arrivalMETAR = result.metars[bookedFlight.arrivalICAO]
1814
1815 self._wizard.gui.endBusy()
1816 self._backButton.set_sensitive(True)
1817 self._button.set_sensitive(True)
1818 self._wizard.nextPage()
1819
1820#-----------------------------------------------------------------------------
1821
1822class BriefingPage(Page):
1823 """Page for the briefing."""
1824 def __init__(self, wizard, departure):
1825 """Construct the briefing page."""
1826 self._departure = departure
1827
1828 title = xstr("briefing_title") % (1 if departure else 2,
1829 xstr("briefing_departure")
1830 if departure
1831 else xstr("briefing_arrival"))
1832 super(BriefingPage, self).__init__(wizard, title, xstr("briefing_help"),
1833 completedHelp = xstr("briefing_chelp"))
1834
1835 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1836 xscale = 1.0, yscale = 1.0)
1837
1838 mainBox = gtk.VBox()
1839 alignment.add(mainBox)
1840 self.setMainWidget(alignment)
1841
1842 self._notamsFrame = gtk.Frame()
1843 self._notamsFrame.set_label(xstr("briefing_notams_init"))
1844 scrolledWindow = gtk.ScrolledWindow()
1845 scrolledWindow.set_size_request(-1, 128)
1846 # FIXME: these constants should be in common
1847 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1848 else gtk.POLICY_AUTOMATIC,
1849 gtk.PolicyType.AUTOMATIC if pygobject
1850 else gtk.POLICY_AUTOMATIC)
1851 self._notams = gtk.TextView()
1852 self._notams.set_editable(False)
1853 self._notams.set_accepts_tab(False)
1854 self._notams.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
1855 scrolledWindow.add(self._notams)
1856 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1857 xscale = 1.0, yscale = 1.0)
1858 alignment.set_padding(padding_top = 4, padding_bottom = 0,
1859 padding_left = 0, padding_right = 0)
1860 alignment.add(scrolledWindow)
1861 self._notamsFrame.add(alignment)
1862 mainBox.pack_start(self._notamsFrame, True, True, 4)
1863
1864 self._metarFrame = gtk.Frame()
1865 self._metarFrame.set_label(xstr("briefing_metar_init"))
1866 scrolledWindow = gtk.ScrolledWindow()
1867 scrolledWindow.set_size_request(-1, 32)
1868 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
1869 else gtk.POLICY_AUTOMATIC,
1870 gtk.PolicyType.AUTOMATIC if pygobject
1871 else gtk.POLICY_AUTOMATIC)
1872
1873 self._uppercasingMETAR = False
1874
1875 self._metar = gtk.TextView()
1876 self._metar.set_accepts_tab(False)
1877 self._metar.set_wrap_mode(gtk.WrapMode.WORD if pygobject else gtk.WRAP_WORD)
1878 self._metar.get_buffer().connect("changed", self._metarChanged)
1879 self._metar.get_buffer().connect_after("insert-text", self._metarInserted)
1880 scrolledWindow.add(self._metar)
1881 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
1882 xscale = 1.0, yscale = 1.0)
1883 alignment.set_padding(padding_top = 4, padding_bottom = 0,
1884 padding_left = 0, padding_right = 0)
1885 alignment.add(scrolledWindow)
1886 self._metarFrame.add(alignment)
1887 mainBox.pack_start(self._metarFrame, True, True, 4)
1888 self.metarEdited = False
1889
1890 self.addCancelFlightButton()
1891
1892 self.addPreviousButton(clicked = self._backClicked)
1893 self._button = self.addNextButton(clicked = self._forwardClicked)
1894
1895 @property
1896 def metar(self):
1897 """Get the METAR on the page."""
1898 buffer = self._metar.get_buffer()
1899 return buffer.get_text(buffer.get_start_iter(),
1900 buffer.get_end_iter(), True)
1901
1902 def setMETAR(self, metar):
1903 """Set the metar."""
1904 self._metar.get_buffer().set_text(metar)
1905 self.metarEdited = False
1906
1907 def activate(self):
1908 """Activate the page."""
1909 if not self._departure:
1910 self._button.set_label(xstr("briefing_button"))
1911 self._button.set_has_tooltip(False)
1912 self._button.set_use_stock(False)
1913
1914 bookedFlight = self._wizard._bookedFlight
1915
1916 icao = bookedFlight.departureICAO if self._departure \
1917 else bookedFlight.arrivalICAO
1918 notams = self._wizard._departureNOTAMs if self._departure \
1919 else self._wizard._arrivalNOTAMs
1920 metar = self._wizard._departureMETAR if self._departure \
1921 else self._wizard._arrivalMETAR
1922
1923 self._notamsFrame.set_label(xstr("briefing_notams_template") % (icao,))
1924 buffer = self._notams.get_buffer()
1925 if notams is None:
1926 buffer.set_text(xstr("briefing_notams_failed"))
1927 elif not notams:
1928 buffer.set_text(xstr("briefing_notams_missing"))
1929 else:
1930 s = ""
1931 for notam in notams:
1932 s += str(notam.begin)
1933 if notam.end is not None:
1934 s += " - " + str(notam.end)
1935 elif notam.permanent:
1936 s += " - PERMANENT"
1937 s += "\n"
1938 if notam.repeatCycle:
1939 s += "Repeat cycle: " + notam.repeatCycle + "\n"
1940 s += notam.notice + "\n"
1941 s += "-------------------- * --------------------\n"
1942 buffer.set_text(s)
1943
1944 self._metarFrame.set_label(xstr("briefing_metar_template") % (icao,))
1945 buffer = self._metar.get_buffer()
1946 if metar is None:
1947 buffer.set_text(xstr("briefing_metar_failed"))
1948 else:
1949 buffer.set_text(metar)
1950
1951 label = self._metarFrame.get_label_widget()
1952 label.set_use_underline(True)
1953 label.set_mnemonic_widget(self._metar)
1954
1955 self.metarEdited = False
1956
1957 def _backClicked(self, button):
1958 """Called when the Back button is pressed."""
1959 self.goBack()
1960
1961 def _forwardClicked(self, button):
1962 """Called when the forward button is clicked."""
1963 if not self._departure:
1964 if not self._completed:
1965 self._wizard.gui.startMonitoring()
1966 self._button.set_label(xstr("button_next"))
1967 self._button.set_tooltip_text(xstr("button_next_tooltip"))
1968 self.complete()
1969
1970 self._wizard.nextPage()
1971
1972 def _metarChanged(self, buffer):
1973 """Called when the METAR has changed."""
1974 if not self._uppercasingMETAR:
1975 self.metarEdited = True
1976 self._button.set_sensitive(buffer.get_text(buffer.get_start_iter(),
1977 buffer.get_end_iter(),
1978 True)!="")
1979
1980 def _metarInserted(self, textBuffer, iter, text, length):
1981 """Called when new characters are inserted into the METAR.
1982
1983 It uppercases all characters."""
1984 if not self._uppercasingMETAR:
1985 self._uppercasingMETAR = True
1986
1987 iter1 = iter.copy()
1988 iter1.backward_chars(length)
1989 textBuffer.delete(iter, iter1)
1990
1991 textBuffer.insert(iter, text.upper())
1992
1993 self._uppercasingMETAR = False
1994
1995#-----------------------------------------------------------------------------
1996
1997class TakeoffPage(Page):
1998 """Page for entering the takeoff data."""
1999 def __init__(self, wizard):
2000 """Construct the takeoff page."""
2001 super(TakeoffPage, self).__init__(wizard, xstr("takeoff_title"),
2002 xstr("takeoff_help"),
2003 completedHelp = xstr("takeoff_chelp"))
2004
2005 self._forwardAllowed = False
2006
2007 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2008 xscale = 0.0, yscale = 0.0)
2009
2010 table = gtk.Table(8, 4)
2011 table.set_row_spacings(4)
2012 table.set_col_spacings(16)
2013 table.set_homogeneous(False)
2014 alignment.add(table)
2015 self.setMainWidget(alignment)
2016
2017 label = gtk.Label(xstr("takeoff_runway"))
2018 label.set_use_underline(True)
2019 label.set_alignment(0.0, 0.5)
2020 table.attach(label, 0, 1, 0, 1)
2021
2022 self._runway = gtk.Entry()
2023 self._runway.set_width_chars(10)
2024 self._runway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
2025 self._runway.connect("changed", self._upperChanged)
2026 table.attach(self._runway, 1, 3, 0, 1)
2027 label.set_mnemonic_widget(self._runway)
2028
2029 label = gtk.Label(xstr("takeoff_sid"))
2030 label.set_use_underline(True)
2031 label.set_alignment(0.0, 0.5)
2032 table.attach(label, 0, 1, 1, 2)
2033
2034 self._sid = gtk.Entry()
2035 self._sid.set_width_chars(10)
2036 self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
2037 self._sid.connect("changed", self._upperChanged)
2038 table.attach(self._sid, 1, 3, 1, 2)
2039 label.set_mnemonic_widget(self._sid)
2040
2041 label = gtk.Label(xstr("takeoff_v1"))
2042 label.set_use_markup(True)
2043 label.set_use_underline(True)
2044 label.set_alignment(0.0, 0.5)
2045 table.attach(label, 0, 1, 2, 3)
2046
2047 self._v1 = IntegerEntry()
2048 self._v1.set_width_chars(4)
2049 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip_knots"))
2050 self._v1.connect("integer-changed", self._valueChanged)
2051 table.attach(self._v1, 2, 3, 2, 3)
2052 label.set_mnemonic_widget(self._v1)
2053
2054 self._v1Unit = gtk.Label(xstr("label_knots"))
2055 self._v1Unit.set_alignment(0.0, 0.5)
2056 table.attach(self._v1Unit, 3, 4, 2, 3)
2057
2058 label = gtk.Label(xstr("takeoff_vr"))
2059 label.set_use_markup(True)
2060 label.set_use_underline(True)
2061 label.set_alignment(0.0, 0.5)
2062 table.attach(label, 0, 1, 3, 4)
2063
2064 self._vr = IntegerEntry()
2065 self._vr.set_width_chars(4)
2066 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip_knots"))
2067 self._vr.connect("integer-changed", self._valueChanged)
2068 table.attach(self._vr, 2, 3, 3, 4)
2069 label.set_mnemonic_widget(self._vr)
2070
2071 self._vrUnit = gtk.Label(xstr("label_knots"))
2072 self._vrUnit.set_alignment(0.0, 0.5)
2073 table.attach(self._vrUnit, 3, 4, 3, 4)
2074
2075 label = gtk.Label(xstr("takeoff_v2"))
2076 label.set_use_markup(True)
2077 label.set_use_underline(True)
2078 label.set_alignment(0.0, 0.5)
2079 table.attach(label, 0, 1, 4, 5)
2080
2081 self._v2 = IntegerEntry()
2082 self._v2.set_width_chars(4)
2083 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip_knots"))
2084 self._v2.connect("integer-changed", self._valueChanged)
2085 table.attach(self._v2, 2, 3, 4, 5)
2086 label.set_mnemonic_widget(self._v2)
2087
2088 self._v2Unit = gtk.Label(xstr("label_knots"))
2089 self._v2Unit.set_alignment(0.0, 0.5)
2090 table.attach(self._v2Unit, 3, 4, 4, 5)
2091
2092 self._derateType = acft.DERATE_NONE
2093
2094 self._derateLabel = gtk.Label()
2095 self._derateLabel.set_use_underline(True)
2096 self._derateLabel.set_markup(xstr("takeoff_derate_tupolev"))
2097 self._derateLabel.set_alignment(0.0, 0.5)
2098 table.attach(self._derateLabel, 0, 1, 5, 6)
2099
2100 self._derate = gtk.Alignment()
2101 table.attach(self._derate, 2, 4, 5, 6)
2102 self._derateWidget = None
2103 self._derateEntry = None
2104 self._derateUnit = None
2105 self._derateButtons = None
2106
2107 self._antiIceOn = gtk.CheckButton(xstr("takeoff_antiice"))
2108 self._antiIceOn.set_use_underline(True)
2109 self._antiIceOn.set_tooltip_text(xstr("takeoff_antiice_tooltip"))
2110 table.attach(self._antiIceOn, 2, 4, 6, 7)
2111
2112 self._rto = gtk.CheckButton(xstr("takeoff_rto"))
2113 self._rto.set_use_underline(True)
2114 self._rto.set_tooltip_text(xstr("takeoff_rto_tooltip"))
2115 self._rto.connect("toggled", self._rtoToggled)
2116 table.attach(self._rto, 2, 4, 7, 8, ypadding = 8)
2117
2118 self.addCancelFlightButton()
2119
2120 self.addPreviousButton(clicked = self._backClicked)
2121
2122 self._button = self.addNextButton(clicked = self._forwardClicked)
2123
2124 @property
2125 def runway(self):
2126 """Get the runway."""
2127 return self._runway.get_text()
2128
2129 @property
2130 def sid(self):
2131 """Get the SID."""
2132 return self._sid.get_text()
2133
2134 @property
2135 def v1(self):
2136 """Get the v1 speed."""
2137 return self._v1.get_int()
2138
2139 @property
2140 def vr(self):
2141 """Get the vr speed."""
2142 return self._vr.get_int()
2143
2144 @property
2145 def v2(self):
2146 """Get the v2 speed."""
2147 return self._v2.get_int()
2148
2149 @property
2150 def derate(self):
2151 """Get the derate value, if any."""
2152 if self._derateWidget is None:
2153 return None
2154 if self._derateType==acft.DERATE_BOEING:
2155 derate = self._derateEntry.get_text()
2156 return derate if derate else None
2157 elif self._derateType==acft.DERATE_EPR:
2158 derate = self._derateWidget.get_text()
2159 return derate if derate else None
2160 elif self._derateType==acft.DERATE_TUPOLEV:
2161 return acft.DERATE_TUPOLEV_NOMINAL \
2162 if self._derateButtons[0].get_active() \
2163 else acft.DERATE_TUPOLEV_TAKEOFF
2164 elif self._derateType==acft.DERATE_B462:
2165 return self._derateWidget.get_active()
2166 else:
2167 return None
2168
2169 @property
2170 def antiIceOn(self):
2171 """Get whether the anti-ice system has been turned on."""
2172 return self._antiIceOn.get_active()
2173
2174 @antiIceOn.setter
2175 def antiIceOn(self, value):
2176 """Set the anti-ice indicator."""
2177 self._antiIceOn.set_active(value)
2178
2179 @property
2180 def rtoIndicated(self):
2181 """Get whether the pilot has indicated if there was an RTO."""
2182 return self._rto.get_active()
2183
2184 def activate(self):
2185 """Activate the page."""
2186 self._runway.set_text("")
2187 self._runway.set_sensitive(True)
2188 self._sid.set_text("")
2189 self._sid.set_sensitive(True)
2190 self._v1.set_int(None)
2191 self._v1.set_sensitive(True)
2192 self._vr.set_int(None)
2193 self._vr.set_sensitive(True)
2194 self._v2.set_int(None)
2195 self._v2.set_sensitive(True)
2196
2197 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
2198 speedUnit = xstr("label" + i18nSpeedUnit)
2199 self._v1Unit.set_text(speedUnit)
2200 self._vrUnit.set_text(speedUnit)
2201 self._v2Unit.set_text(speedUnit)
2202
2203 self._v1.set_tooltip_markup(xstr("takeoff_v1_tooltip" + i18nSpeedUnit))
2204 self._vr.set_tooltip_markup(xstr("takeoff_vr_tooltip" + i18nSpeedUnit))
2205 self._v2.set_tooltip_markup(xstr("takeoff_v2_tooltip" + i18nSpeedUnit))
2206
2207 self._derateType = self._wizard.gui.flight.aircraft.derateType
2208
2209 self._setupDerateWidget()
2210
2211 self._rto.set_active(False)
2212 self._rto.set_sensitive(False)
2213
2214 self._button.set_sensitive(False)
2215 self._forwardAllowed = False
2216
2217 def allowForward(self):
2218 """Allow going to the next page."""
2219 self._forwardAllowed = True
2220 self._updateForwardButton()
2221
2222 def reset(self):
2223 """Reset the page if the wizard is reset."""
2224 super(TakeoffPage, self).reset()
2225 self._v1.reset()
2226 self._vr.reset()
2227 self._v2.reset()
2228 self._hasDerate = False
2229 self._antiIceOn.set_active(False)
2230
2231 def setRTOEnabled(self, enabled):
2232 """Set the RTO checkbox enabled or disabled."""
2233 if not enabled:
2234 self._rto.set_active(False)
2235 self._rto.set_sensitive(enabled)
2236
2237 def _updateForwardButton(self):
2238 """Update the sensitivity of the forward button based on some conditions."""
2239 sensitive = self._forwardAllowed and \
2240 self._runway.get_text()!="" and \
2241 self._sid.get_text()!="" and \
2242 self.v1 is not None and \
2243 self.vr is not None and \
2244 self.v2 is not None and \
2245 self.v1 <= self.vr and \
2246 self.vr <= self.v2 and \
2247 (self._derateType==acft.DERATE_NONE or
2248 self.derate is not None)
2249 self._button.set_sensitive(sensitive)
2250
2251 def _valueChanged(self, widget, arg = None):
2252 """Called when the value of some widget has changed."""
2253 self._updateForwardButton()
2254
2255 def _upperChanged(self, entry, arg = None):
2256 """Called when the value of some entry widget has changed and the value
2257 should be converted to uppercase."""
2258 entry.set_text(entry.get_text().upper())
2259 self._valueChanged(entry, arg)
2260
2261 def _derateChanged(self, entry):
2262 """Called when the value of the derate is changed."""
2263 self._updateForwardButton()
2264
2265 def _rtoToggled(self, button):
2266 """Called when the RTO check button is toggled."""
2267 self._wizard.rtoToggled(button.get_active())
2268
2269 def _backClicked(self, button):
2270 """Called when the Back button is pressed."""
2271 self.goBack()
2272
2273 def _forwardClicked(self, button):
2274 """Called when the forward button is clicked."""
2275 aircraft = self._wizard.gui.flight.aircraft
2276 aircraft.updateV1R2()
2277 if self.derate is not None:
2278 aircraft.updateDerate()
2279 aircraft.updateTakeoffAntiIce()
2280 self._wizard.nextPage()
2281
2282 def _setupDerateWidget(self):
2283 """Setup the derate widget."""
2284 if self._derateWidget is not None:
2285 self._derate.remove(self._derateWidget)
2286
2287 if self._derateType==acft.DERATE_BOEING:
2288 self._derateLabel.set_text(xstr("takeoff_derate_boeing"))
2289 self._derateLabel.set_use_underline(True)
2290 self._derateLabel.set_sensitive(True)
2291
2292 self._derateEntry = gtk.Entry()
2293 self._derateEntry.set_width_chars(7)
2294 self._derateEntry.set_tooltip_text(xstr("takeoff_derate_boeing_tooltip"))
2295 self._derateEntry.set_alignment(1.0)
2296 self._derateEntry.connect("changed", self._derateChanged)
2297 self._derateLabel.set_mnemonic_widget(self._derateEntry)
2298
2299 self._derateUnit = gtk.Label("%")
2300 self._derateUnit.set_alignment(0.0, 0.5)
2301
2302 self._derateWidget = gtk.Table(3, 1)
2303 self._derateWidget.set_row_spacings(4)
2304 self._derateWidget.set_col_spacings(16)
2305 self._derateWidget.set_homogeneous(False)
2306
2307 self._derateWidget.attach(self._derateEntry, 0, 2, 0, 1)
2308 self._derateWidget.attach(self._derateUnit, 2, 3, 0, 1)
2309
2310 self._derate.add(self._derateWidget)
2311 elif self._derateType==acft.DERATE_EPR:
2312 self._derateLabel.set_text("_EPR:")
2313 self._derateLabel.set_use_underline(True)
2314 self._derateLabel.set_sensitive(True)
2315
2316 self._derateWidget = gtk.Entry()
2317 self._derateWidget.set_width_chars(7)
2318 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_epr_tooltip"))
2319 self._derateWidget.set_alignment(1.0)
2320 self._derateWidget.connect("changed", self._derateChanged)
2321 self._derateLabel.set_mnemonic_widget(self._derateWidget)
2322
2323 self._derate.add(self._derateWidget)
2324 elif self._derateType==acft.DERATE_TUPOLEV:
2325 self._derateLabel.set_text(xstr("takeoff_derate_tupolev"))
2326 self._derateLabel.set_use_underline(True)
2327 self._derateLabel.set_sensitive(True)
2328
2329 if pygobject:
2330 nominal = gtk.RadioButton.\
2331 new_with_label_from_widget(None,
2332 xstr("takeoff_derate_tupolev_nominal"))
2333 else:
2334 nominal = gtk.RadioButton(None,
2335 xstr("takeoff_derate_tupolev_nominal"))
2336 nominal.set_use_underline(True)
2337 nominal.set_tooltip_text(xstr("takeoff_derate_tupolev_nominal_tooltip"))
2338 nominal.connect("toggled", self._derateChanged)
2339
2340 if pygobject:
2341 takeoff = gtk.RadioButton.\
2342 new_with_label_from_widget(nominal,
2343 xstr("takeoff_derate_tupolev_takeoff"))
2344 else:
2345 takeoff = gtk.RadioButton(nominal,
2346 xstr("takeoff_derate_tupolev_takeoff"))
2347
2348 takeoff.set_use_underline(True)
2349 takeoff.set_tooltip_text(xstr("takeoff_derate_tupolev_takeoff_tooltip"))
2350 takeoff.connect("toggled", self._derateChanged)
2351
2352 self._derateButtons = [nominal, takeoff]
2353
2354 self._derateWidget = gtk.HBox()
2355 self._derateWidget.pack_start(nominal, False, False, 4)
2356 self._derateWidget.pack_start(takeoff, False, False, 4)
2357
2358 self._derate.add(self._derateWidget)
2359 elif self._derateType==acft.DERATE_B462:
2360 self._derateLabel.set_text("")
2361
2362 self._derateWidget = gtk.CheckButton(xstr("takeoff_derate_b462"))
2363 self._derateWidget.set_tooltip_text(xstr("takeoff_derate_b462_tooltip"))
2364 self._derateWidget.set_use_underline(True)
2365 self._derate.add(self._derateWidget)
2366 else:
2367 self._derateWidget = None
2368 self._derateLabel.set_text("")
2369 self._derateLabel.set_sensitive(False)
2370
2371#-----------------------------------------------------------------------------
2372
2373class CruisePage(Page):
2374 """The page containing the flight level that might change during flight."""
2375 def __init__(self, wizard):
2376 """Construct the page."""
2377 super(CruisePage, self).__init__(wizard, xstr("cruise_title"),
2378 xstr("cruise_help"))
2379
2380 self._loggable = False
2381 self._loggedCruiseLevel = 240
2382 self._activated = False
2383
2384 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0,
2385 xscale = 0.0, yscale = 1.0)
2386
2387 mainBox = gtk.VBox()
2388 alignment.add(mainBox)
2389 self.setMainWidget(alignment)
2390
2391 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2392 xscale = 0.0, yscale = 0.0)
2393 mainBox.pack_start(alignment, False, False, 16)
2394
2395 levelBox = gtk.HBox()
2396
2397 label = gtk.Label(xstr("route_level"))
2398 label.set_use_underline(True)
2399 levelBox.pack_start(label, True, True, 0)
2400
2401 self._cruiseLevel = gtk.SpinButton()
2402 self._cruiseLevel.set_increments(step = 10, page = 100)
2403 self._cruiseLevel.set_range(min = 50, max = 500)
2404 self._cruiseLevel.set_tooltip_text(xstr("cruise_route_level_tooltip"))
2405 self._cruiseLevel.set_numeric(True)
2406 self._cruiseLevel.connect("value-changed", self._cruiseLevelChanged)
2407 label.set_mnemonic_widget(self._cruiseLevel)
2408
2409 levelBox.pack_start(self._cruiseLevel, False, False, 8)
2410
2411 self._updateButton = gtk.Button(xstr("cruise_route_level_update"));
2412 self._updateButton.set_use_underline(True)
2413 self._updateButton.set_tooltip_text(xstr("cruise_route_level_update_tooltip"))
2414 self._updateButton.connect("clicked", self._updateButtonClicked)
2415
2416 levelBox.pack_start(self._updateButton, False, False, 16)
2417
2418 mainBox.pack_start(levelBox, False, False, 0)
2419
2420 alignment = gtk.Alignment(xalign = 0.0, yalign = 0.0,
2421 xscale = 0.0, yscale = 1.0)
2422 mainBox.pack_start(alignment, True, True, 0)
2423
2424 self.addCancelFlightButton()
2425
2426 self._backButton = self.addPreviousButton(clicked = self._backClicked)
2427 self._button = self.addNextButton(clicked = self._forwardClicked)
2428
2429 @property
2430 def activated(self):
2431 """Determine if the page is already activated or not."""
2432 return self._activated
2433
2434 @property
2435 def cruiseLevel(self):
2436 """Get the cruise level."""
2437 return self._loggedCruiseLevel
2438
2439 @property
2440 def loggableCruiseLevel(self):
2441 """Get the cruise level which should be logged."""
2442 return self._cruiseLevel.get_value_as_int()
2443
2444 def setLoggable(self, loggable):
2445 """Set whether the cruise altitude can be logged."""
2446 self._loggable = loggable
2447 self._updateButtons()
2448
2449 def activate(self):
2450 """Setup the route from the booked flight."""
2451 self._loggedCruiseLevel = self._wizard.filedCruiseLevel
2452 self._cruiseLevel.set_value(self._loggedCruiseLevel)
2453 self._activated = True
2454
2455 def reset(self):
2456 """Reset the page."""
2457 self._loggable = False
2458 self._activated = False
2459 super(CruisePage, self).reset()
2460
2461 def _updateButtons(self):
2462 """Update the sensitivity of the buttons."""
2463 self._updateButton.set_sensitive(self._loggable and
2464 self.loggableCruiseLevel!=
2465 self._loggedCruiseLevel)
2466
2467 def _cruiseLevelChanged(self, spinButton):
2468 """Called when the cruise level has changed."""
2469 self._updateButtons()
2470
2471 def _updateButtonClicked(self, button):
2472 """Called when the update button is clicked."""
2473 if self._wizard.cruiseLevelChanged():
2474 self._loggedCruiseLevel = self.loggableCruiseLevel
2475 self._updateButtons()
2476
2477 def _backClicked(self, button):
2478 """Called when the Back button is pressed."""
2479 self.goBack()
2480
2481 def _forwardClicked(self, button):
2482 """Called when the Forward button is clicked."""
2483 self._wizard.nextPage()
2484
2485#-----------------------------------------------------------------------------
2486
2487class LandingPage(Page):
2488 """Page for entering landing data."""
2489 def __init__(self, wizard):
2490 """Construct the landing page."""
2491 super(LandingPage, self).__init__(wizard, xstr("landing_title"),
2492 xstr("landing_help"),
2493 completedHelp = xstr("landing_chelp"))
2494
2495 self._flightEnded = False
2496
2497 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2498 xscale = 0.0, yscale = 0.0)
2499
2500 table = gtk.Table(6, 5)
2501 table.set_row_spacings(4)
2502 table.set_col_spacings(16)
2503 table.set_homogeneous(False)
2504 alignment.add(table)
2505 self.setMainWidget(alignment)
2506
2507 self._starButton = gtk.CheckButton()
2508 self._starButton.connect("clicked", self._starButtonClicked)
2509 table.attach(self._starButton, 0, 1, 0, 1)
2510
2511 label = gtk.Label(xstr("landing_star"))
2512 label.set_use_underline(True)
2513 label.set_alignment(0.0, 0.5)
2514 table.attach(label, 1, 2, 0, 1)
2515
2516 self._star = gtk.Entry()
2517 self._star.set_width_chars(10)
2518 self._star.set_tooltip_text(xstr("landing_star_tooltip"))
2519 self._star.connect("changed", self._upperChanged)
2520 self._star.set_sensitive(False)
2521 table.attach(self._star, 2, 4, 0, 1)
2522 label.set_mnemonic_widget(self._starButton)
2523
2524 self._transitionButton = gtk.CheckButton()
2525 self._transitionButton.connect("clicked", self._transitionButtonClicked)
2526 table.attach(self._transitionButton, 0, 1, 1, 2)
2527
2528 label = gtk.Label(xstr("landing_transition"))
2529 label.set_use_underline(True)
2530 label.set_alignment(0.0, 0.5)
2531 table.attach(label, 1, 2, 1, 2)
2532
2533 self._transition = gtk.Entry()
2534 self._transition.set_width_chars(10)
2535 self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
2536 self._transition.connect("changed", self._upperChanged)
2537 self._transition.set_sensitive(False)
2538 table.attach(self._transition, 2, 4, 1, 2)
2539 label.set_mnemonic_widget(self._transitionButton)
2540
2541 label = gtk.Label(xstr("landing_runway"))
2542 label.set_use_underline(True)
2543 label.set_alignment(0.0, 0.5)
2544 table.attach(label, 1, 2, 2, 3)
2545
2546 self._runway = gtk.Entry()
2547 self._runway.set_width_chars(10)
2548 self._runway.set_tooltip_text(xstr("landing_runway_tooltip"))
2549 self._runway.connect("changed", self._upperChanged)
2550 table.attach(self._runway, 2, 4, 2, 3)
2551 label.set_mnemonic_widget(self._runway)
2552
2553 label = gtk.Label(xstr("landing_approach"))
2554 label.set_use_underline(True)
2555 label.set_alignment(0.0, 0.5)
2556 table.attach(label, 1, 2, 3, 4)
2557
2558 self._approachType = gtk.Entry()
2559 self._approachType.set_width_chars(10)
2560 self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
2561 self._approachType.connect("changed", self._upperChanged)
2562 table.attach(self._approachType, 2, 4, 3, 4)
2563 label.set_mnemonic_widget(self._approachType)
2564
2565 label = gtk.Label(xstr("landing_vref"))
2566 label.set_use_markup(True)
2567 label.set_use_underline(True)
2568 label.set_alignment(0.0, 0.5)
2569 table.attach(label, 1, 2, 4, 5)
2570
2571 self._vref = IntegerEntry()
2572 self._vref.set_width_chars(5)
2573 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip_knots"))
2574 self._vref.connect("integer-changed", self._vrefChanged)
2575 table.attach(self._vref, 3, 4, 4, 5)
2576 label.set_mnemonic_widget(self._vref)
2577
2578 self._vrefUnit = gtk.Label(xstr("label_knots"))
2579 table.attach(self._vrefUnit, 4, 5, 4, 5)
2580
2581 self._antiIceOn = gtk.CheckButton(xstr("landing_antiice"))
2582 self._antiIceOn.set_use_underline(True)
2583 self._antiIceOn.set_tooltip_text(xstr("landing_antiice_tooltip"))
2584 table.attach(self._antiIceOn, 3, 5, 5, 6)
2585
2586 self.addCancelFlightButton()
2587
2588 self.addPreviousButton(clicked = self._backClicked)
2589
2590 self._button = self.addNextButton(clicked = self._forwardClicked)
2591
2592 # These are needed for correct size calculations
2593 self._starButton.set_active(True)
2594 self._transitionButton.set_active(True)
2595
2596 @property
2597 def star(self):
2598 """Get the STAR or None if none entered."""
2599 return self._star.get_text() if self._starButton.get_active() else None
2600
2601 @property
2602 def transition(self):
2603 """Get the transition or None if none entered."""
2604 return self._transition.get_text() \
2605 if self._transitionButton.get_active() else None
2606
2607 @property
2608 def approachType(self):
2609 """Get the approach type."""
2610 return self._approachType.get_text()
2611
2612 @property
2613 def runway(self):
2614 """Get the runway."""
2615 return self._runway.get_text()
2616
2617 @property
2618 def vref(self):
2619 """Return the landing reference speed."""
2620 return self._vref.get_int()
2621
2622 @property
2623 def antiIceOn(self):
2624 """Get whether the anti-ice system has been turned on."""
2625 return self._antiIceOn.get_active()
2626
2627 @antiIceOn.setter
2628 def antiIceOn(self, value):
2629 """Set the anti-ice indicator."""
2630 self._antiIceOn.set_active(value)
2631
2632 def reset(self):
2633 """Reset the page if the wizard is reset."""
2634 super(LandingPage, self).reset()
2635 self._vref.reset()
2636 self._antiIceOn.set_active(False)
2637 self._flightEnded = False
2638
2639 def activate(self):
2640 """Called when the page is activated."""
2641 self._starButton.set_sensitive(True)
2642 self._starButton.set_active(False)
2643 self._star.set_text("")
2644
2645 self._transitionButton.set_sensitive(True)
2646 self._transitionButton.set_active(False)
2647 self._transition.set_text("")
2648
2649 self._runway.set_text("")
2650 self._runway.set_sensitive(True)
2651
2652 self._approachType.set_text("")
2653 self._approachType.set_sensitive(True)
2654
2655 self._vref.set_int(None)
2656 self._vref.set_sensitive(True)
2657
2658 i18nSpeedUnit = self._wizard.gui.flight.getI18NSpeedUnit()
2659 speedUnit = xstr("label" + i18nSpeedUnit)
2660 self._vrefUnit.set_text(speedUnit)
2661
2662 self._vref.set_tooltip_markup(xstr("landing_vref_tooltip" +
2663 i18nSpeedUnit))
2664
2665 self._updateForwardButton()
2666
2667 def flightEnded(self):
2668 """Called when the flight has ended."""
2669 super(LandingPage, self).flightEnded()
2670 self._flightEnded = True
2671 self._updateForwardButton()
2672
2673 def _starButtonClicked(self, button):
2674 """Called when the STAR button is clicked."""
2675 active = button.get_active()
2676 self._star.set_sensitive(active)
2677 if active:
2678 self._star.grab_focus()
2679 self._updateForwardButton()
2680
2681 def _transitionButtonClicked(self, button):
2682 """Called when the Transition button is clicked."""
2683 active = button.get_active()
2684 self._transition.set_sensitive(active)
2685 if active:
2686 self._transition.grab_focus()
2687 self._updateForwardButton()
2688
2689 def _updateForwardButton(self):
2690 """Update the sensitivity of the forward button."""
2691 sensitive = self._flightEnded and \
2692 (self._starButton.get_active() or \
2693 self._transitionButton.get_active()) and \
2694 (self._star.get_text()!="" or
2695 not self._starButton.get_active()) and \
2696 (self._transition.get_text()!="" or
2697 not self._transitionButton.get_active()) and \
2698 self._runway.get_text()!="" and \
2699 self._approachType.get_text()!="" and \
2700 self.vref is not None
2701 self._button.set_sensitive(sensitive)
2702
2703 def _upperChanged(self, entry):
2704 """Called for entry widgets that must be converted to uppercase."""
2705 entry.set_text(entry.get_text().upper())
2706 self._updateForwardButton()
2707
2708 def _vrefChanged(self, widget, value):
2709 """Called when the Vref has changed."""
2710 self._updateForwardButton()
2711
2712 def _backClicked(self, button):
2713 """Called when the Back button is pressed."""
2714 self.goBack()
2715
2716 def _forwardClicked(self, button):
2717 """Called when the forward button is clicked."""
2718 aircraft = self._wizard.gui.flight.aircraft
2719 aircraft.updateVRef()
2720 aircraft.updateLandingAntiIce()
2721 if self._wizard.gui.config.onlineGateSystem and \
2722 self._wizard.loggedIn and not self._completed and \
2723 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
2724 not self._wizard.entranceExam:
2725 self._wizard.getFleet(callback = self._fleetRetrieved,
2726 force = True)
2727 else:
2728 self._wizard.nextPage()
2729
2730 def _fleetRetrieved(self, fleet):
2731 """Callback for the fleet retrieval."""
2732 self._wizard.nextPage()
2733
2734#-----------------------------------------------------------------------------
2735
2736class FinishPage(Page):
2737 """Flight finish page."""
2738 _flightTypes = [ ("flighttype_scheduled", const.FLIGHTTYPE_SCHEDULED),
2739 ("flighttype_ot", const.FLIGHTTYPE_OLDTIMER),
2740 ("flighttype_vip", const.FLIGHTTYPE_VIP),
2741 ("flighttype_charter", const.FLIGHTTYPE_CHARTER) ]
2742
2743 def __init__(self, wizard):
2744 """Construct the finish page."""
2745 super(FinishPage, self).__init__(wizard, xstr("finish_title"),
2746 xstr("finish_help"))
2747
2748 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
2749 xscale = 0.0, yscale = 0.0)
2750
2751 table = gtk.Table(8, 2)
2752 table.set_row_spacings(4)
2753 table.set_col_spacings(16)
2754 table.set_homogeneous(False)
2755 alignment.add(table)
2756 self.setMainWidget(alignment)
2757
2758 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2759 label = gtk.Label(xstr("finish_rating"))
2760 labelAlignment.add(label)
2761 table.attach(labelAlignment, 0, 1, 0, 1)
2762
2763 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2764 self._flightRating = gtk.Label()
2765 self._flightRating.set_width_chars(8)
2766 self._flightRating.set_alignment(0.0, 0.5)
2767 self._flightRating.set_use_markup(True)
2768 labelAlignment.add(self._flightRating)
2769 table.attach(labelAlignment, 1, 2, 0, 1)
2770
2771 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2772 label = gtk.Label(xstr("finish_flight_time"))
2773 labelAlignment.add(label)
2774 table.attach(labelAlignment, 0, 1, 1, 2)
2775
2776 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2777 self._flightTime = gtk.Label()
2778 self._flightTime.set_width_chars(10)
2779 self._flightTime.set_alignment(0.0, 0.5)
2780 self._flightTime.set_use_markup(True)
2781 labelAlignment.add(self._flightTime)
2782 table.attach(labelAlignment, 1, 2, 1, 2)
2783
2784 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2785 label = gtk.Label(xstr("finish_block_time"))
2786 labelAlignment.add(label)
2787 table.attach(labelAlignment, 0, 1, 2, 3)
2788
2789 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2790 self._blockTime = gtk.Label()
2791 self._blockTime.set_width_chars(10)
2792 self._blockTime.set_alignment(0.0, 0.5)
2793 self._blockTime.set_use_markup(True)
2794 labelAlignment.add(self._blockTime)
2795 table.attach(labelAlignment, 1, 2, 2, 3)
2796
2797 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2798 label = gtk.Label(xstr("finish_distance"))
2799 labelAlignment.add(label)
2800 table.attach(labelAlignment, 0, 1, 3, 4)
2801
2802 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2803 self._distanceFlown = gtk.Label()
2804 self._distanceFlown.set_width_chars(10)
2805 self._distanceFlown.set_alignment(0.0, 0.5)
2806 self._distanceFlown.set_use_markup(True)
2807 labelAlignment.add(self._distanceFlown)
2808 table.attach(labelAlignment, 1, 2, 3, 4)
2809
2810 labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
2811 label = gtk.Label(xstr("finish_fuel"))
2812 labelAlignment.add(label)
2813 table.attach(labelAlignment, 0, 1, 4, 5)
2814
2815 labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2816 self._fuelUsed = gtk.Label()
2817 self._fuelUsed.set_width_chars(10)
2818 self._fuelUsed.set_alignment(0.0, 0.5)
2819 self._fuelUsed.set_use_markup(True)
2820 labelAlignment.add(self._fuelUsed)
2821 table.attach(labelAlignment, 1, 2, 4, 5)
2822
2823 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
2824 yalign = 0.5, yscale = 0.0)
2825 label = gtk.Label(xstr("finish_type"))
2826 label.set_use_underline(True)
2827 labelAlignment.add(label)
2828 table.attach(labelAlignment, 0, 1, 5, 6)
2829
2830 flightTypeModel = gtk.ListStore(str, int)
2831 for (name, type) in FinishPage._flightTypes:
2832 flightTypeModel.append([xstr(name), type])
2833
2834 self._flightType = gtk.ComboBox(model = flightTypeModel)
2835 renderer = gtk.CellRendererText()
2836 self._flightType.pack_start(renderer, True)
2837 self._flightType.add_attribute(renderer, "text", 0)
2838 self._flightType.set_tooltip_text(xstr("finish_type_tooltip"))
2839 self._flightType.set_active(0)
2840 self._flightType.connect("changed", self._flightTypeChanged)
2841 flightTypeAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2842 flightTypeAlignment.add(self._flightType)
2843 table.attach(flightTypeAlignment, 1, 2, 5, 6)
2844 label.set_mnemonic_widget(self._flightType)
2845
2846 self._onlineFlight = gtk.CheckButton(xstr("finish_online"))
2847 self._onlineFlight.set_use_underline(True)
2848 self._onlineFlight.set_tooltip_text(xstr("finish_online_tooltip"))
2849 onlineFlightAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
2850 onlineFlightAlignment.add(self._onlineFlight)
2851 table.attach(onlineFlightAlignment, 1, 2, 6, 7)
2852
2853 labelAlignment = gtk.Alignment(xalign = 1.0, xscale = 0.0,
2854 yalign = 0.5, yscale = 0.0)
2855 self._gateLabel = gtk.Label(xstr("finish_gate"))
2856 self._gateLabel.set_use_underline(True)
2857 labelAlignment.add(self._gateLabel)
2858 table.attach(labelAlignment, 0, 1, 7, 8)
2859
2860 self._gatesModel = gtk.ListStore(str)
2861
2862 self._gate = gtk.ComboBox(model = self._gatesModel)
2863 renderer = gtk.CellRendererText()
2864 self._gate.pack_start(renderer, True)
2865 self._gate.add_attribute(renderer, "text", 0)
2866 self._gate.set_tooltip_text(xstr("finish_gate_tooltip"))
2867 self._gate.connect("changed", self._gateChanged)
2868 gateAlignment = gtk.Alignment(xalign=0.0, xscale=1.0)
2869 gateAlignment.add(self._gate)
2870 table.attach(gateAlignment, 1, 2, 7, 8)
2871 self._gateLabel.set_mnemonic_widget(self._gate)
2872
2873 self.addButton(xstr("finish_newFlight"),
2874 sensitive = True,
2875 clicked = self._newFlightClicked,
2876 tooltip = xstr("finish_newFlight_tooltip"),
2877 padding = 16)
2878
2879 self.addPreviousButton(clicked = self._backClicked)
2880
2881 self._saveButton = self.addButton(xstr("finish_save"),
2882 sensitive = False,
2883 clicked = self._saveClicked,
2884 tooltip = xstr("finish_save_tooltip"))
2885 self._savePIREPDialog = None
2886 self._lastSavePath = None
2887
2888 self._pirepSaved = False
2889 self._pirepSent = False
2890
2891 self._sendButton = self.addButton(xstr("sendPIREP"), default = True,
2892 sensitive = False,
2893 clicked = self._sendClicked,
2894 tooltip = xstr("sendPIREP_tooltip"))
2895
2896 @property
2897 def flightType(self):
2898 """Get the flight type."""
2899 index = self._flightType.get_active()
2900 return None if index<0 else self._flightType.get_model()[index][1]
2901
2902 @property
2903 def online(self):
2904 """Get whether the flight was an online flight or not."""
2905 return self._onlineFlight.get_active()
2906
2907 def activate(self):
2908 """Activate the page."""
2909 self._pirepSaved = False
2910 self._pirepSent = False
2911
2912 flight = self._wizard.gui._flight
2913 rating = flight.logger.getRating()
2914 if rating<0:
2915 self._flightRating.set_markup('<b><span foreground="red">NO GO</span></b>')
2916 else:
2917 self._flightRating.set_markup("<b>%.1f %%</b>" % (rating,))
2918
2919 flightLength = flight.flightTimeEnd - flight.flightTimeStart
2920 self._flightTime.set_markup("<b>%s</b>" % \
2921 (util.getTimeIntervalString(flightLength),))
2922
2923 blockLength = flight.blockTimeEnd - flight.blockTimeStart
2924 self._blockTime.set_markup("<b>%s</b>" % \
2925 (util.getTimeIntervalString(blockLength),))
2926
2927 self._distanceFlown.set_markup("<b>%.2f NM</b>" % \
2928 (flight.flownDistance,))
2929
2930 self._fuelUsed.set_markup("<b>%.0f kg</b>" % \
2931 (flight.startFuel - flight.endFuel,))
2932
2933 self._flightType.set_active(-1)
2934 self._onlineFlight.set_active(self._wizard.loggedIn)
2935
2936 self._gatesModel.clear()
2937 if self._wizard.gui.config.onlineGateSystem and \
2938 self._wizard.loggedIn and \
2939 self._wizard.bookedFlight.arrivalICAO=="LHBP" and \
2940 not self._wizard.entranceExam:
2941 occupiedGates = self._wizard._fleet.getOccupiedGateNumbers()
2942 for gateNumber in const.lhbpGateNumbers:
2943 if gateNumber not in occupiedGates:
2944 self._gatesModel.append([gateNumber])
2945 self._gateLabel.set_sensitive(True)
2946 self._gate.set_sensitive(True)
2947 self._gate.set_active(-1)
2948 else:
2949 self._gateLabel.set_sensitive(False)
2950 self._gate.set_sensitive(False)
2951
2952 def _backClicked(self, button):
2953 """Called when the Back button is pressed."""
2954 self.goBack()
2955
2956 def _updateButtons(self):
2957 """Update the sensitivity state of the buttons."""
2958 sensitive = self._flightType.get_active()>=0 and \
2959 (self._gatesModel.get_iter_first() is None or
2960 self._gate.get_active()>=0)
2961
2962 wasSensitive = self._saveButton.get_sensitive()
2963
2964 gui = self._wizard.gui
2965 config = gui.config
2966 if config.pirepAutoSave and sensitive and not wasSensitive:
2967 self._lastSavePath = os.path.join(config.pirepDirectory,
2968 self._getDefaultPIREPName())
2969 self._lastSavePath = text2unicode(self._lastSavePath)
2970 self._savePIREP(automatic = True)
2971
2972 self._saveButton.set_sensitive(sensitive)
2973 self._sendButton.set_sensitive(sensitive and
2974 self._wizard.bookedFlight.id is not None)
2975
2976 def _flightTypeChanged(self, comboBox):
2977 """Called when the flight type has changed."""
2978 self._updateButtons()
2979
2980 def _gateChanged(self, comboBox):
2981 """Called when the arrival gate has changed."""
2982 self._updateButtons()
2983
2984 def _newFlightClicked(self, button):
2985 """Called when the new flight button is clicked."""
2986 gui = self._wizard.gui
2987 if not self._pirepSent and not self._pirepSaved:
2988 dialog = gtk.MessageDialog(parent = gui.mainWindow,
2989 type = MESSAGETYPE_QUESTION,
2990 message_format = xstr("finish_newFlight_question"))
2991
2992 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
2993 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
2994
2995 dialog.set_title(WINDOW_TITLE_BASE)
2996 result = dialog.run()
2997 dialog.hide()
2998 if result!=RESPONSETYPE_YES:
2999 return
3000
3001 gui.reset()
3002
3003 def _getDefaultPIREPName(self):
3004 """Get the default name of the PIREP."""
3005 gui = self._wizard.gui
3006
3007 bookedFlight = gui.bookedFlight
3008 tm = time.gmtime()
3009
3010 pilotID = self._wizard.pilotID
3011 if pilotID: pilotID += " "
3012 return "%s%s %02d%02d %s-%s.pirep" % \
3013 (pilotID, str(bookedFlight.departureTime.date()),
3014 tm.tm_hour, tm.tm_min,
3015 bookedFlight.departureICAO, bookedFlight.arrivalICAO)
3016
3017
3018 def _saveClicked(self, button):
3019 """Called when the Save PIREP button is clicked."""
3020 gui = self._wizard.gui
3021
3022 fileName = self._getDefaultPIREPName()
3023
3024 dialog = self._getSaveDialog()
3025
3026 if self._lastSavePath is None:
3027 pirepDirectory = gui.config.pirepDirectory
3028 if pirepDirectory is not None:
3029 dialog.set_current_folder(pirepDirectory)
3030 else:
3031 dialog.set_current_folder(os.path.dirname(self._lastSavePath))
3032
3033 dialog.set_current_name(fileName)
3034 result = dialog.run()
3035 dialog.hide()
3036
3037 if result==RESPONSETYPE_OK:
3038 self._lastSavePath = text2unicode(dialog.get_filename())
3039 self._savePIREP()
3040
3041 def _savePIREP(self, automatic = False):
3042 """Perform the saving of the PIREP."""
3043
3044 gui = self._wizard.gui
3045
3046 if automatic:
3047 gui.beginBusy(xstr("finish_autosave_busy"))
3048
3049 pirep = PIREP(gui.flight)
3050 error = pirep.save(self._lastSavePath)
3051
3052 if automatic:
3053 gui.endBusy()
3054
3055 if error:
3056 type = MESSAGETYPE_ERROR
3057 message = xstr("finish_save_failed")
3058 secondary = xstr("finish_save_failed_sec") % (text2unicode(error),)
3059 else:
3060 type = MESSAGETYPE_INFO
3061 message = xstr("finish_save_done")
3062 if automatic:
3063 secondary = xstr("finish_save_done_sec") % (self._lastSavePath,)
3064 else:
3065 secondary = None
3066 self._pirepSaved = True
3067
3068 dialog = gtk.MessageDialog(parent = gui.mainWindow,
3069 type = type, message_format = message)
3070 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3071 dialog.set_title(WINDOW_TITLE_BASE)
3072 if secondary is not None:
3073 dialog.format_secondary_markup(secondary)
3074
3075 dialog.run()
3076 dialog.hide()
3077
3078 def _getSaveDialog(self):
3079 """Get the PIREP saving dialog.
3080
3081 If it does not exist yet, create it."""
3082 if self._savePIREPDialog is None:
3083 gui = self._wizard.gui
3084 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
3085 xstr("finish_save_title"),
3086 action = FILE_CHOOSER_ACTION_SAVE,
3087 buttons = (gtk.STOCK_CANCEL,
3088 RESPONSETYPE_CANCEL,
3089 gtk.STOCK_OK, RESPONSETYPE_OK),
3090 parent = gui.mainWindow)
3091 dialog.set_modal(True)
3092 dialog.set_do_overwrite_confirmation(True)
3093
3094 filter = gtk.FileFilter()
3095 filter.set_name(xstr("file_filter_pireps"))
3096 filter.add_pattern("*.pirep")
3097 dialog.add_filter(filter)
3098
3099 filter = gtk.FileFilter()
3100 filter.set_name(xstr("file_filter_all"))
3101 filter.add_pattern("*.*")
3102 dialog.add_filter(filter)
3103
3104 self._savePIREPDialog = dialog
3105
3106 return self._savePIREPDialog
3107
3108
3109 def _sendClicked(self, button):
3110 """Called when the Send button is clicked."""
3111 pirep = PIREP(self._wizard.gui.flight)
3112 self._wizard.gui.sendPIREP(pirep,
3113 callback = self._handlePIREPSent)
3114
3115 def _handlePIREPSent(self, returned, result):
3116 """Callback for the PIREP sending result."""
3117 self._pirepSent = returned and result.success
3118 if self._wizard.gui.config.onlineGateSystem and \
3119 self._wizard.loggedIn and not self._wizard.entranceExam and \
3120 returned and result.success:
3121 bookedFlight = self._wizard.bookedFlight
3122 if bookedFlight.arrivalICAO=="LHBP":
3123 iter = self._gate.get_active_iter()
3124 gateNumber = None if iter is None \
3125 else self._gatesModel.get_value(iter, 0)
3126
3127 status = const.PLANE_PARKING if gateNumber is None \
3128 else const.PLANE_HOME
3129 else:
3130 gateNumber = None
3131 status = const.PLANE_AWAY
3132
3133 self._wizard.updatePlane(self._planeUpdated,
3134 bookedFlight.tailNumber,
3135 status, gateNumber = gateNumber)
3136
3137 def _planeUpdated(self, success):
3138 """Callback for the plane updating."""
3139 pass
3140
3141#-----------------------------------------------------------------------------
3142
3143class Wizard(gtk.VBox):
3144 """The flight wizard."""
3145 def __init__(self, gui):
3146 """Construct the wizard."""
3147 super(Wizard, self).__init__()
3148
3149 self.gui = gui
3150
3151 self._pages = []
3152 self._currentPage = None
3153
3154 self._loginPage = LoginPage(self)
3155 self._pages.append(self._loginPage)
3156 self._pages.append(FlightSelectionPage(self))
3157 self._pages.append(GateSelectionPage(self))
3158 self._pages.append(ConnectPage(self))
3159 self._payloadPage = PayloadPage(self)
3160 self._pages.append(self._payloadPage)
3161 self._payloadIndex = len(self._pages)
3162 self._pages.append(TimePage(self))
3163 self._pages.append(FuelPage(self))
3164 self._routePage = RoutePage(self)
3165 self._pages.append(self._routePage)
3166 self._departureBriefingPage = BriefingPage(self, True)
3167 self._pages.append(self._departureBriefingPage)
3168 self._arrivalBriefingPage = BriefingPage(self, False)
3169 self._pages.append(self._arrivalBriefingPage)
3170 self._arrivalBriefingIndex = len(self._pages)
3171 self._takeoffPage = TakeoffPage(self)
3172 self._pages.append(self._takeoffPage)
3173 self._cruisePage = CruisePage(self)
3174 self._pages.append(self._cruisePage)
3175 self._landingPage = LandingPage(self)
3176 self._pages.append(self._landingPage)
3177 self._finishPage = FinishPage(self)
3178 self._pages.append(self._finishPage)
3179
3180 maxWidth = 0
3181 maxHeight = 0
3182 for page in self._pages:
3183 page.show_all()
3184 pageSizeRequest = page.size_request()
3185 width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
3186 height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
3187 maxWidth = max(maxWidth, width)
3188 maxHeight = max(maxHeight, height)
3189 page.setStyle()
3190 maxWidth += 16
3191 maxHeight += 32
3192 self.set_size_request(maxWidth, maxHeight)
3193
3194 self._initialize()
3195
3196 @property
3197 def pilotID(self):
3198 """Get the pilot ID, if given."""
3199 return self._loginPage.pilotID
3200
3201 @property
3202 def entranceExam(self):
3203 """Get whether an entrance exam is about to be taken."""
3204 return self._loginPage.entranceExam
3205
3206 @property
3207 def loggedIn(self):
3208 """Indicate if there was a successful login."""
3209 return self._loginResult is not None
3210
3211 @property
3212 def loginResult(self):
3213 """Get the login result."""
3214 return self._loginResult
3215
3216 def setCurrentPage(self, index, finalize = False):
3217 """Set the current page to the one with the given index."""
3218 assert index < len(self._pages)
3219
3220 fromPage = self._currentPage
3221 if fromPage is not None:
3222 page = self._pages[fromPage]
3223 if finalize and not page._completed:
3224 page.complete()
3225 self.remove(page)
3226
3227 self._currentPage = index
3228 page = self._pages[index]
3229 self.add(page)
3230 if page._fromPage is None:
3231 page._fromPage = fromPage
3232 page.initialize()
3233 self.show_all()
3234 if fromPage is not None:
3235 self.grabDefault()
3236
3237 @property
3238 def bookedFlight(self):
3239 """Get the booked flight selected."""
3240 return self._bookedFlight
3241
3242 @property
3243 def numCrew(self):
3244 """Get the number of crew members."""
3245 return self._payloadPage.numCrew
3246
3247 @property
3248 def numPassengers(self):
3249 """Get the number of passengers."""
3250 return self._payloadPage.numPassengers
3251
3252 @property
3253 def bagWeight(self):
3254 """Get the baggage weight."""
3255 return self._payloadPage.bagWeight
3256
3257 @property
3258 def cargoWeight(self):
3259 """Get the cargo weight."""
3260 return self._payloadPage.cargoWeight
3261
3262 @property
3263 def mailWeight(self):
3264 """Get the mail weight."""
3265 return self._payloadPage.mailWeight
3266
3267 @property
3268 def zfw(self):
3269 """Get the calculated ZFW value."""
3270 return 0 if self._bookedFlight is None \
3271 else self._payloadPage.calculateZFW()
3272
3273 @property
3274 def filedCruiseLevel(self):
3275 """Get the filed cruise level."""
3276 return self._routePage.filedCruiseLevel
3277
3278 @property
3279 def filedCruiseAltitude(self):
3280 """Get the filed cruise altitude."""
3281 return self._routePage.filedCruiseLevel * 100
3282
3283 @property
3284 def cruiseAltitude(self):
3285 """Get the cruise altitude."""
3286 level = self._cruisePage.cruiseLevel if self._cruisePage.activated \
3287 else self._routePage.filedCruiseLevel
3288 return level * 100
3289
3290 @property
3291 def loggableCruiseAltitude(self):
3292 """Get the cruise altitude that can be logged."""
3293 if self._cruisePage.activated:
3294 return self._cruisePage.loggableCruiseLevel * 100
3295 else:
3296 return 0
3297
3298 @property
3299 def route(self):
3300 """Get the route."""
3301 return self._routePage.route
3302
3303 @property
3304 def departureMETAR(self):
3305 """Get the METAR of the departure airport."""
3306 return self._departureBriefingPage.metar
3307
3308 @property
3309 def arrivalMETAR(self):
3310 """Get the METAR of the arrival airport."""
3311 return self._arrivalBriefingPage.metar
3312
3313 @property
3314 def departureRunway(self):
3315 """Get the departure runway."""
3316 return self._takeoffPage.runway
3317
3318 @property
3319 def sid(self):
3320 """Get the SID."""
3321 return self._takeoffPage.sid
3322
3323 @property
3324 def v1(self):
3325 """Get the V1 speed."""
3326 return self._takeoffPage.v1
3327
3328 @property
3329 def vr(self):
3330 """Get the Vr speed."""
3331 return self._takeoffPage.vr
3332
3333 @property
3334 def v2(self):
3335 """Get the V2 speed."""
3336 return self._takeoffPage.v2
3337
3338 @property
3339 def derate(self):
3340 """Get the derate value."""
3341 return self._takeoffPage.derate
3342
3343 @property
3344 def takeoffAntiIceOn(self):
3345 """Get whether the anti-ice system was on during take-off."""
3346 return self._takeoffPage.antiIceOn
3347
3348 @takeoffAntiIceOn.setter
3349 def takeoffAntiIceOn(self, value):
3350 """Set anti-ice on indicator."""
3351 self._takeoffPage.antiIceOn = value
3352
3353 @property
3354 def rtoIndicated(self):
3355 """Get whether the pilot has indicated that an RTO has occured."""
3356 return self._takeoffPage.rtoIndicated
3357
3358 @property
3359 def arrivalRunway(self):
3360 """Get the arrival runway."""
3361 return self._landingPage.runway
3362
3363 @property
3364 def star(self):
3365 """Get the STAR."""
3366 return self._landingPage.star
3367
3368 @property
3369 def transition(self):
3370 """Get the transition."""
3371 return self._landingPage.transition
3372
3373 @property
3374 def approachType(self):
3375 """Get the approach type."""
3376 return self._landingPage.approachType
3377
3378 @property
3379 def vref(self):
3380 """Get the Vref speed."""
3381 return self._landingPage.vref
3382
3383 @property
3384 def landingAntiIceOn(self):
3385 """Get whether the anti-ice system was on during landing."""
3386 return self._landingPage.antiIceOn
3387
3388 @landingAntiIceOn.setter
3389 def landingAntiIceOn(self, value):
3390 """Set anti-ice on indicator."""
3391 self._landingPage.antiIceOn = value
3392
3393 @property
3394 def flightType(self):
3395 """Get the flight type."""
3396 return self._finishPage.flightType
3397
3398 @property
3399 def online(self):
3400 """Get whether the flight was online or not."""
3401 return self._finishPage.online
3402
3403 def nextPage(self, finalize = True):
3404 """Go to the next page."""
3405 self.jumpPage(1, finalize)
3406
3407 def jumpPage(self, count, finalize = True):
3408 """Go to the page which is 'count' pages after the current one."""
3409 self.setCurrentPage(self._currentPage + count, finalize = finalize)
3410
3411 def grabDefault(self):
3412 """Make the default button of the current page the default."""
3413 self._pages[self._currentPage].grabDefault()
3414
3415 def connected(self, fsType, descriptor):
3416 """Called when the connection could be made to the simulator."""
3417 self.nextPage()
3418
3419 def reset(self, loginResult):
3420 """Resets the wizard to go back to the login page."""
3421 self._initialize(keepLoginResult = loginResult is None,
3422 loginResult = loginResult)
3423
3424 def setStage(self, stage):
3425 """Set the flight stage to the given one."""
3426 if stage!=const.STAGE_END:
3427 self._cruisePage.setLoggable(Flight.canLogCruiseAltitude(stage))
3428
3429 if stage==const.STAGE_TAKEOFF:
3430 self._takeoffPage.allowForward()
3431 elif stage==const.STAGE_LANDING:
3432 if not self._arrivalBriefingPage.metarEdited:
3433 print "Downloading arrival METAR again"
3434 self.gui.webHandler.getMETARs(self._arrivalMETARCallback,
3435 [self._bookedFlight.arrivalICAO])
3436
3437 elif stage==const.STAGE_END:
3438 for page in self._pages:
3439 page.flightEnded()
3440
3441 def _initialize(self, keepLoginResult = False, loginResult = None):
3442 """Initialize the wizard."""
3443 if not keepLoginResult:
3444 self._loginResult = loginResult
3445
3446 self._loginCallback = None
3447
3448 self._fleet = None
3449 self._fleetCallback = None
3450
3451 self._bookedFlight = None
3452 self._departureGate = "-"
3453 self._fuelData = None
3454 self._departureNOTAMs = None
3455 self._departureMETAR = None
3456 self._arrivalNOTAMs = None
3457 self._arrivalMETAR = None
3458
3459 firstPage = 0 if self._loginResult is None else 1
3460 for page in self._pages[firstPage:]:
3461 page.reset()
3462
3463 self.setCurrentPage(firstPage)
3464
3465 def login(self, callback, pilotID, password, entranceExam):
3466 """Called when the login button was clicked."""
3467 self._loginCallback = callback
3468 if pilotID is None:
3469 loginResult = self._loginResult
3470 assert loginResult is not None and loginResult.loggedIn
3471 pilotID = loginResult.pilotID
3472 password = loginResult.password
3473 entranceExam = loginResult.entranceExam
3474 busyMessage = xstr("reload_busy")
3475 else:
3476 self._loginResult = None
3477 busyMessage = xstr("login_busy")
3478
3479 self.gui.beginBusy(busyMessage)
3480
3481 self.gui.webHandler.login(self._loginResultCallback,
3482 pilotID, password,
3483 entranceExam = entranceExam)
3484
3485 def reloadFlights(self, callback):
3486 """Reload the flights from the MAVA server."""
3487 self.login(callback, None, None, None)
3488
3489 def cruiseLevelChanged(self):
3490 """Called when the cruise level is changed."""
3491 return self.gui.cruiseLevelChanged()
3492
3493 def _loginResultCallback(self, returned, result):
3494 """The login result callback, called in the web handler's thread."""
3495 gobject.idle_add(self._handleLoginResult, returned, result)
3496
3497 def _handleLoginResult(self, returned, result):
3498 """Handle the login result."""
3499 self.gui.endBusy()
3500 isReload = self._loginResult is not None
3501 if returned:
3502 if result.loggedIn:
3503 self._loginResult = result
3504 else:
3505 if isReload:
3506 message = xstr("reload_failed")
3507 else:
3508 message = xstr("login_entranceExam_invalid"
3509 if self.entranceExam else
3510 xstr("login_invalid"))
3511 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
3512 type = MESSAGETYPE_ERROR,
3513 message_format = message)
3514 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3515 dialog.set_title(WINDOW_TITLE_BASE)
3516 if isReload:
3517 secondary = xstr("reload_failed_sec")
3518 else:
3519 secondary = xstr("login_entranceExam_invalid_sec"
3520 if self.entranceExam else
3521 xstr("login_invalid_sec"))
3522 dialog.format_secondary_markup(secondary)
3523 dialog.run()
3524 dialog.hide()
3525 else:
3526 message = xstr("reload_failconn") if isReload \
3527 else xstr("login_failconn")
3528 dialog = gtk.MessageDialog(parent = self.gui.mainWindow,
3529 type = MESSAGETYPE_ERROR,
3530 message_format = message)
3531 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
3532 dialog.set_title(WINDOW_TITLE_BASE)
3533 secondary = xstr("reload_failconn_sec") if isReload \
3534 else xstr("login_failconn_sec")
3535 dialog.format_secondary_markup(secondary)
3536
3537 dialog.run()
3538 dialog.hide()
3539
3540 callback = self._loginCallback
3541 self._loginCallback = None
3542 callback(returned, result)
3543
3544 def getFleet(self, callback, force = False):
3545 """Get the fleet via the GUI and call the given callback."""
3546 self._fleetCallback = callback
3547 self.gui.getFleet(callback = self._fleetRetrieved, force = force)
3548
3549 def _fleetRetrieved(self, fleet):
3550 """Callback for the fleet retrieval."""
3551 self._fleet = fleet
3552 if self._fleetCallback is not None:
3553 self._fleetCallback(fleet)
3554 self._fleetCallback = None
3555
3556 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
3557 """Update the given plane's gate information."""
3558 self.gui.updatePlane(tailNumber, status, gateNumber = gateNumber,
3559 callback = callback)
3560
3561 def updateRTO(self):
3562 """Update the RTO state.
3563
3564 The RTO checkbox will be enabled if the flight has an RTO state and the
3565 comments field contains some text."""
3566 flight = self.gui.flight
3567 rtoEnabled = flight is not None and flight.hasRTO and \
3568 self.gui.hasComments
3569 self._takeoffPage.setRTOEnabled(rtoEnabled)
3570
3571 def rtoToggled(self, indicated):
3572 """Called when the RTO indication has changed."""
3573 self.gui.rtoToggled(indicated)
3574
3575 def _connectSimulator(self):
3576 """Connect to the simulator."""
3577 self.gui.connectSimulator(self._bookedFlight.aircraftType)
3578
3579 def _arrivalMETARCallback(self, returned, result):
3580 """Called when the METAR of the arrival airport is retrieved."""
3581 gobject.idle_add(self._handleArrivalMETAR, returned, result)
3582
3583 def _handleArrivalMETAR(self, returned, result):
3584 """Called when the METAR of the arrival airport is retrieved."""
3585 icao = self._bookedFlight.arrivalICAO
3586 if returned and icao in result.metars:
3587 metar = result.metars[icao]
3588 if metar!="":
3589 self._arrivalBriefingPage.setMETAR(metar)
3590
3591#-----------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.