source: src/mlx/gui/gui.py@ 142:4fe5a65e7c37

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

Eliminated the annoying background of the fuel tank figures

File size: 29.2 KB
Line 
1# The main file for the GUI
2
3from statusicon import StatusIcon
4from statusbar import Statusbar
5from info import FlightInfo
6from update import Updater
7from mlx.gui.common import *
8from mlx.gui.flight import Wizard
9from mlx.gui.monitor import MonitorWindow
10from mlx.gui.weighthelp import WeightHelp
11from mlx.gui.gates import FleetGateStatus
12from mlx.gui.prefs import Preferences
13
14import mlx.const as const
15import mlx.fs as fs
16import mlx.flight as flight
17import mlx.logger as logger
18import mlx.acft as acft
19import mlx.web as web
20from mlx.i18n import xstr
21
22import time
23import threading
24import sys
25
26#------------------------------------------------------------------------------
27
28class GUI(fs.ConnectionListener):
29 """The main GUI class."""
30 @staticmethod
31 def _formatFlightLogLine(timeStr, line):
32 """Format the given line for flight logging."""
33 if timeStr is not None:
34 line = timeStr + ": " + line
35 return line + "\n"
36
37 def __init__(self, programDirectory, config):
38 """Construct the GUI."""
39 gobject.threads_init()
40
41 self._programDirectory = programDirectory
42 self.config = config
43 self._connecting = False
44 self._reconnecting = False
45 self._connected = False
46 self._logger = logger.Logger(self)
47 self._flight = None
48 self._simulator = None
49 self._monitoring = False
50
51 self._fleet = None
52
53 self._fleetCallback = None
54
55 self._updatePlaneCallback = None
56 self._updatePlaneTailNumber = None
57 self._updatePlaneStatus = None
58 self._updatePlaneGateNumber = None
59
60 self._stdioLock = threading.Lock()
61 self._stdioText = ""
62
63 self.webHandler = web.Handler()
64 self.webHandler.start()
65
66 self.toRestart = False
67
68 def build(self, iconDirectory):
69 """Build the GUI."""
70
71 self._mainWindow = window = gtk.Window()
72 window.set_title(WINDOW_TITLE_BASE)
73 window.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
74 window.connect("delete-event",
75 lambda a, b: self.hideMainWindow())
76 window.connect("window-state-event", self._handleMainWindowState)
77 accelGroup = gtk.AccelGroup()
78 window.add_accel_group(accelGroup)
79
80 mainVBox = gtk.VBox()
81 window.add(mainVBox)
82
83 self._preferences = Preferences(self)
84
85 menuBar = self._buildMenuBar(accelGroup)
86 mainVBox.pack_start(menuBar, False, False, 0)
87
88 self._notebook = gtk.Notebook()
89 mainVBox.pack_start(self._notebook, True, True, 4)
90
91 self._wizard = Wizard(self)
92 label = gtk.Label(xstr("tab_flight"))
93 label.set_use_underline(True)
94 label.set_tooltip_text(xstr("tab_flight_tooltip"))
95 self._notebook.append_page(self._wizard, label)
96
97 self._flightInfo = FlightInfo(self)
98 label = gtk.Label(xstr("tab_flight_info"))
99 label.set_use_underline(True)
100 label.set_tooltip_text(xstr("tab_flight_info_tooltip"))
101 self._notebook.append_page(self._flightInfo, label)
102 self._flightInfo.disable()
103
104 self._weightHelp = WeightHelp(self)
105 label = gtk.Label(xstr("tab_weight_help"))
106 label.set_use_underline(True)
107 label.set_tooltip_text(xstr("tab_weight_help_tooltip"))
108 self._notebook.append_page(self._weightHelp, label)
109
110 (logWidget, self._logView) = self._buildLogWidget()
111 label = gtk.Label(xstr("tab_log"))
112 label.set_use_underline(True)
113 label.set_tooltip_text(xstr("tab_log_tooltip"))
114 self._notebook.append_page(logWidget, label)
115
116 self._fleetGateStatus = FleetGateStatus(self)
117 label = gtk.Label(xstr("tab_gates"))
118 label.set_use_underline(True)
119 label.set_tooltip_text(xstr("tab_gates_tooltip"))
120 self._notebook.append_page(self._fleetGateStatus, label)
121
122 (self._debugLogWidget, self._debugLogView) = self._buildLogWidget()
123 self._debugLogWidget.show_all()
124
125 mainVBox.pack_start(gtk.HSeparator(), False, False, 0)
126
127 self._statusbar = Statusbar()
128 mainVBox.pack_start(self._statusbar, False, False, 0)
129
130 self._notebook.connect("switch-page", self._notebookPageSwitch)
131
132 self._monitorWindow = MonitorWindow(self, iconDirectory)
133 self._monitorWindow.add_accel_group(accelGroup)
134 self._monitorWindowX = None
135 self._monitorWindowY = None
136 self._selfToggling = False
137
138 window.show_all()
139 self._wizard.grabDefault()
140 self._weightHelp.reset()
141 self._weightHelp.disable()
142
143 self._statusIcon = StatusIcon(iconDirectory, self)
144
145 self._busyCursor = gdk.Cursor(gdk.CursorType.WATCH if pygobject
146 else gdk.WATCH)
147
148 @property
149 def mainWindow(self):
150 """Get the main window of the GUI."""
151 return self._mainWindow
152
153 @property
154 def logger(self):
155 """Get the logger used by us."""
156 return self._logger
157
158 @property
159 def simulator(self):
160 """Get the simulator used by us."""
161 return self._simulator
162
163 @property
164 def flight(self):
165 """Get the flight being performed."""
166 return self._flight
167
168 @property
169 def loginResult(self):
170 """Get the result of the login."""
171 return self._wizard.loginResult
172
173 @property
174 def bookedFlight(self):
175 """Get the booked flight selected, if any."""
176 return self._wizard.bookedFlight
177
178 @property
179 def cargoWeight(self):
180 """Get the cargo weight."""
181 return self._wizard.cargoWeight
182
183 @property
184 def zfw(self):
185 """Get Zero-Fuel Weight calculated for the current flight."""
186 return self._wizard.zfw
187
188 @property
189 def filedCruiseAltitude(self):
190 """Get cruise altitude filed for the current flight."""
191 return self._wizard.filedCruiseAltitude
192
193 @property
194 def cruiseAltitude(self):
195 """Get cruise altitude set for the current flight."""
196 return self._wizard.cruiseAltitude
197
198 @property
199 def route(self):
200 """Get the flight route."""
201 return self._wizard.route
202
203 @property
204 def departureMETAR(self):
205 """Get the METAR of the deprature airport."""
206 return self._wizard.departureMETAR
207
208 @property
209 def arrivalMETAR(self):
210 """Get the METAR of the deprature airport."""
211 return self._wizard.arrivalMETAR
212
213 @property
214 def departureRunway(self):
215 """Get the name of the departure runway."""
216 return self._wizard.departureRunway
217
218 @property
219 def sid(self):
220 """Get the SID."""
221 return self._wizard.sid
222
223 @property
224 def v1(self):
225 """Get the V1 speed calculated for the flight."""
226 return self._wizard.v1
227
228 @property
229 def vr(self):
230 """Get the Vr speed calculated for the flight."""
231 return self._wizard.vr
232
233 @property
234 def v2(self):
235 """Get the V2 speed calculated for the flight."""
236 return self._wizard.v2
237
238 @property
239 def arrivalRunway(self):
240 """Get the arrival runway."""
241 return self._wizard.arrivalRunway
242
243 @property
244 def star(self):
245 """Get the STAR."""
246 return self._wizard.star
247
248 @property
249 def transition(self):
250 """Get the transition."""
251 return self._wizard.transition
252
253 @property
254 def approachType(self):
255 """Get the approach type."""
256 return self._wizard.approachType
257
258 @property
259 def vref(self):
260 """Get the Vref speed calculated for the flight."""
261 return self._wizard.vref
262
263 @property
264 def flightType(self):
265 """Get the flight type."""
266 return self._wizard.flightType
267
268 @property
269 def online(self):
270 """Get whether the flight was online or not."""
271 return self._wizard.online
272
273 @property
274 def comments(self):
275 """Get the comments."""
276 return self._flightInfo.comments
277
278 @property
279 def flightDefects(self):
280 """Get the flight defects."""
281 return self._flightInfo.flightDefects
282
283 @property
284 def delayCodes(self):
285 """Get the delay codes."""
286 return self._flightInfo.delayCodes
287
288 def run(self):
289 """Run the GUI."""
290 if self.config.autoUpdate:
291 self._updater = Updater(self,
292 self._programDirectory,
293 self.config.updateURL,
294 self._mainWindow)
295 self._updater.start()
296
297 gtk.main()
298
299 self._disconnect()
300
301 def connected(self, fsType, descriptor):
302 """Called when we have connected to the simulator."""
303 self._connected = True
304 self._logger.untimedMessage("Connected to the simulator %s" % (descriptor,))
305 fs.sendMessage(const.MESSAGETYPE_INFORMATION,
306 "Welcome to MAVA Logger X " + const.VERSION)
307 gobject.idle_add(self._handleConnected, fsType, descriptor)
308
309 def _handleConnected(self, fsType, descriptor):
310 """Called when the connection to the simulator has succeeded."""
311 self._statusbar.updateConnection(self._connecting, self._connected)
312 self.endBusy()
313 if not self._reconnecting:
314 self._wizard.connected(fsType, descriptor)
315 self._reconnecting = False
316
317 def connectionFailed(self):
318 """Called when the connection failed."""
319 self._logger.untimedMessage("Connection to the simulator failed")
320 gobject.idle_add(self._connectionFailed)
321
322 def _connectionFailed(self):
323 """Called when the connection failed."""
324 self.endBusy()
325 self._statusbar.updateConnection(self._connecting, self._connected)
326
327 dialog = gtk.MessageDialog(parent = self._mainWindow,
328 type = MESSAGETYPE_ERROR,
329 message_format = xstr("conn_failed"))
330
331 dialog.set_title(WINDOW_TITLE_BASE)
332 dialog.format_secondary_markup(xstr("conn_failed_sec"))
333
334 dialog.add_button(xstr("button_cancel"), 0)
335 dialog.add_button(xstr("button_tryagain"), 1)
336 dialog.set_default_response(1)
337
338 result = dialog.run()
339 dialog.hide()
340 if result == 1:
341 self.beginBusy(xstr("connect_busy"))
342 self._simulator.reconnect()
343 else:
344 self.reset()
345
346 def disconnected(self):
347 """Called when we have disconnected from the simulator."""
348 self._connected = False
349 self._logger.untimedMessage("Disconnected from the simulator")
350
351 gobject.idle_add(self._disconnected)
352
353 def _disconnected(self):
354 """Called when we have disconnected from the simulator unexpectedly."""
355 self._statusbar.updateConnection(self._connecting, self._connected)
356
357 dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
358 message_format = xstr("conn_broken"),
359 parent = self._mainWindow)
360 dialog.set_title(WINDOW_TITLE_BASE)
361 dialog.format_secondary_markup(xstr("conn_broken_sec"))
362
363 dialog.add_button(xstr("button_cancel"), 0)
364 dialog.add_button(xstr("button_reconnect"), 1)
365 dialog.set_default_response(1)
366
367 result = dialog.run()
368 dialog.hide()
369 if result == 1:
370 self.beginBusy(xstr("connect_busy"))
371 self._reconnecting = True
372 self._simulator.reconnect()
373 else:
374 self.reset()
375
376 def enableFlightInfo(self):
377 """Enable the flight info tab."""
378 self._flightInfo.enable()
379
380 def reset(self):
381 """Reset the GUI."""
382 self._disconnect()
383
384 self._flightInfo.reset()
385 self._flightInfo.disable()
386 self.resetFlightStatus()
387
388 self._weightHelp.reset()
389 self._weightHelp.disable()
390 self._wizard.reset()
391 self._notebook.set_current_page(0)
392
393 self._logView.get_buffer().set_text("")
394
395 def _disconnect(self):
396 """Disconnect from the simulator if connected."""
397 self.stopMonitoring()
398
399 if self._connected:
400 self._flight.simulator.disconnect()
401 self._connected = False
402
403 self._connecting = False
404 self._reconnecting = False
405 self._statusbar.updateConnection(False, False)
406 self._weightHelp.disable()
407
408 return True
409
410 def addFlightLogLine(self, timeStr, line):
411 """Write the given message line to the log."""
412 gobject.idle_add(self._writeLog,
413 GUI._formatFlightLogLine(timeStr, line),
414 self._logView)
415
416 def updateFlightLogLine(self, index, timeStr, line):
417 """Update the line with the given index."""
418 gobject.idle_add(self._updateFlightLogLine, index,
419 GUI._formatFlightLogLine(timeStr, line))
420
421 def _updateFlightLogLine(self, index, line):
422 """Replace the contents of the given line in the log."""
423 buffer = self._logView.get_buffer()
424 startIter = buffer.get_iter_at_line(index)
425 endIter = buffer.get_iter_at_line(index + 1)
426 buffer.delete(startIter, endIter)
427 buffer.insert(startIter, line)
428 self._logView.scroll_mark_onscreen(buffer.get_insert())
429
430 def check(self, flight, aircraft, logger, oldState, state):
431 """Update the data."""
432 gobject.idle_add(self._monitorWindow.setData, state)
433 gobject.idle_add(self._statusbar.updateTime, state.timestamp)
434
435 def resetFlightStatus(self):
436 """Reset the status of the flight."""
437 self._statusbar.resetFlightStatus()
438 self._statusbar.updateTime()
439 self._statusIcon.resetFlightStatus()
440
441 def setStage(self, stage):
442 """Set the stage of the flight."""
443 gobject.idle_add(self._setStage, stage)
444
445 def _setStage(self, stage):
446 """Set the stage of the flight."""
447 self._statusbar.setStage(stage)
448 self._statusIcon.setStage(stage)
449 self._wizard.setStage(stage)
450 if stage==const.STAGE_END:
451 # FIXME: perhaps a more elegant method, e.g.
452 # the simulator should provide a function disconnect
453 # with a final message
454 gobject.timeout_add(1000, self._disconnect)
455
456 def setRating(self, rating):
457 """Set the rating of the flight."""
458 gobject.idle_add(self._setRating, rating)
459
460 def _setRating(self, rating):
461 """Set the rating of the flight."""
462 self._statusbar.setRating(rating)
463 self._statusIcon.setRating(rating)
464
465 def setNoGo(self, reason):
466 """Set the rating of the flight to No-Go with the given reason."""
467 gobject.idle_add(self._setNoGo, reason)
468
469 def _setNoGo(self, reason):
470 """Set the rating of the flight."""
471 self._statusbar.setNoGo(reason)
472 self._statusIcon.setNoGo(reason)
473
474 def _handleMainWindowState(self, window, event):
475 """Hande a change in the state of the window"""
476 iconified = gdk.WindowState.ICONIFIED if pygobject \
477 else gdk.WINDOW_STATE_ICONIFIED
478 if (event.changed_mask&iconified)!=0 and (event.new_window_state&iconified)!=0:
479 self.hideMainWindow(savePosition = False)
480
481 def hideMainWindow(self, savePosition = True):
482 """Hide the main window and save its position."""
483 if savePosition:
484 (self._mainWindowX, self._mainWindowY) = \
485 self._mainWindow.get_window().get_root_origin()
486 else:
487 self._mainWindowX = self._mainWindowY = None
488 self._mainWindow.hide()
489 self._statusIcon.mainWindowHidden()
490 return True
491
492 def showMainWindow(self):
493 """Show the main window at its former position."""
494 if self._mainWindowX is not None and self._mainWindowY is not None:
495 self._mainWindow.move(self._mainWindowX, self._mainWindowY)
496
497 self._mainWindow.show()
498 self._mainWindow.deiconify()
499
500 self._statusIcon.mainWindowShown()
501
502 def toggleMainWindow(self):
503 """Toggle the main window."""
504 if self._mainWindow.get_visible():
505 self.hideMainWindow()
506 else:
507 self.showMainWindow()
508
509 def hideMonitorWindow(self, savePosition = True):
510 """Hide the monitor window."""
511 if savePosition:
512 (self._monitorWindowX, self._monitorWindowY) = \
513 self._monitorWindow.get_window().get_root_origin()
514 else:
515 self._monitorWindowX = self._monitorWindowY = None
516 self._monitorWindow.hide()
517 self._statusIcon.monitorWindowHidden()
518 if self._showMonitorMenuItem.get_active():
519 self._selfToggling = True
520 self._showMonitorMenuItem.set_active(False)
521 return True
522
523 def showMonitorWindow(self):
524 """Show the monitor window."""
525 if self._monitorWindowX is not None and self._monitorWindowY is not None:
526 self._monitorWindow.move(self._monitorWindowX, self._monitorWindowY)
527 self._monitorWindow.show_all()
528 self._statusIcon.monitorWindowShown()
529 if not self._showMonitorMenuItem.get_active():
530 self._selfToggling = True
531 self._showMonitorMenuItem.set_active(True)
532
533 def _toggleMonitorWindow(self, menuItem):
534 if self._selfToggling:
535 self._selfToggling = False
536 elif self._monitorWindow.get_visible():
537 self.hideMonitorWindow()
538 else:
539 self.showMonitorWindow()
540
541 def restart(self):
542 """Quit and restart the application."""
543 self.toRestart = True
544 self._quit(force = True)
545
546 def flushStdIO(self):
547 """Flush any text to the standard error that could not be logged."""
548 if self._stdioText:
549 sys.__stderr__.write(self._stdioText)
550
551 def writeStdIO(self, text):
552 """Write the given text into standard I/O log."""
553 with self._stdioLock:
554 self._stdioText += text
555
556 gobject.idle_add(self._writeStdIO)
557
558 def beginBusy(self, message):
559 """Begin a period of background processing."""
560 self._wizard.set_sensitive(False)
561 self._weightHelp.set_sensitive(False)
562 self._mainWindow.get_window().set_cursor(self._busyCursor)
563 self._statusbar.updateBusyState(message)
564
565 def endBusy(self):
566 """End a period of background processing."""
567 self._mainWindow.get_window().set_cursor(None)
568 self._weightHelp.set_sensitive(True)
569 self._wizard.set_sensitive(True)
570 self._statusbar.updateBusyState(None)
571
572 def initializeWeightHelp(self):
573 """Initialize the weight help tab."""
574 self._weightHelp.reset()
575 self._weightHelp.enable()
576
577 def getFleetAsync(self, callback = None, force = None):
578 """Get the fleet asynchronously."""
579 gobject.idle_add(self.getFleet, callback, force)
580
581 def getFleet(self, callback = None, force = False):
582 """Get the fleet.
583
584 If force is False, and we already have a fleet retrieved,
585 that one will be used."""
586 if self._fleet is None or force:
587 self._fleetCallback = callback
588 self.beginBusy(xstr("fleet_busy"))
589 self.webHandler.getFleet(self._fleetResultCallback)
590 else:
591 callback(self._fleet)
592
593 def _fleetResultCallback(self, returned, result):
594 """Called when the fleet has been queried."""
595 gobject.idle_add(self._handleFleetResult, returned, result)
596
597 def _handleFleetResult(self, returned, result):
598 """Handle the fleet result."""
599 self.endBusy()
600 if returned:
601 self._fleet = result.fleet
602 else:
603 self._fleet = None
604
605 dialog = gtk.MessageDialog(parent = self.mainWindow,
606 type = MESSAGETYPE_ERROR,
607 message_format = xstr("fleet_failed"))
608 dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
609 dialog.set_title(WINDOW_TITLE_BASE)
610 dialog.run()
611 dialog.hide()
612
613 callback = self._fleetCallback
614 self._fleetCallback = None
615 if callback is not None:
616 callback(self._fleet)
617 self._fleetGateStatus.handleFleet(self._fleet)
618
619 def updatePlane(self, tailNumber, status,
620 gateNumber = None, callback = None):
621 """Update the status of the given plane."""
622 self.beginBusy(xstr("fleet_update_busy"))
623
624 self._updatePlaneCallback = callback
625
626 self._updatePlaneTailNumber = tailNumber
627 self._updatePlaneStatus = status
628 self._updatePlaneGateNumber = gateNumber
629
630 self.webHandler.updatePlane(self._updatePlaneResultCallback,
631 tailNumber, status, gateNumber)
632
633 def _updatePlaneResultCallback(self, returned, result):
634 """Called when the status of a plane has been updated."""
635 gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
636
637 def _handleUpdatePlaneResult(self, returned, result):
638 """Handle the plane update result."""
639 self.endBusy()
640 if returned:
641 success = result.success
642 if success:
643 if self._fleet is not None:
644 self._fleet.updatePlane(self._updatePlaneTailNumber,
645 self._updatePlaneStatus,
646 self._updatePlaneGateNumber)
647 self._fleetGateStatus.handleFleet(self._fleet)
648 else:
649 dialog = gtk.MessageDialog(parent = self.mainWindow,
650 type = MESSAGETYPE_ERROR,
651 message_format = xstr("fleet_update_failed"))
652 dialog.add_button(xstr("button_ok"), RESPONSETYPE_ACCEPT)
653 dialog.set_title(WINDOW_TITLE_BASE)
654 dialog.run()
655 dialog.hide()
656
657 success = None
658
659 callback = self._updatePlaneCallback
660 self._updatePlaneCallback = None
661 if callback is not None:
662 callback(success)
663
664 def _writeStdIO(self):
665 """Perform the real writing."""
666 with self._stdioLock:
667 text = self._stdioText
668 self._stdioText = ""
669 if not text: return
670
671 lines = text.splitlines()
672 if text[-1]=="\n":
673 text = ""
674 else:
675 text = lines[-1]
676 lines = lines[:-1]
677
678 for line in lines:
679 #print >> sys.__stdout__, line
680 self._writeLog(line + "\n", self._debugLogView)
681
682 if text:
683 #print >> sys.__stdout__, text,
684 self._writeLog(text, self._debugLogView)
685
686 def connectSimulator(self, aircraftType):
687 """Connect to the simulator for the first time."""
688 self._logger.reset()
689
690 self._flight = flight.Flight(self._logger, self)
691 self._flight.flareTimeFromFS = self.config.flareTimeFromFS
692 self._flight.aircraftType = aircraftType
693 self._flight.aircraft = acft.Aircraft.create(self._flight)
694 self._flight.aircraft._checkers.append(self)
695
696 if self._simulator is None:
697 self._simulator = fs.createSimulator(const.SIM_MSFS9, self)
698 fs.setupMessageSending(self.config, self._simulator)
699
700 self._flight.simulator = self._simulator
701
702 self.beginBusy(xstr("connect_busy"))
703 self._statusbar.updateConnection(self._connecting, self._connected)
704
705 self._connecting = True
706 self._simulator.connect(self._flight.aircraft)
707
708 def startMonitoring(self):
709 """Start monitoring."""
710 if not self._monitoring:
711 self.simulator.startMonitoring()
712 self._monitoring = True
713
714 def stopMonitoring(self):
715 """Stop monitoring."""
716 if self._monitoring:
717 self.simulator.stopMonitoring()
718 self._monitoring = False
719
720 def _buildMenuBar(self, accelGroup):
721 """Build the main menu bar."""
722 menuBar = gtk.MenuBar()
723
724 fileMenuItem = gtk.MenuItem(xstr("menu_file"))
725 fileMenu = gtk.Menu()
726 fileMenuItem.set_submenu(fileMenu)
727 menuBar.append(fileMenuItem)
728
729 quitMenuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
730 quitMenuItem.set_use_stock(True)
731 quitMenuItem.set_label(xstr("menu_file_quit"))
732 quitMenuItem.add_accelerator("activate", accelGroup,
733 ord(xstr("menu_file_quit_key")),
734 CONTROL_MASK, ACCEL_VISIBLE)
735 quitMenuItem.connect("activate", self._quit)
736 fileMenu.append(quitMenuItem)
737
738 toolsMenuItem = gtk.MenuItem(xstr("menu_tools"))
739 toolsMenu = gtk.Menu()
740 toolsMenuItem.set_submenu(toolsMenu)
741 menuBar.append(toolsMenuItem)
742
743 prefsMenuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
744 prefsMenuItem.set_use_stock(True)
745 prefsMenuItem.set_label(xstr("menu_tools_prefs"))
746 prefsMenuItem.add_accelerator("activate", accelGroup,
747 ord(xstr("menu_tools_prefs_key")),
748 CONTROL_MASK, ACCEL_VISIBLE)
749 prefsMenuItem.connect("activate", self._editPreferences)
750 toolsMenu.append(prefsMenuItem)
751
752 viewMenuItem = gtk.MenuItem(xstr("menu_view"))
753 viewMenu = gtk.Menu()
754 viewMenuItem.set_submenu(viewMenu)
755 menuBar.append(viewMenuItem)
756
757 self._showMonitorMenuItem = gtk.CheckMenuItem()
758 self._showMonitorMenuItem.set_label(xstr("menu_view_monitor"))
759 self._showMonitorMenuItem.set_use_underline(True)
760 self._showMonitorMenuItem.set_active(False)
761 self._showMonitorMenuItem.add_accelerator("activate", accelGroup,
762 ord(xstr("menu_view_monitor_key")),
763 CONTROL_MASK, ACCEL_VISIBLE)
764 self._showMonitorMenuItem.connect("toggled", self._toggleMonitorWindow)
765 viewMenu.append(self._showMonitorMenuItem)
766
767 showDebugMenuItem = gtk.CheckMenuItem()
768 showDebugMenuItem.set_label(xstr("menu_view_debug"))
769 showDebugMenuItem.set_use_underline(True)
770 showDebugMenuItem.set_active(False)
771 showDebugMenuItem.add_accelerator("activate", accelGroup,
772 ord(xstr("menu_view_debug_key")),
773 CONTROL_MASK, ACCEL_VISIBLE)
774 showDebugMenuItem.connect("toggled", self._toggleDebugLog)
775 viewMenu.append(showDebugMenuItem)
776
777 return menuBar
778
779 def _toggleDebugLog(self, menuItem):
780 """Toggle the debug log."""
781 if menuItem.get_active():
782 label = gtk.Label(xstr("tab_debug_log"))
783 label.set_use_underline(True)
784 label.set_tooltip_text(xstr("tab_debug_log_tooltip"))
785 self._debugLogPage = self._notebook.append_page(self._debugLogWidget, label)
786 self._notebook.set_current_page(self._debugLogPage)
787 else:
788 self._notebook.remove_page(self._debugLogPage)
789
790 def _buildLogWidget(self):
791 """Build the widget for the log."""
792 alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
793
794 alignment.set_padding(padding_top = 8, padding_bottom = 8,
795 padding_left = 16, padding_right = 16)
796
797 logScroller = gtk.ScrolledWindow()
798 # FIXME: these should be constants in common
799 logScroller.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
800 else gtk.POLICY_AUTOMATIC,
801 gtk.PolicyType.AUTOMATIC if pygobject
802 else gtk.POLICY_AUTOMATIC)
803 logScroller.set_shadow_type(gtk.ShadowType.IN if pygobject
804 else gtk.SHADOW_IN)
805 logView = gtk.TextView()
806 logView.set_editable(False)
807 logScroller.add(logView)
808
809 logBox = gtk.VBox()
810 logBox.pack_start(logScroller, True, True, 0)
811 logBox.set_size_request(-1, 200)
812
813 alignment.add(logBox)
814
815 return (alignment, logView)
816
817 def _writeLog(self, msg, logView):
818 """Write the given message to the log."""
819 buffer = logView.get_buffer()
820 buffer.insert(buffer.get_end_iter(), msg)
821 logView.scroll_mark_onscreen(buffer.get_insert())
822
823 def _quit(self, what = None, force = False):
824 """Quit from the application."""
825 if force:
826 result=RESPONSETYPE_YES
827 else:
828 dialog = gtk.MessageDialog(parent = self._mainWindow,
829 type = MESSAGETYPE_QUESTION,
830 message_format = xstr("quit_question"))
831
832 dialog.add_button(xstr("button_no"), RESPONSETYPE_NO)
833 dialog.add_button(xstr("button_yes"), RESPONSETYPE_YES)
834
835 dialog.set_title(WINDOW_TITLE_BASE)
836 result = dialog.run()
837 dialog.hide()
838
839 if result==RESPONSETYPE_YES:
840 self._statusIcon.destroy()
841 return gtk.main_quit()
842
843 def _notebookPageSwitch(self, notebook, page, page_num):
844 """Called when the current page of the notebook has changed."""
845 if page_num==0:
846 gobject.idle_add(self._wizard.grabDefault)
847 else:
848 self._mainWindow.set_default(None)
849
850 def _editPreferences(self, menuItem):
851 """Callback for editing the preferences."""
852 self._preferences.run(self.config)
Note: See TracBrowser for help on using the repository browser.