source: src/mlx/gui/flight.py@ 866:0be744731d1b

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

Some padding to the left of the flight-specific buttons (re #304)

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