source: src/mlx/gui/flight.py@ 899:ec5238c77005

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

Cockpit and cabin crew weight is included in DOW, extra people are calulcated with specific weights, and passenger weight is set to 84 kgs (re #332)

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