source: src/mlx/gui/flight.py@ 997:57814da33b6a

python3
Last change on this file since 997:57814da33b6a was 996:8035d80d5feb, checked in by István Váradi <ivaradi@…>, 5 years ago

Using 'Gtk' instead of 'gtk' (re #347)

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