source: src/mlx/gui/gui.py@ 258:81b2f4109e64

Last change on this file since 258:81b2f4109e64 was 257:0f7fb7fec4fc, checked in by István Váradi <ivaradi@…>, 12 years ago

Added Nagy Dani as tester

File size: 49.0 KB
Line 
1# The main file for the GUI
2# -*- coding: utf-8 -*-
3
4from statusicon import StatusIcon
5from statusbar import Statusbar
6from info import FlightInfo
7from update import Updater
8from mlx.gui.common import *
9from mlx.gui.flight import Wizard
10from mlx.gui.monitor import MonitorWindow
11from mlx.gui.weighthelp import WeightHelp
12from mlx.gui.gates import FleetGateStatus
13from mlx.gui.prefs import Preferences
14from mlx.gui.checklist import ChecklistEditor
15from mlx.gui.pirep import PIREPViewer
16
17import mlx.const as const
18import mlx.fs as fs
19import mlx.flight as flight
20import mlx.logger as logger
21import mlx.acft as acft
22import mlx.web as web
23import mlx.singleton as singleton
24from mlx.i18n import xstr, getLanguage
25from mlx.pirep import PIREP
26
27import time
28import threading
29import sys
30import datetime
31import webbrowser
32
33#------------------------------------------------------------------------------
34
35class GUI(fs.ConnectionListener):
36 """The main GUI class."""
37 _authors = [ ("Váradi", "István", "prog_test"),
38 ("Galyassy", "Tamás", "negotiation"),
39 ("Petrovszki", "Gábor", "test"),
40 ("Zsebényi-Loksa", "Gergely", "test"),
41 ("Kurják", "Ákos", "test"),
42 ("Nagy", "Dániel", "test"),
43 ("Radó", "Iván", "test") ]
44
45 def __init__(self, programDirectory, config):
46 """Construct the GUI."""
47 gobject.threads_init()
48
49 self._programDirectory = programDirectory
50 self.config = config
51 self._connecting = False
52 self._reconnecting = False
53 self._connected = False
54 self._logger = logger.Logger(self)
55 self._flight = None
56 self._simulator = None
57 self._monitoring = False
58
59 self._fleet = None
60
61 self._fleetCallback = None
62
63 self._updatePlaneCallback = None
64 self._updatePlaneTailNumber = None
65 self._updatePlaneStatus = None
66 self._updatePlaneGateNumber = None
67
68 self._stdioLock = threading.Lock()
69 self._stdioText = ""
70 self._stdioStartingLine = True
71
72 self._sendPIREPCallback = None
73
74 self.webHandler = web.Handler()
75 self.webHandler.start()
76
77 self.toRestart = False
78
79 def build(self, iconDirectory):
80 """Build the GUI."""
81
82 self._mainWindow = window = gtk.Window()
83 window.set_title(WINDOW_TITLE_BASE)
84 window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
85 window.set_resizable(False)
86 window.connect("delete-event", self.deleteMainWindow)
87 window.connect("window-state-event", self._handleMainWindowState)
88 accelGroup = gtk.AccelGroup()
89 window.add_accel_group(accelGroup)
90
91 mainVBox = gtk.VBox()
92 window.add(mainVBox)
93
94 self._preferences = Preferences(self)
95 self._checklistEditor = ChecklistEditor(self)
96
97 menuBar = self._buildMenuBar(accelGroup)
98 mainVBox.pack_start(menuBar, False, False, 0)
99
100 self._notebook = gtk.Notebook()
101 mainVBox.pack_start(self._notebook, True, True, 4)
102
103 self._wizard = Wizard(self)
104 label = gtk.Label(xstr("tab_flight"))
105 label.set_use_underline(True)
106 label.set_tooltip_text(xstr("tab_flight_tooltip"))
107 self._notebook.append_page(self._wizard, label)
108
109 self._flightInfo = FlightInfo(self)
110 label = gtk.Label(xstr("tab_flight_info"))
111 label.set_use_underline(True)
112 label.set_tooltip_text(xstr("tab_flight_info_tooltip"))
113 self._notebook.append_page(self._flightInfo, label)
114 self._flightInfo.disable()
115
116 self._weightHelp = WeightHelp(self)
117 label = gtk.Label(xstr("tab_weight_help"))
118 label.set_use_underline(True)
119 label.set_tooltip_text(xstr("tab_weight_help_tooltip"))
120 self._notebook.append_page(self._weightHelp, label)
121
122 (logWidget, self._logView) = self._buildLogWidget()
123 addFaultTag(self._logView.get_buffer())
124 label = gtk.Label(xstr("tab_log"))
125 label.set_use_underline(True)
126 label.set_tooltip_text(xstr("tab_log_tooltip"))
127 self._notebook.append_page(logWidget, label)
128
129 self._fleetGateStatus = FleetGateStatus(self)
130 label = gtk.Label(xstr("tab_gates"))
131 label.set_use_underline(True)
132 label.set_tooltip_text(xstr("tab_gates_tooltip"))
133 self._notebook.append_page(self._fleetGateStatus, label)
134
135 (self._debugLogWidget, self._debugLogView) = self._buildLogWidget()
136 self._debugLogWidget.show_all()
137
138 mainVBox.pack_start(gtk.HSeparator(), False, False, 0)
139
140 self._statusbar = Statusbar()
141 mainVBox.pack_start(self._statusbar, False, False, 0)
142
143 self._notebook.connect("switch-page", self._notebookPageSwitch)
144
145 self._monitorWindow = MonitorWindow(self, iconDirectory)
146 self._monitorWindow.add_accel_group(accelGroup)
147 self._monitorWindowX = None
148 self._monitorWindowY = None
149 self._selfToggling = False
150
151 self._pirepViewer = PIREPViewer(self)
152
153 window.show_all()
154 self._wizard.grabDefault()
155 self._weightHelp.reset()
156 self._weightHelp.disable()
157
158 self._statusIcon = StatusIcon(iconDirectory, self)
159
160 self._busyCursor = gdk.Cursor(gdk.CursorType.WATCH if pygobject
161 else gdk.WATCH)
162
163 self._loadPIREPDialog = None
164 self._lastLoadedPIREP = None
165
166 self._hotkeySetID = None
167 self._pilotHotkeyIndex = None
168 self._checklistHotkeyIndex = None
169
170 self._aboutDialog = None
171
172 @property
173 def mainWindow(self):
174 """Get the main window of the GUI."""
175 return self._mainWindow
176
177 @property
178 def logger(self):
179 """Get the logger used by us."""
180 return self._logger
181
182 @property
183 def simulator(self):
184 """Get the simulator used by us."""
185 return self._simulator
186
187 @property
188 def flight(self):
189 """Get the flight being performed."""
190 return self._flight
191
192 @property
193 def entranceExam(self):
194 """Get whether an entrance exam is about to be taken."""
195 return self._wizard.entranceExam
196
197 @property
198 def loggedIn(self):
199 """Indicate if the user has logged in properly."""
200 return self._wizard.loggedIn
201
202 @property
203 def loginResult(self):
204 """Get the result of the login."""
205 return self._wizard.loginResult
206
207 @property
208 def bookedFlight(self):
209 """Get the booked flight selected, if any."""
210 return self._wizard.bookedFlight
211
212 @property
213 def cargoWeight(self):
214 """Get the cargo weight."""
215 return self._wizard.cargoWeight
216
217 @property
218 def zfw(self):
219 """Get Zero-Fuel Weight calculated for the current flight."""
220 return self._wizard.zfw
221
222 @property
223 def filedCruiseAltitude(self):
224 """Get cruise altitude filed for the current flight."""
225 return self._wizard.filedCruiseAltitude
226
227 @property
228 def cruiseAltitude(self):
229 """Get cruise altitude set for the current flight."""
230 return self._wizard.cruiseAltitude
231
232 @property
233 def route(self):
234 """Get the flight route."""
235 return self._wizard.route
236
237 @property
238 def departureMETAR(self):
239 """Get the METAR of the deprature airport."""
240 return self._wizard.departureMETAR
241
242 @property
243 def arrivalMETAR(self):
244 """Get the METAR of the deprature airport."""
245 return self._wizard.arrivalMETAR
246
247 @property
248 def departureRunway(self):
249 """Get the name of the departure runway."""
250 return self._wizard.departureRunway
251
252 @property
253 def sid(self):
254 """Get the SID."""
255 return self._wizard.sid
256
257 @property
258 def v1(self):
259 """Get the V1 speed calculated for the flight."""
260 return self._wizard.v1
261
262 @property
263 def vr(self):
264 """Get the Vr speed calculated for the flight."""
265 return self._wizard.vr
266
267 @property
268 def v2(self):
269 """Get the V2 speed calculated for the flight."""
270 return self._wizard.v2
271
272 @property
273 def arrivalRunway(self):
274 """Get the arrival runway."""
275 return self._wizard.arrivalRunway
276
277 @property
278 def star(self):
279 """Get the STAR."""
280 return self._wizard.star
281
282 @property
283 def transition(self):
284 """Get the transition."""
285 return self._wizard.transition
286
287 @property
288 def approachType(self):
289 """Get the approach type."""
290 return self._wizard.approachType
291
292 @property
293 def vref(self):
294 """Get the Vref speed calculated for the flight."""
295 return self._wizard.vref
296
297 @property
298 def flightType(self):
299 """Get the flight type."""
300 return self._wizard.flightType
301
302 @property
303 def online(self):
304 """Get whether the flight was online or not."""
305 return self._wizard.online
306
307 @property
308 def comments(self):
309 """Get the comments."""
310 return self._flightInfo.comments
311
312 @property
313 def flightDefects(self):
314 """Get the flight defects."""
315 return self._flightInfo.flightDefects
316
317 @property
318 def delayCodes(self):
319 """Get the delay codes."""
320 return self._flightInfo.delayCodes
321
322 def run(self):
323 """Run the GUI."""
324 if self.config.autoUpdate:
325 self._updater = Updater(self,
326 self._programDirectory,
327 self.config.updateURL,
328 self._mainWindow)
329 self._updater.start()
330
331 singleton.raiseCallback = self.raiseCallback
332 gtk.main()
333 singleton.raiseCallback = None
334
335 self._disconnect()
336
337 def connected(self, fsType, descriptor):
338 """Called when we have connected to the simulator."""
339 self._connected = True
340 self._logger.untimedMessage("MLX %s connected to the simulator %s" % \
341 (const.VERSION, descriptor))
342 fs.sendMessage(const.MESSAGETYPE_INFORMATION,
343 "Welcome to MAVA Logger X " + const.VERSION)
344 gobject.idle_add(self._handleConnected, fsType, descriptor)
345
346 def _handleConnected(self, fsType, descriptor):
347 """Called when the connection to the simulator has succeeded."""
348 self._statusbar.updateConnection(self._connecting, self._connected)
349 self.endBusy()
350 if not self._reconnecting:
351 self._wizard.connected(fsType, descriptor)
352 self._reconnecting = False
353 self._listenHotkeys()
354
355 def connectionFailed(self):
356 """Called when the connection failed."""
357 self._logger.untimedMessage("Connection to the simulator failed")
358 gobject.idle_add(self._connectionFailed)
359
360 def _connectionFailed(self):
361 """Called when the connection failed."""
362 self.endBusy()
363 self._statusbar.updateConnection(self._connecting, self._connected)
364
365 dialog = gtk.MessageDialog(parent = self._mainWindow,
366 type = MESSAGETYPE_ERROR,
367 message_format = xstr("conn_failed"))
368
369 dialog.set_title(WINDOW_TITLE_BASE)
370 dialog.format_secondary_markup(xstr("conn_failed_sec"))
371
372 dialog.add_button(xstr("button_cancel"), 0)
373 dialog.add_button(xstr("button_tryagain"), 1)
374 dialog.set_default_response(1)
375
376 result = dialog.run()
377 dialog.hide()
378 if result == 1:
379 self.beginBusy(xstr("connect_busy"))
380 self._simulator.reconnect()
381 else:
382 self.reset()
383
384 def disconnected(self):
385 """Called when we have disconnected from the simulator."""
386 self._connected = False
387 self._logger.untimedMessage("Disconnected from the simulator")
388
389 gobject.idle_add(self._disconnected)
390
391 def _disconnected(self):
392 """Called when we have disconnected from the simulator unexpectedly."""
393 self._statusbar.updateConnection(self._connecting, self._connected)
394
395 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
396 message_format = xstr("conn_broken"),
397 parent = self._mainWindow)
398 dialog.set_title(WINDOW_TITLE_BASE)
399 dialog.format_secondary_markup(xstr("conn_broken_sec"))
400
401 dialog.add_button(xstr("button_cancel"), 0)
402 dialog.add_button(xstr("button_reconnect"), 1)
403 dialog.set_default_response(1)
404
405 result = dialog.run()
406 dialog.hide()
407 if result == 1:
408 self.beginBusy(xstr("connect_busy"))
409 self._reconnecting = True
410 self._simulator.reconnect()
411 else:
412 self.reset()
413
414 def enableFlightInfo(self):
415 """Enable the flight info tab."""
416 self._flightInfo.enable()
417
418 def cancelFlight(self):
419 """Cancel the current file, if the user confirms it."""
420 dialog = gtk.MessageDialog(parent = self._mainWindow,
421 type = MESSAGETYPE_QUESTION,
422 message_format = xstr("cancelFlight_question"))
423
424 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
425 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
426
427 dialog.set_title(WINDOW_TITLE_BASE)
428 result = dialog.run()
429 dialog.hide()
430
431 if result==RESPONSETYPE_YES:
432 self.reset()
433
434 def reset(self):
435 """Reset the GUI."""
436 self._disconnect()
437
438 self._flightInfo.reset()
439 self._flightInfo.disable()
440 self.resetFlightStatus()
441
442 self._weightHelp.reset()
443 self._weightHelp.disable()
444 self._notebook.set_current_page(0)
445
446 self._logView.get_buffer().set_text("")
447
448 if self.loggedIn:
449 self._wizard.reloadFlights(self._handleReloadResult)
450 else:
451 self._wizard.reset(None)
452
453 def _handleReloadResult(self, returned, result):
454 """Handle the result of the reloading of the flights."""
455 self._wizard.reset(result if returned and result.loggedIn else None)
456
457 def _disconnect(self, closingMessage = None, duration = 3):
458 """Disconnect from the simulator if connected."""
459 self.stopMonitoring()
460 self._clearHotkeys()
461
462 if self._connected:
463 if closingMessage is None:
464 self._flight.simulator.disconnect()
465 else:
466 fs.sendMessage(const.MESSAGETYPE_ENVIRONMENT,
467 closingMessage, duration,
468 disconnect = True)
469 self._connected = False
470
471 self._connecting = False
472 self._reconnecting = False
473 self._statusbar.updateConnection(False, False)
474 self._weightHelp.disable()
475
476 return True
477
478 def addFlightLogLine(self, timeStr, line, isFault = False):
479 """Write the given message line to the log."""
480 gobject.idle_add(self._writeLog,
481 formatFlightLogLine(timeStr, line),
482 self._logView, isFault)
483
484 def updateFlightLogLine(self, index, timeStr, line):
485 """Update the line with the given index."""
486 gobject.idle_add(self._updateFlightLogLine, index,
487 formatFlightLogLine(timeStr, line))
488
489 def _updateFlightLogLine(self, index, line):
490 """Replace the contents of the given line in the log."""
491 buffer = self._logView.get_buffer()
492 startIter = buffer.get_iter_at_line(index)
493 endIter = buffer.get_iter_at_line(index + 1)
494 buffer.delete(startIter, endIter)
495 buffer.insert(startIter, line)
496 self._logView.scroll_mark_onscreen(buffer.get_insert())
497
498 def check(self, flight, aircraft, logger, oldState, state):
499 """Update the data."""
500 gobject.idle_add(self._monitorWindow.setData, state)
501 gobject.idle_add(self._statusbar.updateTime, state.timestamp)
502
503 def resetFlightStatus(self):
504 """Reset the status of the flight."""
505 self._statusbar.resetFlightStatus()
506 self._statusbar.updateTime()
507 self._statusIcon.resetFlightStatus()
508
509 def setStage(self, stage):
510 """Set the stage of the flight."""
511 gobject.idle_add(self._setStage, stage)
512
513 def _setStage(self, stage):
514 """Set the stage of the flight."""
515 self._statusbar.setStage(stage)
516 self._statusIcon.setStage(stage)
517 self._wizard.setStage(stage)
518 if stage==const.STAGE_END:
519 self._disconnect(closingMessage =
520 "Flight plan closed. Welcome to %s" % \
521 (self.bookedFlight.arrivalICAO,),
522 duration = 5)
523
524 def setRating(self, rating):
525 """Set the rating of the flight."""
526 gobject.idle_add(self._setRating, rating)
527
528 def _setRating(self, rating):
529 """Set the rating of the flight."""
530 self._statusbar.setRating(rating)
531 self._statusIcon.setRating(rating)
532
533 def setNoGo(self, reason):
534 """Set the rating of the flight to No-Go with the given reason."""
535 gobject.idle_add(self._setNoGo, reason)
536
537 def _setNoGo(self, reason):
538 """Set the rating of the flight."""
539 self._statusbar.setNoGo(reason)
540 self._statusIcon.setNoGo(reason)
541
542 def _handleMainWindowState(self, window, event):
543 """Hande a change in the state of the window"""
544 iconified = gdk.WindowState.ICONIFIED if pygobject \
545 else gdk.WINDOW_STATE_ICONIFIED
546
547 if (event.changed_mask&WINDOW_STATE_WITHDRAWN)!=0:
548 if (event.new_window_state&WINDOW_STATE_WITHDRAWN)!=0:
549 self._statusIcon.mainWindowHidden()
550 else:
551 self._statusIcon.mainWindowShown()
552
553 if self.config.hideMinimizedWindow and not pygobject and \
554 (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
555 (event.new_window_state&WINDOW_STATE_ICONIFIED)!=0:
556 self.hideMainWindow(savePosition = False)
557 elif (event.changed_mask&WINDOW_STATE_ICONIFIED)!=0 and \
558 (event.new_window_state&WINDOW_STATE_ICONIFIED)==0:
559 self._mainWindow.present()
560
561 def raiseCallback(self):
562 """Callback for the singleton handling code."""
563 gobject.idle_add(self.raiseMainWindow)
564
565 def raiseMainWindow(self):
566 """Show the main window if invisible, and raise it."""
567 if not self._mainWindow.get_visible():
568 self.showMainWindow()
569 self._mainWindow.present()
570
571 def deleteMainWindow(self, window, event):
572 """Handle the delete event for the main window."""
573 if self.config.quitOnClose:
574 self._quit()
575 else:
576 self.hideMainWindow()
577 return True
578
579 def hideMainWindow(self, savePosition = True):
580 """Hide the main window and save its position."""
581 if savePosition:
582 (self._mainWindowX, self._mainWindowY) = \
583 self._mainWindow.get_window().get_root_origin()
584 else:
585 self._mainWindowX = self._mainWindowY = None
586 self._mainWindow.hide()
587 return True
588
589 def showMainWindow(self):
590 """Show the main window at its former position."""
591 if self._mainWindowX is not None and self._mainWindowY is not None:
592 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
593
594 if pygobject:
595 self._mainWindow.show()
596 else:
597 self._mainWindow.present()
598 self._mainWindow.deiconify()
599
600 def toggleMainWindow(self):
601 """Toggle the main window."""
602 if self._mainWindow.get_visible():
603 self.hideMainWindow()
604 else:
605 self.showMainWindow()
606
607 def hideMonitorWindow(self, savePosition = True):
608 """Hide the monitor window."""
609 if savePosition:
610 (self._monitorWindowX, self._monitorWindowY) = \
611 self._monitorWindow.get_window().get_root_origin()
612 else:
613 self._monitorWindowX = self._monitorWindowY = None
614 self._monitorWindow.hide()
615 self._statusIcon.monitorWindowHidden()
616 if self._showMonitorMenuItem.get_active():
617 self._selfToggling = True
618 self._showMonitorMenuItem.set_active(False)
619 return True
620
621 def showMonitorWindow(self):
622 """Show the monitor window."""
623 if self._monitorWindowX is not None and self._monitorWindowY is not None:
624 self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
625 self._monitorWindow.show_all()
626 self._statusIcon.monitorWindowShown()
627 if not self._showMonitorMenuItem.get_active():
628 self._selfToggling = True
629 self._showMonitorMenuItem.set_active(True)
630
631 def _toggleMonitorWindow(self, menuItem):
632 if self._selfToggling:
633 self._selfToggling = False
634 elif self._monitorWindow.get_visible():
635 self.hideMonitorWindow()
636 else:
637 self.showMonitorWindow()
638
639 def restart(self):
640 """Quit and restart the application."""
641 self.toRestart = True
642 self._quit(force = True)
643
644 def flushStdIO(self):
645 """Flush any text to the standard error that could not be logged."""
646 if self._stdioText:
647 sys.__stderr__.write(self._stdioText)
648
649 def writeStdIO(self, text):
650 """Write the given text into standard I/O log."""
651 with self._stdioLock:
652 self._stdioText += text
653
654 gobject.idle_add(self._writeStdIO)
655
656 def beginBusy(self, message):
657 """Begin a period of background processing."""
658 self._wizard.set_sensitive(False)
659 self._weightHelp.set_sensitive(False)
660 self._mainWindow.get_window().set_cursor(self._busyCursor)
661 self._statusbar.updateBusyState(message)
662
663 def endBusy(self):
664 """End a period of background processing."""
665 self._mainWindow.get_window().set_cursor(None)
666 self._weightHelp.set_sensitive(True)
667 self._wizard.set_sensitive(True)
668 self._statusbar.updateBusyState(None)
669
670 def initializeWeightHelp(self):
671 """Initialize the weight help tab."""
672 self._weightHelp.reset()
673 self._weightHelp.enable()
674
675 def getFleetAsync(self, callback = None, force = None):
676 """Get the fleet asynchronously."""
677 gobject.idle_add(self.getFleet, callback, force)
678
679 def getFleet(self, callback = None, force = False):
680 """Get the fleet.
681
682 If force is False, and we already have a fleet retrieved,
683 that one will be used."""
684 if self._fleet is None or force:
685 self._fleetCallback = callback
686 self.beginBusy(xstr("fleet_busy"))
687 self.webHandler.getFleet(self._fleetResultCallback)
688 else:
689 callback(self._fleet)
690
691 def _fleetResultCallback(self, returned, result):
692 """Called when the fleet has been queried."""
693 gobject.idle_add(self._handleFleetResult, returned, result)
694
695 def _handleFleetResult(self, returned, result):
696 """Handle the fleet result."""
697 self.endBusy()
698 if returned:
699 self._fleet = result.fleet
700 else:
701 self._fleet = None
702
703 dialog = gtk.MessageDialog(parent = self.mainWindow,
704 type = MESSAGETYPE_ERROR,
705 message_format = xstr("fleet_failed"))
706 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
707 dialog.set_title(WINDOW_TITLE_BASE)
708 dialog.run()
709 dialog.hide()
710
711 callback = self._fleetCallback
712 self._fleetCallback = None
713 if callback is not None:
714 callback(self._fleet)
715 self._fleetGateStatus.handleFleet(self._fleet)
716
717 def updatePlane(self, tailNumber, status,
718 gateNumber = None, callback = None):
719 """Update the status of the given plane."""
720 self.beginBusy(xstr("fleet_update_busy"))
721
722 self._updatePlaneCallback = callback
723
724 self._updatePlaneTailNumber = tailNumber
725 self._updatePlaneStatus = status
726 self._updatePlaneGateNumber = gateNumber
727
728 self.webHandler.updatePlane(self._updatePlaneResultCallback,
729 tailNumber, status, gateNumber)
730
731 def _updatePlaneResultCallback(self, returned, result):
732 """Called when the status of a plane has been updated."""
733 gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
734
735 def _handleUpdatePlaneResult(self, returned, result):
736 """Handle the plane update result."""
737 self.endBusy()
738 if returned:
739 success = result.success
740 if success:
741 if self._fleet is not None:
742 self._fleet.updatePlane(self._updatePlaneTailNumber,
743 self._updatePlaneStatus,
744 self._updatePlaneGateNumber)
745 self._fleetGateStatus.handleFleet(self._fleet)
746 else:
747 dialog = gtk.MessageDialog(parent = self.mainWindow,
748 type = MESSAGETYPE_ERROR,
749 message_format = xstr("fleet_update_failed"))
750 dialog.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
751 dialog.set_title(WINDOW_TITLE_BASE)
752 dialog.run()
753 dialog.hide()
754
755 success = None
756
757 callback = self._updatePlaneCallback
758 self._updatePlaneCallback = None
759 if callback is not None:
760 callback(success)
761
762 def _writeStdIO(self):
763 """Perform the real writing."""
764 with self._stdioLock:
765 text = self._stdioText
766 self._stdioText = ""
767 if not text: return
768
769 lines = text.splitlines()
770 if text[-1]=="\n":
771 text = ""
772 else:
773 text = lines[-1]
774 lines = lines[:-1]
775
776 now = datetime.datetime.now()
777 timeStr = "%02d:%02d:%02d: " % (now.hour, now.minute, now.second)
778
779 for line in lines:
780 #print >> sys.__stdout__, line
781 if self._stdioStartingLine:
782 self._writeLog(timeStr, self._debugLogView)
783 self._writeLog(line + "\n", self._debugLogView)
784 self._stdioStartingLine = True
785
786 if text:
787 #print >> sys.__stdout__, text,
788 if self._stdioStartingLine:
789 self._writeLog(timeStr, self._debugLogView)
790 self._writeLog(text, self._debugLogView)
791 self._stdioStartingLine = False
792
793 def connectSimulator(self, aircraftType):
794 """Connect to the simulator for the first time."""
795 self._logger.reset()
796
797 self._flight = flight.Flight(self._logger, self)
798 self._flight.flareTimeFromFS = self.config.flareTimeFromFS
799 self._flight.aircraftType = aircraftType
800 self._flight.aircraft = acft.Aircraft.create(self._flight)
801 self._flight.aircraft._checkers.append(self)
802
803 if self._simulator is None:
804 self._simulator = fs.createSimulator(const.SIM_MSFS9, self)
805 fs.setupMessageSending(self.config, self._simulator)
806 self._setupTimeSync()
807
808 self._flight.simulator = self._simulator
809
810 self.beginBusy(xstr("connect_busy"))
811 self._statusbar.updateConnection(self._connecting, self._connected)
812
813 self._connecting = True
814 self._simulator.connect(self._flight.aircraft)
815
816 def startMonitoring(self):
817 """Start monitoring."""
818 if not self._monitoring:
819 self.simulator.startMonitoring()
820 self._monitoring = True
821
822 def stopMonitoring(self):
823 """Stop monitoring."""
824 if self._monitoring:
825 self.simulator.stopMonitoring()
826 self._monitoring = False
827
828 def _buildMenuBar(self, accelGroup):
829 """Build the main menu bar."""
830 menuBar = gtk.MenuBar()
831
832 fileMenuItem = gtk.MenuItem(xstr("menu_file"))
833 fileMenu = gtk.Menu()
834 fileMenuItem.set_submenu(fileMenu)
835 menuBar.append(fileMenuItem)
836
837 loadPIREPMenuItem = gtk.ImageMenuItem(gtk.STOCK_OPEN)
838 loadPIREPMenuItem.set_use_stock(True)
839 loadPIREPMenuItem.set_label(xstr("menu_file_loadPIREP"))
840 loadPIREPMenuItem.add_accelerator("activate", accelGroup,
841 ord(xstr("menu_file_loadPIREP_key")),
842 CONTROL_MASK, ACCEL_VISIBLE)
843 loadPIREPMenuItem.connect("activate", self._loadPIREP)
844 fileMenu.append(loadPIREPMenuItem)
845
846 fileMenu.append(gtk.SeparatorMenuItem())
847
848 quitMenuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
849 quitMenuItem.set_use_stock(True)
850 quitMenuItem.set_label(xstr("menu_file_quit"))
851 quitMenuItem.add_accelerator("activate", accelGroup,
852 ord(xstr("menu_file_quit_key")),
853 CONTROL_MASK, ACCEL_VISIBLE)
854 quitMenuItem.connect("activate", self._quit)
855 fileMenu.append(quitMenuItem)
856
857 toolsMenuItem = gtk.MenuItem(xstr("menu_tools"))
858 toolsMenu = gtk.Menu()
859 toolsMenuItem.set_submenu(toolsMenu)
860 menuBar.append(toolsMenuItem)
861
862 checklistMenuItem = gtk.ImageMenuItem(gtk.STOCK_APPLY)
863 checklistMenuItem.set_use_stock(True)
864 checklistMenuItem.set_label(xstr("menu_tools_chklst"))
865 checklistMenuItem.add_accelerator("activate", accelGroup,
866 ord(xstr("menu_tools_chklst_key")),
867 CONTROL_MASK, ACCEL_VISIBLE)
868 checklistMenuItem.connect("activate", self._editChecklist)
869 toolsMenu.append(checklistMenuItem)
870
871 prefsMenuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
872 prefsMenuItem.set_use_stock(True)
873 prefsMenuItem.set_label(xstr("menu_tools_prefs"))
874 prefsMenuItem.add_accelerator("activate", accelGroup,
875 ord(xstr("menu_tools_prefs_key")),
876 CONTROL_MASK, ACCEL_VISIBLE)
877 prefsMenuItem.connect("activate", self._editPreferences)
878 toolsMenu.append(prefsMenuItem)
879
880 viewMenuItem = gtk.MenuItem(xstr("menu_view"))
881 viewMenu = gtk.Menu()
882 viewMenuItem.set_submenu(viewMenu)
883 menuBar.append(viewMenuItem)
884
885 self._showMonitorMenuItem = gtk.CheckMenuItem()
886 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
887 self._showMonitorMenuItem.set_use_underline(True)
888 self._showMonitorMenuItem.set_active(False)
889 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
890 ord(xstr("menu_view_monitor_key")),
891 CONTROL_MASK, ACCEL_VISIBLE)
892 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
893 viewMenu.append(self._showMonitorMenuItem)
894
895 showDebugMenuItem = gtk.CheckMenuItem()
896 showDebugMenuItem.set_label(xstr("menu_view_debug"))
897 showDebugMenuItem.set_use_underline(True)
898 showDebugMenuItem.set_active(False)
899 showDebugMenuItem.add_accelerator("activate", accelGroup,
900 ord(xstr("menu_view_debug_key")),
901 CONTROL_MASK, ACCEL_VISIBLE)
902 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
903 viewMenu.append(showDebugMenuItem)
904
905 helpMenuItem = gtk.MenuItem(xstr("menu_help"))
906 helpMenu = gtk.Menu()
907 helpMenuItem.set_submenu(helpMenu)
908 menuBar.append(helpMenuItem)
909
910 manualMenuItem = gtk.ImageMenuItem(gtk.STOCK_HELP)
911 manualMenuItem.set_use_stock(True)
912 manualMenuItem.set_label(xstr("menu_help_manual"))
913 manualMenuItem.add_accelerator("activate", accelGroup,
914 ord(xstr("menu_help_manual_key")),
915 CONTROL_MASK, ACCEL_VISIBLE)
916 manualMenuItem.connect("activate", self._showManual)
917 helpMenu.append(manualMenuItem)
918
919 helpMenu.append(gtk.SeparatorMenuItem())
920
921 aboutMenuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
922 aboutMenuItem.set_use_stock(True)
923 aboutMenuItem.set_label(xstr("menu_help_about"))
924 aboutMenuItem.add_accelerator("activate", accelGroup,
925 ord(xstr("menu_help_about_key")),
926 CONTROL_MASK, ACCEL_VISIBLE)
927 aboutMenuItem.connect("activate", self._showAbout)
928 helpMenu.append(aboutMenuItem)
929
930 return menuBar
931
932 def _toggleDebugLog(self, menuItem):
933 """Toggle the debug log."""
934 if menuItem.get_active():
935 label = gtk.Label(xstr("tab_debug_log"))
936 label.set_use_underline(True)
937 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
938 self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
939 self._notebook.set_current_page(self._debugLogPage)
940 else:
941 self._notebook.remove_page(self._debugLogPage)
942
943 def _buildLogWidget(self):
944 """Build the widget for the log."""
945 alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
946
947 alignment.set_padding(padding_top = 8, padding_bottom = 8,
948 padding_left = 16, padding_right = 16)
949
950 logScroller = gtk.ScrolledWindow()
951 # FIXME: these should be constants in common
952 logScroller.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
953 else gtk.POLICY_AUTOMATIC,
954 gtk.PolicyType.AUTOMATIC if pygobject
955 else gtk.POLICY_AUTOMATIC)
956 logScroller.set_shadow_type(gtk.ShadowType.IN if pygobject
957 else gtk.SHADOW_IN)
958 logView = gtk.TextView()
959 logView.set_editable(False)
960 logView.set_cursor_visible(False)
961 logScroller.add(logView)
962
963 logBox = gtk.VBox()
964 logBox.pack_start(logScroller, True, True, 0)
965 logBox.set_size_request(-1, 200)
966
967 alignment.add(logBox)
968
969 return (alignment, logView)
970
971 def _writeLog(self, msg, logView, isFault = False):
972 """Write the given message to the log."""
973 buffer = logView.get_buffer()
974 appendTextBuffer(buffer, msg, isFault = isFault)
975 logView.scroll_mark_onscreen(buffer.get_insert())
976
977 def _quit(self, what = None, force = False):
978 """Quit from the application."""
979 if force:
980 result=RESPONSETYPE_YES
981 else:
982 dialog = gtk.MessageDialog(parent = self._mainWindow,
983 type = MESSAGETYPE_QUESTION,
984 message_format = xstr("quit_question"))
985
986 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
987 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
988
989 dialog.set_title(WINDOW_TITLE_BASE)
990 result = dialog.run()
991 dialog.hide()
992
993 if result==RESPONSETYPE_YES:
994 self._statusIcon.destroy()
995 return gtk.main_quit()
996
997 def _notebookPageSwitch(self, notebook, page, page_num):
998 """Called when the current page of the notebook has changed."""
999 if page_num==0:
1000 gobject.idle_add(self._wizard.grabDefault)
1001 else:
1002 self._mainWindow.set_default(None)
1003
1004 def _editChecklist(self, menuItem):
1005 """Callback for editing the checklists."""
1006 self._checklistEditor.run()
1007
1008 def _editPreferences(self, menuItem):
1009 """Callback for editing the preferences."""
1010 self._clearHotkeys()
1011 self._preferences.run(self.config)
1012 self._setupTimeSync()
1013 self._listenHotkeys()
1014
1015 def _setupTimeSync(self):
1016 """Enable or disable the simulator time synchronization based on the
1017 configuration."""
1018 simulator = self._simulator
1019 if simulator is not None:
1020 if self.config.syncFSTime:
1021 simulator.enableTimeSync()
1022 else:
1023 simulator.disableTimeSync()
1024
1025 def _loadPIREP(self, menuItem):
1026 """Load a PIREP for sending."""
1027 dialog = self._getLoadPirepDialog()
1028
1029 if self._lastLoadedPIREP:
1030 dialog.set_current_folder(os.path.dirname(self._lastLoadedPIREP))
1031 else:
1032 pirepDirectory = self.config.pirepDirectory
1033 if pirepDirectory is not None:
1034 dialog.set_current_folder(pirepDirectory)
1035
1036 result = dialog.run()
1037 dialog.hide()
1038
1039 if result==RESPONSETYPE_OK:
1040 self._lastLoadedPIREP = text2unicode(dialog.get_filename())
1041
1042 pirep = PIREP.load(self._lastLoadedPIREP)
1043 if pirep is None:
1044 dialog = gtk.MessageDialog(parent = self._mainWindow,
1045 type = MESSAGETYPE_ERROR,
1046 message_format = xstr("loadPIREP_failed"))
1047 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1048 dialog.set_title(WINDOW_TITLE_BASE)
1049 dialog.format_secondary_markup(xstr("loadPIREP_failed_sec"))
1050 dialog.run()
1051 dialog.hide()
1052 else:
1053 dialog = self._getSendLoadedDialog(pirep)
1054 dialog.show_all()
1055 while True:
1056 result = dialog.run()
1057
1058 if result==RESPONSETYPE_OK:
1059 self.sendPIREP(pirep)
1060 elif result==1:
1061 self._pirepViewer.setPIREP(pirep)
1062 self._pirepViewer.show_all()
1063 self._pirepViewer.run()
1064 self._pirepViewer.hide()
1065 else:
1066 break
1067
1068 dialog.hide()
1069
1070 def _getLoadPirepDialog(self):
1071 """Get the PIREP loading file chooser dialog.
1072
1073 If it is not created yet, it will be created."""
1074 if self._loadPIREPDialog is None:
1075 dialog = gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
1076 xstr("loadPIREP_browser_title"),
1077 action = FILE_CHOOSER_ACTION_OPEN,
1078 buttons = (gtk.STOCK_CANCEL,
1079 RESPONSETYPE_CANCEL,
1080 gtk.STOCK_OK, RESPONSETYPE_OK),
1081 parent = self._mainWindow)
1082 dialog.set_modal(True)
1083
1084
1085 filter = gtk.FileFilter()
1086 filter.set_name(xstr("file_filter_pireps"))
1087 filter.add_pattern("*.pirep")
1088 dialog.add_filter(filter)
1089
1090 filter = gtk.FileFilter()
1091 filter.set_name(xstr("file_filter_all"))
1092 filter.add_pattern("*.*")
1093 dialog.add_filter(filter)
1094
1095 self._loadPIREPDialog = dialog
1096
1097 return self._loadPIREPDialog
1098
1099 def _getSendLoadedDialog(self, pirep):
1100 """Get a dialog displaying the main information of the flight from the
1101 PIREP and providing Cancel and Send buttons."""
1102 dialog = gtk.Dialog(title = WINDOW_TITLE_BASE + " - " +
1103 xstr("loadPIREP_send_title"),
1104 parent = self._mainWindow,
1105 flags = DIALOG_MODAL)
1106
1107 contentArea = dialog.get_content_area()
1108
1109 label = gtk.Label(xstr("loadPIREP_send_help"))
1110 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1111 xscale = 0.0, yscale = 0.0)
1112 alignment.set_padding(padding_top = 16, padding_bottom = 0,
1113 padding_left = 48, padding_right = 48)
1114 alignment.add(label)
1115 contentArea.pack_start(alignment, False, False, 8)
1116
1117 table = gtk.Table(5, 2)
1118 tableAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
1119 xscale = 0.0, yscale = 0.0)
1120 tableAlignment.set_padding(padding_top = 0, padding_bottom = 32,
1121 padding_left = 48, padding_right = 48)
1122 table.set_row_spacings(4)
1123 table.set_col_spacings(16)
1124 tableAlignment.add(table)
1125 contentArea.pack_start(tableAlignment, True, True, 8)
1126
1127 bookedFlight = pirep.bookedFlight
1128
1129 label = gtk.Label("<b>" + xstr("loadPIREP_send_flightno") + "</b>")
1130 label.set_use_markup(True)
1131 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1132 xscale = 0.0, yscale = 0.0)
1133 labelAlignment.add(label)
1134 table.attach(labelAlignment, 0, 1, 0, 1)
1135
1136 label = gtk.Label(bookedFlight.callsign)
1137 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1138 xscale = 0.0, yscale = 0.0)
1139 labelAlignment.add(label)
1140 table.attach(labelAlignment, 1, 2, 0, 1)
1141
1142 label = gtk.Label("<b>" + xstr("loadPIREP_send_date") + "</b>")
1143 label.set_use_markup(True)
1144 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1145 xscale = 0.0, yscale = 0.0)
1146 labelAlignment.add(label)
1147 table.attach(labelAlignment, 0, 1, 1, 2)
1148
1149 label = gtk.Label(str(bookedFlight.departureTime.date()))
1150 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1151 xscale = 0.0, yscale = 0.0)
1152 labelAlignment.add(label)
1153 table.attach(labelAlignment, 1, 2, 1, 2)
1154
1155 label = gtk.Label("<b>" + xstr("loadPIREP_send_from") + "</b>")
1156 label.set_use_markup(True)
1157 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1158 xscale = 0.0, yscale = 0.0)
1159 labelAlignment.add(label)
1160 table.attach(labelAlignment, 0, 1, 2, 3)
1161
1162 label = gtk.Label(bookedFlight.departureICAO)
1163 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1164 xscale = 0.0, yscale = 0.0)
1165 labelAlignment.add(label)
1166 table.attach(labelAlignment, 1, 2, 2, 3)
1167
1168 label = gtk.Label("<b>" + xstr("loadPIREP_send_to") + "</b>")
1169 label.set_use_markup(True)
1170 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1171 xscale = 0.0, yscale = 0.0)
1172 labelAlignment.add(label)
1173 table.attach(labelAlignment, 0, 1, 3, 4)
1174
1175 label = gtk.Label(bookedFlight.arrivalICAO)
1176 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1177 xscale = 0.0, yscale = 0.0)
1178 labelAlignment.add(label)
1179 table.attach(labelAlignment, 1, 2, 3, 4)
1180
1181 label = gtk.Label("<b>" + xstr("loadPIREP_send_rating") + "</b>")
1182 label.set_use_markup(True)
1183 labelAlignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
1184 xscale = 0.0, yscale = 0.0)
1185 labelAlignment.add(label)
1186 table.attach(labelAlignment, 0, 1, 4, 5)
1187
1188 rating = pirep.rating
1189 label = gtk.Label()
1190 if rating<0:
1191 label.set_markup('<b><span foreground="red">NO GO</span></b>')
1192 else:
1193 label.set_text("%.1f %%" % (rating,))
1194
1195 labelAlignment = gtk.Alignment(xalign = 0.0, yalign = 0.5,
1196 xscale = 0.0, yscale = 0.0)
1197 labelAlignment.add(label)
1198 table.attach(labelAlignment, 1, 2, 4, 5)
1199
1200 dialog.add_button(xstr("button_cancel"), RESPONSETYPE_REJECT)
1201 dialog.add_button(xstr("viewPIREP"), 1)
1202 dialog.add_button(xstr("sendPIREP"), RESPONSETYPE_OK)
1203
1204 return dialog
1205
1206 def sendPIREP(self, pirep, callback = None):
1207 """Send the given PIREP."""
1208 self.beginBusy(xstr("sendPIREP_busy"))
1209 self._sendPIREPCallback = callback
1210 self.webHandler.sendPIREP(self._pirepSentCallback, pirep)
1211
1212 def _pirepSentCallback(self, returned, result):
1213 """Callback for the PIREP sending result."""
1214 gobject.idle_add(self._handlePIREPSent, returned, result)
1215
1216 def _handlePIREPSent(self, returned, result):
1217 """Callback for the PIREP sending result."""
1218 self.endBusy()
1219 secondaryMarkup = None
1220 type = MESSAGETYPE_ERROR
1221 if returned:
1222 if result.success:
1223 type = MESSAGETYPE_INFO
1224 messageFormat = xstr("sendPIREP_success")
1225 secondaryMarkup = xstr("sendPIREP_success_sec")
1226 elif result.alreadyFlown:
1227 messageFormat = xstr("sendPIREP_already")
1228 secondaryMarkup = xstr("sendPIREP_already_sec")
1229 elif result.notAvailable:
1230 messageFormat = xstr("sendPIREP_notavail")
1231 else:
1232 messageFormat = xstr("sendPIREP_unknown")
1233 secondaryMarkup = xstr("sendPIREP_unknown_sec")
1234 else:
1235 print "PIREP sending failed", result
1236 messageFormat = xstr("sendPIREP_failed")
1237 secondaryMarkup = xstr("sendPIREP_failed_sec")
1238
1239 dialog = gtk.MessageDialog(parent = self._wizard.gui.mainWindow,
1240 type = type, message_format = messageFormat)
1241 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
1242 dialog.set_title(WINDOW_TITLE_BASE)
1243 if secondaryMarkup is not None:
1244 dialog.format_secondary_markup(secondaryMarkup)
1245
1246 dialog.run()
1247 dialog.hide()
1248
1249 callback = self._sendPIREPCallback
1250 self._sendPIREPCallback = None
1251 if callback is not None:
1252 callback(returned, result)
1253
1254 def _listenHotkeys(self):
1255 """Setup the hotkeys based on the configuration."""
1256 if self._hotkeySetID is None and self._simulator is not None:
1257 self._pilotHotkeyIndex = None
1258 self._checklistHotkeyIndex = None
1259
1260 hotkeys = []
1261
1262 config = self.config
1263 if config.enableSounds and config.pilotControlsSounds:
1264 self._pilotHotkeyIndex = len(hotkeys)
1265 hotkeys.append(config.pilotHotkey)
1266
1267 if config.enableChecklists:
1268 self._checklistHotkeyIndex = len(hotkeys)
1269 hotkeys.append(config.checklistHotkey)
1270
1271 if hotkeys:
1272 self._hotkeySetID = \
1273 self._simulator.listenHotkeys(hotkeys, self._handleHotkeys)
1274
1275 def _clearHotkeys(self):
1276 """Clear the hotkeys."""
1277 if self._hotkeySetID is not None:
1278 self._hotkeySetID=None
1279 self._simulator.clearHotkeys()
1280
1281 def _handleHotkeys(self, id, hotkeys):
1282 """Handle the hotkeys."""
1283 if id==self._hotkeySetID:
1284 for index in hotkeys:
1285 if index==self._pilotHotkeyIndex:
1286 print "gui.GUI._handleHotkeys: pilot hotkey pressed"
1287 self._flight.pilotHotkeyPressed()
1288 elif index==self._checklistHotkeyIndex:
1289 print "gui.GUI._handleHotkeys: checklist hotkey pressed"
1290 self._flight.checklistHotkeyPressed()
1291 else:
1292 print "gui.GUI._handleHotkeys: unhandled hotkey index:", index
1293
1294 def _showManual(self, menuitem):
1295 """Show the user's manual."""
1296 webbrowser.open(url ="file://" +
1297 os.path.join(self._programDirectory, "doc", "manual",
1298 getLanguage(), "index.html"),
1299 new = 1)
1300
1301 def _showAbout(self, menuitem):
1302 """Show the about dialog."""
1303 dialog = self._getAboutDialog()
1304 dialog.show_all()
1305 dialog.run()
1306 dialog.hide()
1307
1308 def _getAboutDialog(self):
1309 """Get the about dialog.
1310
1311 If it does not exist yet, it will be created."""
1312 if self._aboutDialog is None:
1313 self._aboutDialog = dialog = gtk.AboutDialog()
1314 dialog.set_transient_for(self._mainWindow)
1315 dialog.set_modal(True)
1316
1317 logoPath = os.path.join(self._programDirectory, "logo.png")
1318 logo = pixbuf_new_from_file(logoPath)
1319 dialog.set_logo(logo)
1320
1321 dialog.set_program_name(PROGRAM_NAME)
1322 dialog.set_version(const.VERSION)
1323 dialog.set_copyright("(c) 2012 by István Váradi")
1324 dialog.set_website("http://mlx.varadiistvan.hu")
1325 dialog.set_website_label(xstr("about_website"))
1326
1327 isHungarian = getLanguage()=="hu"
1328 authors = []
1329 for (familyName, firstName, role) in GUI._authors:
1330 authors.append("%s %s (%s)" % \
1331 (familyName if isHungarian else firstName,
1332 firstName if isHungarian else familyName,
1333 xstr("about_role_" + role)))
1334 dialog.set_authors(authors)
1335
1336 dialog.set_license(xstr("about_license"))
1337
1338 if not pygobject:
1339 gtk.about_dialog_set_url_hook(self._showAboutURL, None)
1340
1341 return self._aboutDialog
1342
1343 def _showAboutURL(self, dialog, link, user_data):
1344 """Show the about URL."""
1345 webbrowser.open(url = link, new = 1)
Note: See TracBrowser for help on using the repository browser.