source: src/mlx/gui/flight.py@ 277:33fbad0ca891

Last change on this file since 277:33fbad0ca891 was 277:33fbad0ca891, checked in by István Váradi <ivaradi@…>, 12 years ago

Added popup menu to the list of flights

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