source: src/mlx/gui/gui.py@ 137:f7489252b322

Last change on this file since 137:f7489252b322 was 135:940d8d54f6d6, checked in by István Váradi <ivaradi@…>, 13 years ago

Fixed delayed disconnection

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