source: src/mlx/gui/timetable.py@ 861:5fa8e92b19b2

Last change on this file since 861:5fa8e92b19b2 was 859:1e789c934953, checked in by István Váradi <ivaradi@…>, 7 years ago

Flight booking works (re #304).

File size: 30.1 KB
Line 
1# Module for handling the time table and booking flights
2
3#-----------------------------------------------------------------------------
4
5from mlx.gui.common import *
6from flightlist import ColumnDescriptor
7from mlx.rpc import ScheduledFlight
8
9import mlx.const as const
10
11import datetime
12import random
13
14#-----------------------------------------------------------------------------
15
16class Timetable(gtk.Alignment):
17 """The widget for the time table."""
18 def _getVIPRenderer():
19 """Get the renderer for the VIP column."""
20 renderer = gtk.CellRendererToggle()
21 renderer.set_activatable(True)
22 return renderer
23
24 defaultColumnDescriptors = [
25 ColumnDescriptor("callsign", xstr("timetable_no"),
26 sortable = True, defaultSortable = True),
27 ColumnDescriptor("aircraftType", xstr("timetable_type"),
28 sortable = True,
29 convertFn = lambda aircraftType, flight:
30 aircraftNames[aircraftType]),
31 ColumnDescriptor("departureICAO", xstr("timetable_from"),
32 sortable = True),
33 ColumnDescriptor("arrivalICAO", xstr("timetable_to"), sortable = True),
34 ColumnDescriptor("departureTime", xstr("timetable_dep"),
35 sortable = True),
36 ColumnDescriptor("arrivalTime", xstr("timetable_arr"), sortable = True),
37 ColumnDescriptor("duration", xstr("timetable_duration"),
38 sortable = True,
39 convertFn = lambda duration, flight:
40 "%02d:%02d" % (duration/3600,
41 (duration%3600)/60)),
42 ColumnDescriptor("type", xstr("timetable_vip"), type = bool,
43 renderer = _getVIPRenderer(),
44 sortable = True,
45 convertFn = lambda type, flight:
46 type==ScheduledFlight.TYPE_VIP)
47 ]
48
49 columnOrdering = ["callsign", "aircraftType",
50 "date", "departureTime", "arrivalTime",
51 "departureICAO", "arrivalICAO", "duration", "type"]
52
53 @staticmethod
54 def isFlightSelected(flight, regularEnabled, vipEnabled, aircraftTypes):
55 """Determine if the given flight is selected by the given
56 filtering conditions."""
57 return ((regularEnabled and flight.type==ScheduledFlight.TYPE_NORMAL) or \
58 (vipEnabled and flight.type==ScheduledFlight.TYPE_VIP)) and \
59 flight.aircraftType in aircraftTypes
60
61 def __init__(self, columnDescriptors = defaultColumnDescriptors,
62 popupMenuProducer = None):
63 """Construct the time table."""
64 # FIXME: this is very similar to flightlist.FlightList
65 self._columnDescriptors = columnDescriptors
66 self._popupMenuProducer = popupMenuProducer
67 self._popupMenu = None
68
69 types = [int]
70 defaultSortableIndex = None
71 for columnDescriptor in self._columnDescriptors:
72 if columnDescriptor.defaultSortable:
73 defaultSortableIndex = len(types)
74 columnDescriptor.appendType(types)
75
76 self._model = gtk.ListStore(*types)
77 if defaultSortableIndex is not None:
78 sortOrder = SORT_DESCENDING \
79 if self._columnDescriptors[defaultSortableIndex-1]._defaultDescending \
80 else SORT_ASCENDING
81 self._model.set_sort_column_id(defaultSortableIndex, sortOrder)
82 self._view = gtk.TreeView(self._model)
83
84 self._tooltips = gtk.Tooltips()
85 self._tooltips.disable()
86 self._view.connect("motion-notify-event", self._updateTooltip)
87
88 flightPairIndexColumn = gtk.TreeViewColumn()
89 flightPairIndexColumn.set_visible(False)
90 self._view.append_column(flightPairIndexColumn)
91
92 index = 1
93 for columnDescriptor in self._columnDescriptors:
94 column = columnDescriptor.getViewColumn(index)
95 self._view.append_column(column)
96 self._model.set_sort_func(index, self._compareFlights,
97 columnDescriptor.attribute)
98 index += 1
99
100 self._view.connect("row-activated", self._rowActivated)
101 self._view.connect("button-press-event", self._buttonPressEvent)
102
103 selection = self._view.get_selection()
104 selection.connect("changed", self._selectionChanged)
105
106 scrolledWindow = gtk.ScrolledWindow()
107 scrolledWindow.add(self._view)
108 scrolledWindow.set_size_request(800, -1)
109
110 # FIXME: these should be constants in common.py
111 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
112 else gtk.POLICY_AUTOMATIC,
113 gtk.PolicyType.AUTOMATIC if pygobject
114 else gtk.POLICY_AUTOMATIC)
115 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
116 else gtk.SHADOW_IN)
117
118 super(Timetable, self).__init__(xalign = 0.5, yalign = 0.0,
119 xscale = 0.0, yscale = 1.0)
120 self.add(scrolledWindow)
121
122 self._flightPairs = []
123
124 @property
125 def selectedIndexes(self):
126 """Get the indexes of the selected entries, if any.
127
128 The indexes are sorted."""
129 selection = self._view.get_selection()
130 (model, rows) = selection.get_selected_rows()
131
132 indexes = [self._getIndexForPath(path) for path in rows]
133 indexes.sort()
134 return indexes
135
136 @property
137 def hasFlightPairs(self):
138 """Determine if the timetable contains any flights."""
139 return len(self._flightPairs)>0
140
141 def clear(self):
142 """Clear the flight pairs."""
143 self._model.clear()
144 self._flightPairs = []
145
146 def setFlightPairs(self, flightPairs):
147 """Setup the table contents from the given list of
148 rpc.ScheduledFlightPair objects."""
149 self.clear()
150
151 self._flightPairs = flightPairs
152
153 def getFlightPair(self, index):
154 """Get the flight pair with the given index."""
155 return self._flightPairs[index]
156
157 def updateList(self, regularEnabled, vipEnabled, types):
158 """Update the actual list according to the given filter values."""
159 index = 0
160 self._model.clear()
161 for flightPair in self._flightPairs:
162 flight = flightPair.flight0
163 if Timetable.isFlightSelected(flight, regularEnabled, vipEnabled,
164 types):
165 values = [index]
166 for columnDescriptor in self._columnDescriptors:
167 values.append(columnDescriptor.getValueFrom(flight))
168 self._model.append(values)
169 index += 1
170
171 def _getIndexForPath(self, path):
172 """Get the index for the given path."""
173 iter = self._model.get_iter(path)
174 return self._model.get_value(iter, 0)
175
176 def _rowActivated(self, flightList, path, column):
177 """Called when a row is selected."""
178 self.emit("row-activated", self._getIndexForPath(path))
179
180 def _buttonPressEvent(self, widget, event):
181 """Called when a mouse button is pressed or released."""
182 if event.type!=EVENT_BUTTON_PRESS or event.button!=3 or \
183 self._popupMenuProducer is None:
184 return
185
186 (path, _, _, _) = self._view.get_path_at_pos(int(event.x),
187 int(event.y))
188 selection = self._view.get_selection()
189 selection.unselect_all()
190 selection.select_path(path)
191
192 if self._popupMenu is None:
193 self._popupMenu = self._popupMenuProducer()
194 menu = self._popupMenu
195 if pygobject:
196 menu.popup(None, None, None, None, event.button, event.time)
197 else:
198 menu.popup(None, None, None, event.button, event.time)
199
200 def _selectionChanged(self, selection):
201 """Called when the selection has changed."""
202 self.emit("selection-changed", self.selectedIndexes)
203
204 def _compareFlights(self, model, iter1, iter2, mainColumn):
205 """Compare the flights at the given iterators according to the given
206 main column."""
207 index1 = self._model.get_value(iter1, 0)
208 index2 = self._model.get_value(iter2, 0)
209
210 flightPair1 = self._flightPairs[index1]
211 flightPair2 = self._flightPairs[index2]
212
213 result = flightPair1.compareBy(flightPair2, mainColumn)
214 if result==0:
215 for column in Timetable.columnOrdering:
216 if column!=mainColumn:
217 result = flightPair1.compareBy(flightPair2, column)
218 if result!=0:
219 break
220 return result
221
222 def _updateTooltip(self, widget, event):
223 """Update the tooltip for the position of the given event."""
224 try:
225 (path, col, x, y) = widget.get_path_at_pos( int(event.x), int(event.y))
226 index = self._getIndexForPath(path)
227
228 flight = self._flightPairs[index].flight0
229 comment = flight.comment
230 date = flight.date
231
232 if comment or date!=const.defaultDate:
233 text = ""
234 if comment:
235 text = comment
236 if date!=const.defaultDate:
237 if text:
238 text += "; "
239 text += date.strftime("%Y-%m-%d")
240
241 self._tooltips.set_tip(widget, text)
242 self._tooltips.enable()
243 else:
244 self._tooltips.set_tip(widget, "")
245 self._tooltips.disable()
246 except Exception, e:
247 print e
248 self._tooltips.set_tip(widget, "")
249 self._tooltips.disable()
250
251#-----------------------------------------------------------------------------
252
253class CalendarWindow(gtk.Window):
254 """A window for a calendar."""
255 def __init__(self):
256 """Construct the window."""
257 super(CalendarWindow, self).__init__()
258
259 self.set_decorated(False)
260 self.set_modal(True)
261 self.connect("key-press-event", self._keyPressed)
262
263 mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
264 xscale = 1.0, yscale = 1.0)
265 #mainAlignment.set_padding(padding_top = 0, padding_bottom = 12,
266 # padding_left = 8, padding_right = 8)
267
268 self._calendar = gtk.Calendar()
269 self._calendar.connect("day-selected-double-click", self._daySelectedDoubleClick)
270 mainAlignment.add(self._calendar)
271
272 self.add(mainAlignment)
273
274 def setDate(self, date):
275 """Set the current date to the given one."""
276 self._calendar.select_month(date.month-1, date.year)
277 self._calendar.select_day(date.day)
278
279 def getDate(self):
280 """Get the currently selected date."""
281 (year, monthM1, day) = self._calendar.get_date()
282 return datetime.date(year, monthM1+1, day)
283
284 def _daySelectedDoubleClick(self, calendar):
285 """Called when a date is double clicked."""
286 self.emit("date-selected")
287
288 def _keyPressed(self, window, event):
289 """Called when a key is pressed in the window.
290
291 If the Escape key is pressed, 'delete-event' is emitted to close the
292 window."""
293 keyName = gdk.keyval_name(event.keyval)
294 if keyName =="Escape":
295 self.emit("delete-event", None)
296 return True
297 elif keyName =="Return":
298 self.emit("date-selected")
299 return True
300
301gobject.signal_new("date-selected", CalendarWindow, gobject.SIGNAL_RUN_FIRST,
302 None, ())
303
304#-----------------------------------------------------------------------------
305
306class BookDialog(gtk.Dialog):
307 """The dialog box to select additional data for a booking."""
308 def __init__(self, timetableWindow, flightPair, planes):
309 """Construct the dialog box."""
310 super(BookDialog, self).__init__(title = WINDOW_TITLE_BASE +
311 " - " +
312 xstr("timetable_book_title"),
313 parent = timetableWindow)
314 contentArea = self.get_content_area()
315
316 frame = gtk.Frame(xstr("timetable_book_frame_title"))
317 frame.set_size_request(600, -1)
318
319 mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
320 xscale = 0.0, yscale = 0.0)
321 mainAlignment.set_padding(padding_top = 16, padding_bottom = 12,
322 padding_left = 8, padding_right = 8)
323
324 table = gtk.Table(6, 2)
325 table.set_row_spacings(8)
326 table.set_col_spacings(16)
327
328 row = 0
329 label = gtk.Label()
330 label.set_markup(xstr("timetable_book_callsign"))
331 label.set_alignment(0.0, 0.5)
332 table.attach(label, 0, 1, row, row + 1)
333
334 text = flightPair.flight0.callsign
335 if flightPair.flight1 is not None:
336 text += " / " + flightPair.flight1.callsign
337 label = gtk.Label(text)
338 label.set_alignment(0.0, 0.5)
339 table.attach(label, 1, 2, row, row + 1)
340
341 row += 1
342
343 label = gtk.Label()
344 label.set_markup(xstr("timetable_book_from_to"))
345 label.set_alignment(0.0, 0.5)
346 table.attach(label, 0, 1, row, row + 1)
347
348 text = flightPair.flight0.departureICAO + " - " + \
349 flightPair.flight0.arrivalICAO
350 if flightPair.flight1 is not None:
351 text += " - " + flightPair.flight1.arrivalICAO
352 label = gtk.Label(text)
353 label.set_alignment(0.0, 0.5)
354 table.attach(label, 1, 2, row, row + 1)
355
356 row += 1
357
358 if flightPair.flight0.type==ScheduledFlight.TYPE_VIP and \
359 flightPair.flight0.date!=const.defaultDate:
360 label = gtk.Label()
361 label.set_markup(xstr("timetable_book_flightDate"))
362 label.set_use_underline(True)
363 label.set_alignment(0.0, 0.5)
364 table.attach(label, 0, 1, row, row + 1)
365
366 self._flightDate = gtk.Button()
367 self._flightDate.connect("clicked", self._flightDateClicked)
368 self._flightDate.set_tooltip_text(xstr("timetable_book_flightDate_tooltip"))
369 label.set_mnemonic_widget(self._flightDate)
370
371 table.attach(self._flightDate, 1, 2, row, row + 1)
372
373 self._calendarWindow = calendarWindow = CalendarWindow()
374 calendarWindow.set_transient_for(self)
375 calendarWindow.connect("delete-event", self._calendarWindowDeleted)
376 calendarWindow.connect("date-selected", self._calendarWindowDateSelected)
377
378 self._setDate(flightPair.flight0.date)
379
380 row += 1
381 else:
382 self._flightDate = None
383 self._calendarWindow = None
384
385 label = gtk.Label()
386 label.set_markup(xstr("timetable_book_dep_arr"))
387 label.set_alignment(0.0, 0.5)
388 table.attach(label, 0, 1, row, row + 1)
389
390 text = str(flightPair.flight0.departureTime) + " - " + \
391 str(flightPair.flight0.arrivalTime)
392 if flightPair.flight1 is not None:
393 text += " / " + str(flightPair.flight1.departureTime) + " - " + \
394 str(flightPair.flight1.arrivalTime)
395 label = gtk.Label(text)
396 label.set_alignment(0.0, 0.5)
397 table.attach(label, 1, 2, row, row + 1)
398
399 row += 1
400
401 label = gtk.Label()
402 label.set_markup(xstr("timetable_book_duration"))
403 label.set_alignment(0.0, 0.5)
404 table.attach(label, 0, 1, row, row + 1)
405
406
407 duration = flightPair.flight0.duration
408 text = "%02d:%02d" % (duration/3600, (duration%3600)/60)
409 if flightPair.flight1 is not None:
410 duration = flightPair.flight0.duration
411 text += " / %02d:%02d" % (duration/3600, (duration%3600)/60)
412 label = gtk.Label(text)
413 label.set_alignment(0.0, 0.5)
414 table.attach(label, 1, 2, row, row + 1)
415
416 row += 2
417
418 label = gtk.Label()
419 label.set_markup(xstr("timetable_book_tailNumber"))
420 label.set_alignment(0.0, 0.5)
421 table.attach(label, 0, 1, row, row + 1)
422
423 self._planes = planes
424 tailNumbersModel = gtk.ListStore(str)
425 for plane in planes:
426 tailNumbersModel.append((plane.tailNumber,))
427
428 self._tailNumber = gtk.ComboBox(model = tailNumbersModel)
429 renderer = gtk.CellRendererText()
430 self._tailNumber.pack_start(renderer, True)
431 self._tailNumber.add_attribute(renderer, "text", 0)
432 self._tailNumber.set_tooltip_text(xstr("timetable_book_tailNumber_tooltip"))
433 self._tailNumber.set_active(random.randint(0, len(planes)-1))
434
435 table.attach(self._tailNumber, 1, 2, row, row + 1)
436
437 mainAlignment.add(table)
438
439 frame.add(mainAlignment)
440 contentArea.pack_start(frame, True, True, 4)
441
442 self.add_button(xstr("button_cancel"), RESPONSETYPE_CANCEL)
443
444 self._okButton = self.add_button(xstr("button_book"), RESPONSETYPE_OK)
445 self._okButton.set_use_underline(True)
446 self._okButton.set_can_default(True)
447
448 @property
449 def plane(self):
450 """Get the currently selected plane."""
451 return self._planes[self._tailNumber.get_active()]
452
453 @property
454 def date(self):
455 """Get the flight date, if selected."""
456 return None if self._calendarWindow is None \
457 else self._calendarWindow.getDate()
458
459 def _setDate(self, date):
460 """Set the date to the given one."""
461 self._flightDate.set_label(date.strftime("%Y-%m-%d"))
462 self._calendarWindow.setDate(date)
463
464 def _flightDateClicked(self, button):
465 """Called when the flight date button is clicked."""
466 self._calendarWindow.set_position(gtk.WIN_POS_MOUSE)
467 self.set_focus(self._calendarWindow)
468 self._calendarWindow.show_all()
469
470 def _calendarWindowDeleted(self, window, event):
471 """Called when the flight date window is deleted."""
472 self._calendarWindow.hide()
473
474 def _calendarWindowDateSelected(self, window):
475 """Called when the flight date window is deleted."""
476 self._calendarWindow.hide()
477 date = window.getDate()
478 self._flightDate.set_label(date.strftime("%Y-%m-%d"))
479
480#-----------------------------------------------------------------------------
481
482class TimetableWindow(gtk.Window):
483 """The window to display the timetable."""
484 typeFamilies = [
485 const.AIRCRAFT_FAMILY_B737NG,
486 const.AIRCRAFT_FAMILY_DH8D,
487 const.AIRCRAFT_FAMILY_B767,
488
489 const.AIRCRAFT_FAMILY_B737CL,
490 const.AIRCRAFT_FAMILY_CRJ2,
491 const.AIRCRAFT_FAMILY_F70,
492
493 const.AIRCRAFT_FAMILY_T134,
494 const.AIRCRAFT_FAMILY_T154
495 ]
496
497 def __init__(self, gui):
498 super(TimetableWindow, self).__init__()
499
500 self._gui = gui
501 self.set_title(WINDOW_TITLE_BASE + " - " + xstr("timetable_title"))
502 self.set_size_request(-1, 600)
503 self.set_transient_for(gui.mainWindow)
504 self.set_modal(True)
505 self.connect("key-press-event", self._keyPressed)
506
507 mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
508 xscale = 1.0, yscale = 1.0)
509 mainAlignment.set_padding(padding_top = 0, padding_bottom = 12,
510 padding_left = 8, padding_right = 8)
511
512 vbox = gtk.VBox()
513
514 filterAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
515 xscale = 1.0, yscale = 1.0)
516
517 filterFrame = gtk.Frame(xstr("timetable_filter"))
518
519 filterVBox = gtk.VBox()
520
521 topAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
522 xscale = 0.0, yscale = 0.0)
523 topHBox = gtk.HBox()
524
525 label = gtk.Label(xstr("timetable_flightdate"))
526 label.set_use_underline(True)
527 topHBox.pack_start(label, False, False, 4)
528
529 self._flightDate = gtk.Button()
530 self._flightDate.connect("clicked", self._flightDateClicked)
531 self._flightDate.connect("clicked", self._flightDateClicked)
532 self._flightDate.set_tooltip_text(xstr("timetable_flightdate_tooltip"))
533 label.set_mnemonic_widget(self._flightDate)
534 topHBox.pack_start(self._flightDate, False, False, 4)
535
536 filler = gtk.Alignment()
537 filler.set_size_request(48, 2)
538 topHBox.pack_start(filler, False, True, 0)
539
540 self._regularFlights = gtk.CheckButton(xstr("timetable_show_regular"))
541 self._regularFlights.set_use_underline(True)
542 self._regularFlights.set_tooltip_text(xstr("timetable_show_regular_tooltip"))
543 self._regularFlights.set_active(True)
544 self._regularFlights.connect("toggled", self._filterChanged)
545 topHBox.pack_start(self._regularFlights, False, False, 8)
546
547 self._vipFlights = gtk.CheckButton(xstr("timetable_show_vip"))
548 self._vipFlights.set_use_underline(True)
549 self._vipFlights.set_tooltip_text(xstr("timetable_show_vip_tooltip"))
550 self._vipFlights.set_active(True)
551 self._vipFlights.connect("toggled", self._filterChanged)
552 topHBox.pack_start(self._vipFlights, False, False, 8)
553
554 topAlignment.add(topHBox)
555
556 filterVBox.pack_start(topAlignment, False, False, 4)
557
558 separator = gtk.HSeparator()
559 filterVBox.pack_start(separator, False, False, 4)
560
561 typeAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
562 xscale = 0.0, yscale = 0.0)
563
564 numColumns = 4
565 numRows = (len(TimetableWindow.typeFamilies)+numColumns-1)/numColumns
566
567 typeTable = gtk.Table(numRows, numColumns)
568 typeTable.set_col_spacings(8)
569 row = 0
570 column = 0
571 self._typeFamilyButtons = {}
572 for typeFamily in TimetableWindow.typeFamilies:
573 checkButton = gtk.CheckButton(aircraftFamilyNames[typeFamily])
574 checkButton.set_active(True)
575 checkButton.connect("toggled", self._filterChanged)
576 self._typeFamilyButtons[typeFamily] = checkButton
577
578 typeTable.attach(checkButton, column, column + 1, row, row+1)
579
580 column += 1
581 if column>=numColumns:
582 row += 1
583 column = 0
584
585 typeAlignment.add(typeTable)
586 filterVBox.pack_start(typeAlignment, False, False, 4)
587
588 filterFrame.add(filterVBox)
589
590 filterAlignment.add(filterFrame)
591 vbox.pack_start(filterAlignment, False, False, 2)
592
593 self._timetable = Timetable(popupMenuProducer =
594 self._createTimetablePopupMenu)
595 self._timetable.connect("row-activated", self._rowActivated)
596 self._timetable.connect("selection-changed", self._selectionChanged)
597 vbox.pack_start(self._timetable, True, True, 2)
598
599 alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
600 xscale = 0.0, yscale = 0.0)
601 buttonBox = gtk.HBox()
602
603 self._bookButton = gtk.Button(xstr("button_book"))
604 self._bookButton.set_use_underline(True)
605 self._bookButton.set_can_default(True)
606 self._bookButton.connect("clicked", self._bookClicked)
607 self._bookButton.set_sensitive(False)
608 buttonBox.pack_start(self._bookButton, False, False, 4);
609
610 self._closeButton = gtk.Button(xstr("button_close"))
611 self._closeButton.set_use_underline(True)
612 self._closeButton.connect("clicked", self._closeClicked)
613 buttonBox.pack_start(self._closeButton, False, False, 4);
614
615 alignment.add(buttonBox)
616 vbox.pack_start(alignment, False, False, 2)
617
618 mainAlignment.add(vbox)
619
620 self._calendarWindow = calendarWindow = CalendarWindow()
621 calendarWindow.set_transient_for(self)
622 calendarWindow.connect("delete-event", self._calendarWindowDeleted)
623 calendarWindow.connect("date-selected", self._calendarWindowDateSelected)
624
625 self.add(mainAlignment)
626
627 self.setDate(datetime.date.today())
628
629 self._flightPairToBook = None
630
631 @property
632 def hasFlightPairs(self):
633 """Determine if there any flight pairs displayed in the window."""
634 return self._timetable.hasFlightPairs
635
636 @property
637 def isRegularEnabled(self):
638 """Determine if regular flights are enabled."""
639 return self._regularFlights.get_active()!=0
640
641 @property
642 def isVIPEnabled(self):
643 """Determine if VIP flights are enabled."""
644 return self._vipFlights.get_active()!=0
645
646 def setTypes(self, aircraftTypes):
647 """Enable/disable the type family checkboxes according to the given
648 list of types."""
649 typeFamilies = set()
650 for aircraftType in aircraftTypes:
651 typeFamilies.add(const.aircraftType2Family(aircraftType))
652
653 for (typeFamily, checkButton) in self._typeFamilyButtons.iteritems():
654 checkButton.set_sensitive(typeFamily in typeFamilies)
655
656 def clear(self):
657 """Clear all flight pairs."""
658 self._timetable.clear()
659
660 def setFlightPairs(self, flightPairs):
661 """Set the flight pairs."""
662 self._timetable.setFlightPairs(flightPairs)
663 self._updateList()
664
665 def setDate(self, date):
666 """Set the date to the given one."""
667 self._flightDate.set_label(date.strftime("%Y-%m-%d"))
668 self._calendarWindow.setDate(date)
669
670 def _closeClicked(self, button):
671 """Called when the Close button is clicked.
672
673 A 'delete-event' is emitted to close the window."""
674 self.emit("delete-event", None)
675
676 def _flightDateClicked(self, button):
677 """Called when the flight date button is clicked."""
678 self._calendarWindow.set_position(gtk.WIN_POS_MOUSE)
679 self.set_focus(self._calendarWindow)
680 self._calendarWindow.show_all()
681
682 def _calendarWindowDeleted(self, window, event):
683 """Called when the flight date window is deleted."""
684 self._calendarWindow.hide()
685
686 def _calendarWindowDateSelected(self, window):
687 """Called when the flight date window is deleted."""
688 self._calendarWindow.hide()
689 date = window.getDate()
690 self._flightDate.set_label(date.strftime("%Y-%m-%d"))
691 self._gui.updateTimeTable(date)
692
693 def _filterChanged(self, checkButton):
694 """Called when the filter conditions have changed."""
695 self._updateList()
696
697 def _keyPressed(self, window, event):
698 """Called when a key is pressed in the window.
699
700 If the Escape key is pressed, 'delete-event' is emitted to close the
701 window."""
702 if gdk.keyval_name(event.keyval) == "Escape":
703 self.emit("delete-event", None)
704 return True
705
706 def _updateList(self):
707 """Update the timetable list."""
708 aircraftTypes = []
709 for (aircraftFamily, button) in self._typeFamilyButtons.iteritems():
710 if button.get_active():
711 aircraftTypes += const.aircraftFamily2Types[aircraftFamily]
712
713 self._timetable.updateList(self.isRegularEnabled,
714 self.isVIPEnabled,
715 aircraftTypes)
716
717 def _bookClicked(self, button):
718 """Called when the book button has been clicked."""
719 self._book(self._timetable.getFlightPair(self._timetable.selectedIndexes[0]))
720
721 def _rowActivated(self, timetable, index):
722 """Called when a row has been activated (e.g. double-clicked) in the
723 timetable."""
724 self._book(self._timetable.getFlightPair(index))
725
726 def _selectionChanged(self, timetable, indexes):
727 """Called when the selection has changed.
728
729 It sets the sensitivity of the book button based on whether a row is
730 selected or not."""
731 self._bookButton.set_sensitive(len(indexes)>0)
732
733 def _book(self, flightPair):
734 """Try to book the given flight pair."""
735 self._flightPairToBook = flightPair
736 self._gui.getFleet(callback = self._continueBook,
737 busyCallback = self._busyCallback)
738
739 def _busyCallback(self, busy):
740 """Called when the busy state has changed."""
741 self.set_sensitive(not busy)
742
743 def _continueBook(self, fleet):
744 """Continue booking, once the fleet is available."""
745 flightPair = self._flightPairToBook
746 aircraftType = flightPair.flight0.aircraftType
747 planes = [plane for plane in fleet
748 if plane.aircraftType == aircraftType]
749 planes.sort(cmp = lambda p1, p2: cmp(p1.tailNumber, p2.tailNumber))
750
751 dialog = BookDialog(self, flightPair, planes)
752 dialog.show_all()
753 result = dialog.run()
754 dialog.hide()
755 if result==RESPONSETYPE_OK:
756 flightIDs = [flightPair.flight0.id]
757 if flightPair.flight1 is not None:
758 flightIDs.append(flightPair.flight1.id)
759
760 date = dialog.date
761 if date is None:
762 date = self._calendarWindow.getDate()
763
764 self._gui.bookFlights(self._bookFlightsCallback,
765 flightIDs, date, dialog.plane.tailNumber,
766 busyCallback = self._busyCallback)
767
768 def _bookFlightsCallback(self, returned, result):
769 """Called when the booking has finished."""
770 if returned:
771 dialog = gtk.MessageDialog(parent = self,
772 type = MESSAGETYPE_INFO,
773 message_format = xstr("bookflights_successful"))
774 dialog.format_secondary_markup(xstr("bookflights_successful_secondary"))
775 else:
776 dialog = gtk.MessageDialog(parent = self,
777 type = MESSAGETYPE_ERROR,
778 message_format = xstr("bookflights_failed"))
779 dialog.format_secondary_markup(xstr("bookflights_failed_secondary"))
780
781 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
782 dialog.set_title(WINDOW_TITLE_BASE)
783
784 dialog.run()
785 dialog.hide()
786
787 def _createTimetablePopupMenu(self):
788 """Get the popuop menu for the timetable."""
789 menu = gtk.Menu()
790
791 menuItem = gtk.MenuItem()
792 menuItem.set_label(xstr("timetable_popup_book"))
793 menuItem.set_use_underline(True)
794 menuItem.connect("activate", self._popupBook)
795 menuItem.show()
796
797 menu.append(menuItem)
798
799 return menu
800
801 def _popupBook(self, menuItem):
802 """Try to book the given flight pair."""
803 index = self._timetable.selectedIndexes[0]
804 self._book(self._timetable.getFlightPair(index))
Note: See TracBrowser for help on using the repository browser.