source: src/mlx/gui/prefs.py@ 1094:a2a4b6462f53

python3
Last change on this file since 1094:a2a4b6462f53 was 1094:a2a4b6462f53, checked in by István Váradi <ivaradi@…>, 10 months ago

The taxi sounds can be started on pushback (re #367)

File size: 38.1 KB
Line 
1
2from .common import *
3
4from mlx.i18n import xstr
5import mlx.const as const
6import mlx.config as config
7
8import urllib.parse
9
10#------------------------------------------------------------------------------
11
12## @package mlx.gui.prefs
13#
14# The preferences dialog.
15#
16# This module implements the preferences dialog, allowing the editing of the
17# configuration of the program. The preferences are grouped into tabs, each
18# containing the necessary controls to set the various options.
19
20#------------------------------------------------------------------------------
21
22class Hotkey(Gtk.HBox):
23 """A widget to handle a hotkey."""
24
25 # Constant to denote that the status of the Ctrl modifier is changed
26 CHANGED_CTRL = 1
27
28 # Constant to denote that the status of the Shift modifier is changed
29 CHANGED_SHIFT = 2
30
31 # Constant to denote that the value of the key is changed
32 CHANGED_KEY = 3
33
34 def __init__(self, labelText, tooltips):
35 """Construct the hotkey widget.
36
37 labelText is the text for the label before the hotkey.
38
39 The tooltips parameter is an array of the tooltips for:
40 - the hotkey combo box,
41 - the control check box, and
42 - the shift check box."""
43 super(Hotkey, self).__init__()
44
45 label = Gtk.Label(labelText)
46 label.set_use_underline(True)
47 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
48 xscale = 0.0, yscale = 0.0)
49 labelAlignment.set_padding(padding_top = 0, padding_bottom = 0,
50 padding_left = 0, padding_right = 4)
51 labelAlignment.add(label)
52 self.pack_start(labelAlignment, False, False, 0)
53
54 self._ctrl = Gtk.CheckButton("Ctrl")
55 self._ctrl.set_tooltip_text(tooltips[1])
56 self._ctrl.connect("toggled", self._ctrlToggled)
57 self.pack_start(self._ctrl, False, False, 4)
58
59 self._shift = Gtk.CheckButton("Shift")
60 self._shift.set_tooltip_text(tooltips[2])
61 self._shift.connect("toggled", self._shiftToggled)
62 self.pack_start(self._shift, False, False, 4)
63
64 self._hotkeyModel = Gtk.ListStore(str)
65 for keyCode in list(range(ord("0"), ord("9")+1)) + list(range(ord("A"), ord("Z")+1)):
66 self._hotkeyModel.append([chr(keyCode)])
67
68 self._hotkey = Gtk.ComboBox(model = self._hotkeyModel)
69 cell = Gtk.CellRendererText()
70 self._hotkey.pack_start(cell, True)
71 self._hotkey.add_attribute(cell, 'text', 0)
72 self._hotkey.set_tooltip_text(tooltips[0])
73 self._hotkey.connect("changed", self._keyChanged)
74 self.pack_start(self._hotkey, False, False, 4)
75
76 label.set_mnemonic_widget(self._hotkey)
77
78 self._setting = False
79
80 @property
81 def ctrl(self):
82 """Get whether the Ctrl modifier is selected."""
83 return self._ctrl.get_active()
84
85 @ctrl.setter
86 def ctrl(self, ctrl):
87 """Get whether the Ctrl modifier is selected."""
88 self._setting = True
89 self._ctrl.set_active(ctrl)
90 self._setting = False
91
92 @property
93 def shift(self):
94 """Get whether the Shift modifier is selected."""
95 return self._shift.get_active()
96
97 @shift.setter
98 def shift(self, shift):
99 """Get whether the Shift modifier is selected."""
100 self._setting = True
101 self._shift.set_active(shift)
102 self._setting = False
103
104 @property
105 def key(self):
106 """Get the value of the key."""
107 return self._hotkeyModel.get_value(self._hotkey.get_active_iter(), 0)
108
109 @key.setter
110 def key(self, key):
111 """Set the value of the key."""
112 self._setting = True
113
114 hotkeyModel = self._hotkeyModel
115 iter = hotkeyModel.get_iter_first()
116 while iter is not None and \
117 hotkeyModel.get_value(iter, 0)!=key:
118 iter = hotkeyModel.iter_next(iter)
119
120 if iter is None:
121 iter = hotkeyModel.get_iter_first()
122
123 self._hotkey.set_active_iter(iter)
124
125 self._setting = False
126
127 def set(self, hotkey):
128 """Set the hotkey widget from the given hotkey."""
129 self.ctrl = hotkey.ctrl
130 self.shift = hotkey.shift
131 self.key = hotkey.key
132
133 def get(self):
134 """Get a hotkey corresponding to the settings in the widghet."""
135
136 key = self._hotkeyModel.get_value(self._hotkey.get_active_iter(), 0)
137
138 return config.Hotkey(ctrl = self.ctrl, shift = self.shift,
139 key = self.key)
140
141 def _ctrlToggled(self, checkButton):
142 """Called when the status of the Ctrl modifier has changed."""
143 if not self._setting:
144 self.emit("hotkey-changed", Hotkey.CHANGED_CTRL)
145
146 def _shiftToggled(self, checkButton):
147 """Called when the status of the Shift modifier has changed."""
148 if not self._setting:
149 self.emit("hotkey-changed", Hotkey.CHANGED_SHIFT)
150
151 def _keyChanged(self, comboBox):
152 """Called when the value of the key has changed."""
153 if not self._setting:
154 self.emit("hotkey-changed", Hotkey.CHANGED_KEY)
155
156 def __eq__(self, other):
157 """Determine if the two hotkeys are equal."""
158 return self.ctrl==other.ctrl and self.shift==other.shift and \
159 self.key==other.key
160
161#------------------------------------------------------------------------------
162
163GObject.signal_new("hotkey-changed", Hotkey, GObject.SIGNAL_RUN_FIRST,
164 None, (int,))
165
166#------------------------------------------------------------------------------
167
168class Preferences(Gtk.Dialog):
169 """The preferences dialog."""
170 def __init__(self, gui):
171 """Construct the dialog."""
172 super(Preferences, self).__init__(WINDOW_TITLE_BASE + " " +
173 xstr("prefs_title"),
174 gui.mainWindow,
175 Gtk.DialogFlags.MODAL)
176
177 self.add_button(xstr("button_cancel"), Gtk.ResponseType.REJECT)
178 self.add_button(xstr("button_ok"), Gtk.ResponseType.ACCEPT)
179 self.set_resizable(False)
180
181 self._gui = gui
182 self._settingFromConfig = False
183
184 contentArea = self.get_content_area()
185
186 notebook = Gtk.Notebook()
187 contentArea.pack_start(notebook, True, True, 4)
188
189 general = self._buildGeneral()
190 label = Gtk.Label(xstr("prefs_tab_general"))
191 label.set_use_underline(True)
192 label.set_tooltip_text(xstr("prefs_tab_general_tooltip"))
193 notebook.append_page(general, label)
194
195 messages = self._buildMessages()
196 label = Gtk.Label(xstr("prefs_tab_messages"))
197 label.set_use_underline(True)
198 label.set_tooltip_text(xstr("prefs_tab_message_tooltip"))
199 notebook.append_page(messages, label)
200
201 sounds = self._buildSounds()
202 label = Gtk.Label(xstr("prefs_tab_sounds"))
203 label.set_use_underline(True)
204 label.set_tooltip_text(xstr("prefs_tab_sounds_tooltip"))
205 notebook.append_page(sounds, label)
206
207 advanced = self._buildAdvanced()
208 label = Gtk.Label(xstr("prefs_tab_advanced"))
209 label.set_use_underline(True)
210 label.set_tooltip_text(xstr("prefs_tab_advanced_tooltip"))
211 notebook.append_page(advanced, label)
212
213 def run(self, config):
214 """Run the preferences dialog.
215
216 The dialog will be set up from data in the given configuration. If the
217 changes are accepted by the user, the configuration is updated and saved."""
218 self._fromConfig(config)
219
220 self.show_all()
221 response = super(Preferences, self).run()
222 self.hide()
223
224 if response==Gtk.ResponseType.ACCEPT:
225 self._toConfig(config)
226 config.save()
227
228 def _fromConfig(self, config):
229 """Setup the dialog from the given configuration."""
230 self._settingFromConfig = True
231
232 self._setLanguage(config.language)
233 self._hideMinimizedWindow.set_active(config.hideMinimizedWindow)
234 self._quitOnClose.set_active(config.quitOnClose)
235 self._onlineGateSystem.set_active(config.onlineGateSystem)
236 self._onlineACARS.set_active(config.onlineACARS)
237 self._flareTimeFromFS.set_active(config.flareTimeFromFS)
238 self._syncFSTime.set_active(config.syncFSTime)
239 self._usingFS2Crew.set_active(config.usingFS2Crew)
240
241 self._setSmoothing(self._iasSmoothingEnabled, self._iasSmoothingLength,
242 config.iasSmoothingLength)
243 self._setSmoothing(self._vsSmoothingEnabled, self._vsSmoothingLength,
244 config.vsSmoothingLength)
245
246 self._useSimBrief.set_active(config.useSimBrief)
247
248 pirepDirectory = config.pirepDirectory
249 self._pirepDirectory.set_text("" if pirepDirectory is None
250 else pirepDirectory)
251 self._pirepAutoSave.set_active(config.pirepAutoSave)
252 if not pirepDirectory:
253 self._pirepAutoSave.set_sensitive(False)
254
255 for messageType in const.messageTypes:
256 level = config.getMessageTypeLevel(messageType)
257 button = self._msgFSCheckButtons[messageType]
258 button.set_active(level == const.MESSAGELEVEL_FS or
259 level == const.MESSAGELEVEL_BOTH)
260 button = self._msgSoundCheckButtons[messageType]
261 button.set_active(level == const.MESSAGELEVEL_SOUND or
262 level == const.MESSAGELEVEL_BOTH)
263
264 self._enableSounds.set_active(config.enableSounds)
265 self._pilotControlsSounds.set_active(config.pilotControlsSounds)
266 self._pilotHotkey.set(config.pilotHotkey)
267 self._enableApproachCallouts.set_active(config.enableApproachCallouts)
268 self._speedbrakeAtTD.set_active(config.speedbrakeAtTD)
269
270 self._enableChecklists.set_active(config.enableChecklists)
271 self._checklistHotkey.set(config.checklistHotkey)
272
273 self._taxiSoundOnPushback.set_active(config.taxiSoundOnPushback)
274
275 self._autoUpdate.set_active(config.autoUpdate)
276 if not config.autoUpdate:
277 self._warnedAutoUpdate = True
278
279 self._updateURL.set_text(config.updateURL)
280
281 self._xplaneRemote.set_active(config.xplaneRemote)
282 self._xplaneAddress.set_text(config.xplaneAddress)
283
284 self._settingFromConfig = False
285
286 def _toConfig(self, config):
287 """Setup the given config from the settings in the dialog."""
288 config.language = self._getLanguage()
289 config.hideMinimizedWindow = self._hideMinimizedWindow.get_active()
290 config.quitOnClose = self._quitOnClose.get_active()
291 config.onlineGateSystem = self._onlineGateSystem.get_active()
292 config.onlineACARS = self._onlineACARS.get_active()
293 config.flareTimeFromFS = self._flareTimeFromFS.get_active()
294 config.syncFSTime = self._syncFSTime.get_active()
295 config.usingFS2Crew = self._usingFS2Crew.get_active()
296 config.iasSmoothingLength = self._getSmoothing(self._iasSmoothingEnabled,
297 self._iasSmoothingLength)
298 config.vsSmoothingLength = self._getSmoothing(self._vsSmoothingEnabled,
299 self._vsSmoothingLength)
300 config.useSimBrief = self._useSimBrief.get_active()
301 config.pirepDirectory = self._pirepDirectory.get_text()
302 config.pirepAutoSave = self._pirepAutoSave.get_active()
303
304 for messageType in const.messageTypes:
305 fsButtonActive = self._msgFSCheckButtons[messageType].get_active()
306 soundButtonActive = self._msgSoundCheckButtons[messageType].get_active()
307 if fsButtonActive:
308 level = const.MESSAGELEVEL_BOTH if soundButtonActive \
309 else const.MESSAGELEVEL_FS
310 elif soundButtonActive:
311 level = const.MESSAGELEVEL_SOUND
312 else:
313 level = const.MESSAGELEVEL_NONE
314 config.setMessageTypeLevel(messageType, level)
315
316 config.enableSounds = self._enableSounds.get_active()
317 config.pilotControlsSounds = self._pilotControlsSounds.get_active()
318 config.pilotHotkey = self._pilotHotkey.get()
319 config.enableApproachCallouts = self._enableApproachCallouts.get_active()
320 config.speedbrakeAtTD = self._speedbrakeAtTD.get_active()
321
322 config.enableChecklists = self._enableChecklists.get_active()
323 config.checklistHotkey = self._checklistHotkey.get()
324
325 config.taxiSoundOnPushback = self._taxiSoundOnPushback.get_active()
326
327 config.autoUpdate = self._autoUpdate.get_active()
328 config.updateURL = self._updateURL.get_text()
329
330 config.xplaneRemote = self._xplaneRemote.get_active()
331 config.xplaneAddress = self._xplaneAddress.get_text()
332
333 def _buildGeneral(self):
334 """Build the page for the general settings."""
335 mainAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
336 xscale = 1.0, yscale = 0.0)
337 mainAlignment.set_padding(padding_top = 0, padding_bottom = 8,
338 padding_left = 4, padding_right = 4)
339 mainBox = Gtk.VBox()
340 mainAlignment.add(mainBox)
341
342 guiBox = self._createFrame(mainBox, xstr("prefs_frame_gui"))
343
344 languageBox = Gtk.HBox()
345 guiBox.pack_start(languageBox, False, False, 4)
346
347 label = Gtk.Label(xstr("prefs_language"))
348 label.set_use_underline(True)
349
350 languageBox.pack_start(label, False, False, 4)
351
352 self._languageList = Gtk.ListStore(str, str)
353 for language in const.languages:
354 self._languageList.append([xstr("prefs_lang_" + language),
355 language])
356
357 self._languageComboBox = languageComboBox = \
358 Gtk.ComboBox(model = self._languageList)
359 cell = Gtk.CellRendererText()
360 languageComboBox.pack_start(cell, True)
361 languageComboBox.add_attribute(cell, 'text', 0)
362 languageComboBox.set_tooltip_text(xstr("prefs_language_tooltip"))
363 languageComboBox.connect("changed", self._languageChanged)
364 languageBox.pack_start(languageComboBox, False, False, 4)
365
366 label.set_mnemonic_widget(languageComboBox)
367
368 self._changingLanguage = False
369 self._warnedRestartNeeded = False
370
371 self._hideMinimizedWindow = Gtk.CheckButton(xstr("prefs_hideMinimizedWindow"))
372 self._hideMinimizedWindow.set_use_underline(True)
373 self._hideMinimizedWindow.set_tooltip_text(xstr("prefs_hideMinimizedWindow_tooltip"))
374 guiBox.pack_start(self._hideMinimizedWindow, False, False, 4)
375
376 self._quitOnClose = Gtk.CheckButton(xstr("prefs_quitOnClose"))
377 self._quitOnClose.set_use_underline(True)
378 self._quitOnClose.set_tooltip_text(xstr("prefs_quitOnClose_tooltip"))
379 guiBox.pack_start(self._quitOnClose, False, False, 4)
380
381 onlineBox = self._createFrame(mainBox, xstr("prefs_frame_online"))
382
383 self._onlineGateSystem = Gtk.CheckButton(xstr("prefs_onlineGateSystem"))
384 self._onlineGateSystem.set_use_underline(True)
385 self._onlineGateSystem.set_tooltip_text(xstr("prefs_onlineGateSystem_tooltip"))
386 onlineBox.pack_start(self._onlineGateSystem, False, False, 4)
387
388 self._onlineACARS = Gtk.CheckButton(xstr("prefs_onlineACARS"))
389 self._onlineACARS.set_use_underline(True)
390 self._onlineACARS.set_tooltip_text(xstr("prefs_onlineACARS_tooltip"))
391 onlineBox.pack_start(self._onlineACARS, False, False, 4)
392
393 simulatorBox = self._createFrame(mainBox, xstr("prefs_frame_simulator"))
394
395 self._flareTimeFromFS = Gtk.CheckButton(xstr("prefs_flaretimeFromFS"))
396 self._flareTimeFromFS.set_use_underline(True)
397 self._flareTimeFromFS.set_tooltip_text(xstr("prefs_flaretimeFromFS_tooltip"))
398 simulatorBox.pack_start(self._flareTimeFromFS, False, False, 4)
399
400 self._syncFSTime = Gtk.CheckButton(xstr("prefs_syncFSTime"))
401 self._syncFSTime.set_use_underline(True)
402 self._syncFSTime.set_tooltip_text(xstr("prefs_syncFSTime_tooltip"))
403 simulatorBox.pack_start(self._syncFSTime, False, False, 4)
404
405 self._usingFS2Crew = Gtk.CheckButton(xstr("prefs_usingFS2Crew"))
406 self._usingFS2Crew.set_use_underline(True)
407 self._usingFS2Crew.set_tooltip_text(xstr("prefs_usingFS2Crew_tooltip"))
408 simulatorBox.pack_start(self._usingFS2Crew, False, False, 4)
409
410 (iasSmoothingBox, self._iasSmoothingEnabled,
411 self._iasSmoothingLength) = \
412 self._createSmoothingBox(xstr("prefs_iasSmoothingEnabled"),
413 xstr("prefs_iasSmoothingEnabledTooltip"))
414 simulatorBox.pack_start(iasSmoothingBox, False, False, 4)
415
416 (vsSmoothingBox, self._vsSmoothingEnabled,
417 self._vsSmoothingLength) = \
418 self._createSmoothingBox(xstr("prefs_vsSmoothingEnabled"),
419 xstr("prefs_vsSmoothingEnabledTooltip"))
420 simulatorBox.pack_start(vsSmoothingBox, False, False, 4)
421
422 self._useSimBrief = Gtk.CheckButton(xstr("prefs_useSimBrief"))
423 self._useSimBrief.set_use_underline(True)
424 self._useSimBrief.set_tooltip_text(xstr("prefs_useSimBrief_tooltip"))
425 mainBox.pack_start(self._useSimBrief, False, False, 0)
426
427 pirepBox = Gtk.HBox()
428 mainBox.pack_start(pirepBox, False, False, 8)
429
430 label = Gtk.Label(xstr("prefs_pirepDirectory"))
431 label.set_use_underline(True)
432 pirepBox.pack_start(label, False, False, 4)
433
434 self._pirepDirectory = Gtk.Entry()
435 self._pirepDirectory.set_tooltip_text(xstr("prefs_pirepDirectory_tooltip"))
436 self._pirepDirectory.connect("changed", self._pirepDirectoryChanged)
437 label.set_mnemonic_widget(self._pirepDirectory)
438 pirepBox.pack_start(self._pirepDirectory, True, True, 4)
439
440 self._pirepDirectoryButton = Gtk.Button(xstr("button_browse"))
441 self._pirepDirectoryButton.connect("clicked",
442 self._pirepDirectoryButtonClicked)
443 pirepBox.pack_start(self._pirepDirectoryButton, False, False, 4)
444
445 self._pirepAutoSave = Gtk.CheckButton(xstr("prefs_pirepAutoSave"))
446 self._pirepAutoSave.set_use_underline(True)
447 self._pirepAutoSave.set_tooltip_text(xstr("prefs_pirepAutoSave_tooltip"))
448 mainBox.pack_start(self._pirepAutoSave, False, False, 0)
449
450 return mainAlignment
451
452 def _createFrame(self, mainBox, label):
453 """Create a frame with an inner alignment and VBox.
454
455 Return the vbox."""
456 frame = Gtk.Frame(label = label)
457 mainBox.pack_start(frame, False, False, 4)
458 alignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
459 xscale = 1.0, yscale = 0.0)
460 alignment.set_padding(padding_top = 4, padding_bottom = 0,
461 padding_left = 0, padding_right = 0)
462 frame.add(alignment)
463 vbox = Gtk.VBox()
464 alignment.add(vbox)
465
466 return vbox
467
468 def _createSmoothingBox(self, checkBoxLabel, checkBoxTooltip,
469 maxSeconds = 10):
470 """Create a HBox that contains entry fields for smoothing some value."""
471 smoothingBox = Gtk.HBox()
472
473 smoothingEnabled = Gtk.CheckButton(checkBoxLabel)
474 smoothingEnabled.set_use_underline(True)
475 smoothingEnabled.set_tooltip_text(checkBoxTooltip)
476
477 smoothingBox.pack_start(smoothingEnabled, False, False, 0)
478
479 smoothingLength = Gtk.SpinButton()
480 smoothingLength.set_numeric(True)
481 smoothingLength.set_range(2, maxSeconds)
482 smoothingLength.set_increments(1, 1)
483 smoothingLength.set_alignment(1.0)
484 smoothingLength.set_width_chars(2)
485
486 smoothingBox.pack_start(smoothingLength, False, False, 0)
487
488 smoothingBox.pack_start(Gtk.Label(xstr("prefs_smoothing_seconds")),
489 False, False, 4)
490
491 smoothingEnabled.connect("toggled", self._smoothingToggled,
492 smoothingLength)
493 smoothingLength.set_sensitive(False)
494
495 return (smoothingBox, smoothingEnabled, smoothingLength)
496
497 def _setLanguage(self, language):
498 """Set the language to the given one."""
499 iter = self._languageList.get_iter_first()
500 while iter is not None:
501 (lang,) = self._languageList.get(iter, 1)
502 if (not language and lang=="$system") or \
503 lang==language:
504 self._changingLanguage = True
505 self._languageComboBox.set_active_iter(iter)
506 self._changingLanguage = False
507 break
508 else:
509 iter = self._languageList.iter_next(iter)
510
511 def _getLanguage(self):
512 """Get the language selected by the user."""
513 iter = self._languageComboBox.get_active_iter()
514 (lang,) = self._languageList.get(iter, 1)
515 return "" if lang=="$system" else lang
516
517 def _languageChanged(self, comboBox):
518 """Called when the language has changed."""
519 if not self._changingLanguage and not self._warnedRestartNeeded:
520 dialog = Gtk.MessageDialog(parent = self,
521 type = Gtk.MessageType.INFO,
522 message_format = xstr("prefs_restart"))
523 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
524 dialog.set_title(self.get_title())
525 dialog.format_secondary_markup(xstr("prefs_language_restart_sec"))
526 dialog.run()
527 dialog.hide()
528 self._warnedRestartNeeded = True
529
530 def _smoothingToggled(self, smoothingEnabled, smoothingLength):
531 """Called when a smoothing enabled check box is toggled."""
532 sensitive = smoothingEnabled.get_active()
533 smoothingLength.set_sensitive(sensitive)
534 if sensitive:
535 smoothingLength.grab_focus()
536
537 def _setSmoothing(self, smoothingEnabled, smoothingLength, smoothing):
538 """Set the smoothing controls from the given value.
539
540 If the value is less than 2, smoothing is disabled. The smoothing
541 length is the absolute value of the value."""
542 smoothingEnabled.set_active(smoothing>=2)
543 smoothingLength.set_value(abs(smoothing))
544
545 def _getSmoothing(self, smoothingEnabled, smoothingLength):
546 """Get the smoothing value from the given controls.
547
548 The returned value is the value of smoothingLength multiplied by -1, if
549 smoothing is disabled."""
550 value = smoothingLength.get_value_as_int()
551 if not smoothingEnabled.get_active():
552 value *= -1
553 return value
554
555 def _pirepDirectoryButtonClicked(self, button):
556 """Called when the PIREP directory button is clicked."""
557 dialog = Gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
558 xstr("prefs_pirepDirectory_browser_title"),
559 action = Gtk.FileChooserAction.SELECT_FOLDER,
560 buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
561 Gtk.STOCK_OK, Gtk.ResponseType.OK),
562 parent = self)
563 dialog.set_modal(True)
564 dialog.set_transient_for(self)
565
566 directory = self._pirepDirectory.get_text()
567 if directory:
568 dialog.select_filename(directory)
569
570 result = dialog.run()
571 dialog.hide()
572
573 if result==Gtk.ResponseType.OK:
574 self._pirepDirectory.set_text(dialog.get_filename())
575
576 def _pirepDirectoryChanged(self, entry):
577 """Called when the PIREP directory is changed."""
578 if self._pirepDirectory.get_text():
579 self._pirepAutoSave.set_sensitive(True)
580 else:
581 self._pirepAutoSave.set_active(False)
582 self._pirepAutoSave.set_sensitive(False)
583
584 def _buildMessages(self):
585 """Build the page for the message settings."""
586
587 mainAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
588 xscale = 0.0, yscale = 0.0)
589 mainAlignment.set_padding(padding_top = 16, padding_bottom = 8,
590 padding_left = 4, padding_right = 4)
591 mainBox = Gtk.VBox()
592 mainAlignment.add(mainBox)
593
594 table = Gtk.Table(len(const.messageTypes) + 1, 3)
595 table.set_row_spacings(8)
596 table.set_col_spacings(32)
597 table.set_homogeneous(False)
598 mainBox.pack_start(table, False, False, 4)
599
600 label = Gtk.Label(xstr("prefs_msgs_fs"))
601 label.set_justify(Gtk.Justification.CENTER)
602 label.set_alignment(0.5, 1.0)
603 table.attach(label, 1, 2, 0, 1)
604
605 label = Gtk.Label(xstr("prefs_msgs_sound"))
606 label.set_justify(Gtk.Justification.CENTER)
607 label.set_alignment(0.5, 1.0)
608 table.attach(label, 2, 3, 0, 1)
609
610 self._msgFSCheckButtons = {}
611 self._msgSoundCheckButtons = {}
612 row = 1
613 for messageType in const.messageTypes:
614 messageTypeStr = const.messageType2string(messageType)
615 label = Gtk.Label(xstr("prefs_msgs_type_" + messageTypeStr))
616 label.set_justify(Gtk.Justification.CENTER)
617 label.set_use_underline(True)
618 label.set_alignment(0.5, 0.5)
619 table.attach(label, 0, 1, row, row+1)
620
621 fsCheckButton = Gtk.CheckButton()
622 alignment = Gtk.Alignment(xscale = 0.0, yscale = 0.0,
623 xalign = 0.5, yalign = 0.5)
624 alignment.add(fsCheckButton)
625 table.attach(alignment, 1, 2, row, row+1)
626 self._msgFSCheckButtons[messageType] = fsCheckButton
627
628 soundCheckButton = Gtk.CheckButton()
629 alignment = Gtk.Alignment(xscale = 0.0, yscale = 0.0,
630 xalign = 0.5, yalign = 0.5)
631 alignment.add(soundCheckButton)
632 table.attach(alignment, 2, 3, row, row+1)
633 self._msgSoundCheckButtons[messageType] = soundCheckButton
634
635 mnemonicWidget = Gtk.Label("")
636 table.attach(mnemonicWidget, 3, 4, row, row+1)
637 label.set_mnemonic_widget(mnemonicWidget)
638 mnemonicWidget.connect("mnemonic-activate",
639 self._msgLabelActivated,
640 messageType)
641
642 row += 1
643
644 return mainAlignment
645
646 def _msgLabelActivated(self, button, cycle_group, messageType):
647 """Called when the mnemonic of a label is activated.
648
649 It cycles the corresponding options."""
650 fsCheckButton = self._msgFSCheckButtons[messageType]
651 soundCheckButton = self._msgSoundCheckButtons[messageType]
652
653 num = 1 if fsCheckButton.get_active() else 0
654 num += 2 if soundCheckButton.get_active() else 0
655 num += 1
656
657 fsCheckButton.set_active((num&0x01)==0x01)
658 soundCheckButton.set_active((num&0x02)==0x02)
659
660 return True
661
662 def _buildSounds(self):
663 """Build the page for the sounds."""
664 mainAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
665 xscale = 1.0, yscale = 1.0)
666 mainAlignment.set_padding(padding_top = 8, padding_bottom = 8,
667 padding_left = 4, padding_right = 4)
668
669 mainBox = Gtk.VBox()
670 mainAlignment.add(mainBox)
671
672 backgroundFrame = Gtk.Frame(label = xstr("prefs_sounds_frame_bg"))
673 mainBox.pack_start(backgroundFrame, False, False, 4)
674
675 backgroundAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
676 xscale = 1.0, yscale = 0.0)
677 backgroundAlignment.set_padding(padding_top = 4, padding_bottom = 4,
678 padding_left = 4, padding_right = 4)
679 backgroundFrame.add(backgroundAlignment)
680
681 backgroundBox = Gtk.VBox()
682 backgroundAlignment.add(backgroundBox)
683
684 self._enableSounds = Gtk.CheckButton(xstr("prefs_sounds_enable"))
685 self._enableSounds.set_use_underline(True)
686 self._enableSounds.set_tooltip_text(xstr("prefs_sounds_enable_tooltip"))
687 self._enableSounds.connect("toggled", self._enableSoundsToggled)
688 alignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
689 xscale = 1.0, yscale = 0.0)
690 alignment.add(self._enableSounds)
691 backgroundBox.pack_start(alignment, False, False, 4)
692
693 self._pilotControlsSounds = Gtk.CheckButton(xstr("prefs_sounds_pilotControls"))
694 self._pilotControlsSounds.set_use_underline(True)
695 self._pilotControlsSounds.set_tooltip_text(xstr("prefs_sounds_pilotControls_tooltip"))
696 self._pilotControlsSounds.connect("toggled", self._pilotControlsSoundsToggled)
697 backgroundBox.pack_start(self._pilotControlsSounds, False, False, 4)
698
699 self._pilotHotkey = Hotkey(xstr("prefs_sounds_pilotHotkey"),
700 [xstr("prefs_sounds_pilotHotkey_tooltip"),
701 xstr("prefs_sounds_pilotHotkeyCtrl_tooltip"),
702 xstr("prefs_sounds_pilotHotkeyShift_tooltip")])
703
704 backgroundBox.pack_start(self._pilotHotkey, False, False, 4)
705
706 self._taxiSoundOnPushback = Gtk.CheckButton(xstr("prefs_sounds_taxiSoundOnPushback"))
707 self._taxiSoundOnPushback.set_use_underline(True)
708 self._taxiSoundOnPushback.set_tooltip_text(xstr("prefs_sounds_taxiSoundOnPushback_tooltip"))
709 backgroundBox.pack_start(self._taxiSoundOnPushback, False, False, 4)
710
711 self._enableApproachCallouts = Gtk.CheckButton(xstr("prefs_sounds_approachCallouts"))
712 self._enableApproachCallouts.set_use_underline(True)
713 self._enableApproachCallouts.set_tooltip_text(xstr("prefs_sounds_approachCallouts_tooltip"))
714 backgroundBox.pack_start(self._enableApproachCallouts, False, False, 4)
715
716 self._speedbrakeAtTD = Gtk.CheckButton(xstr("prefs_sounds_speedbrakeAtTD"))
717 self._speedbrakeAtTD.set_use_underline(True)
718 self._speedbrakeAtTD.set_tooltip_text(xstr("prefs_sounds_speedbrakeAtTD_tooltip"))
719 backgroundBox.pack_start(self._speedbrakeAtTD, False, False, 4)
720
721 checklistFrame = Gtk.Frame(label = xstr("prefs_sounds_frame_checklists"))
722 mainBox.pack_start(checklistFrame, False, False, 4)
723
724 checklistAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
725 xscale = 1.0, yscale = 0.0)
726 checklistAlignment.set_padding(padding_top = 4, padding_bottom = 4,
727 padding_left = 4, padding_right = 4)
728 checklistFrame.add(checklistAlignment)
729
730 checklistBox = Gtk.VBox()
731 checklistAlignment.add(checklistBox)
732
733 self._enableChecklists = Gtk.CheckButton(xstr("prefs_sounds_enableChecklists"))
734 self._enableChecklists.set_use_underline(True)
735 self._enableChecklists.set_tooltip_text(xstr("prefs_sounds_enableChecklists_tooltip"))
736 self._enableChecklists.connect("toggled", self._enableChecklistsToggled)
737 checklistBox.pack_start(self._enableChecklists, False, False, 4)
738
739 self._checklistHotkey = Hotkey(xstr("prefs_sounds_checklistHotkey"),
740 [xstr("prefs_sounds_checklistHotkey_tooltip"),
741 xstr("prefs_sounds_checklistHotkeyCtrl_tooltip"),
742 xstr("prefs_sounds_checklistHotkeyShift_tooltip")])
743
744 checklistBox.pack_start(self._checklistHotkey, False, False, 4)
745
746 self._enableSoundsToggled(self._enableSounds)
747 self._enableChecklistsToggled(self._enableChecklists)
748
749 self._pilotHotkey.connect("hotkey-changed", self._reconcileHotkeys,
750 self._checklistHotkey)
751 self._checklistHotkey.connect("hotkey-changed", self._reconcileHotkeys,
752 self._pilotHotkey)
753
754 return mainAlignment
755
756 def _enableSoundsToggled(self, button):
757 """Called when the enable sounds button is toggled."""
758 active = button.get_active()
759 self._pilotControlsSounds.set_sensitive(active)
760 self._pilotControlsSoundsToggled(self._pilotControlsSounds)
761 self._taxiSoundOnPushback.set_sensitive(active)
762 self._enableApproachCallouts.set_sensitive(active)
763 self._speedbrakeAtTD.set_sensitive(active)
764
765 def _pilotControlsSoundsToggled(self, button):
766 """Called when the enable sounds button is toggled."""
767 active = button.get_active() and self._enableSounds.get_active()
768 self._pilotHotkey.set_sensitive(active)
769 if active and self._checklistHotkey.get_sensitive():
770 self._reconcileHotkeys(self._checklistHotkey, Hotkey.CHANGED_SHIFT,
771 self._pilotHotkey)
772
773 def _enableChecklistsToggled(self, button):
774 """Called when the enable checklists button is toggled."""
775 active = button.get_active()
776 self._checklistHotkey.set_sensitive(active)
777 if active and self._pilotHotkey.get_sensitive():
778 self._reconcileHotkeys(self._pilotHotkey, Hotkey.CHANGED_SHIFT,
779 self._checklistHotkey)
780
781 def _reconcileHotkeys(self, changedHotkey, what, otherHotkey):
782 """Reconcile the given hotkeys so that they are different.
783
784 changedHotkey is the hotkey that has changed. what is one of the
785 Hotkey.CHANGED_XXX constants denoting what has changed. otherHotkey is
786 the other hotkey that must be reconciled.
787
788 If the other hotkey is not sensitive or is not equal to the changed
789 one, nothing happens.
790
791 Otherwise, if the status of the Ctrl modifier has changed, the status
792 of the Ctrl modifier on the other hotkey will be negated. Similarly, if
793 the Shift modifier has changed. If the key has changed, the Shift
794 modifier is negated in the other hotkey."""
795 if otherHotkey.get_sensitive() and changedHotkey==otherHotkey:
796 if what==Hotkey.CHANGED_CTRL:
797 otherHotkey.ctrl = not changedHotkey.ctrl
798 elif what==Hotkey.CHANGED_SHIFT or what==Hotkey.CHANGED_KEY:
799 otherHotkey.shift = not changedHotkey.shift
800
801 def _buildAdvanced(self):
802 """Build the page for the advanced settings."""
803
804 mainAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
805 xscale = 1.0, yscale = 0.0)
806 mainAlignment.set_padding(padding_top = 16, padding_bottom = 8,
807 padding_left = 4, padding_right = 4)
808 mainBox = Gtk.VBox()
809 mainAlignment.add(mainBox)
810
811 frame = Gtk.Frame.new()
812
813 self._autoUpdate = Gtk.CheckButton(xstr("prefs_update_auto"))
814 frame.set_label_widget(self._autoUpdate)
815 frame.set_label_align(0.025, 0.5)
816 mainBox.pack_start(frame, False, False, 4)
817
818 self._autoUpdate.set_use_underline(True)
819 self._autoUpdate.connect("toggled", self._autoUpdateToggled)
820 self._autoUpdate.set_tooltip_text(xstr("prefs_update_auto_tooltip"))
821 self._warnedAutoUpdate = False
822
823 updateURLBox = Gtk.HBox()
824 label = Gtk.Label(xstr("prefs_update_url"))
825 label.set_use_underline(True)
826 updateURLBox.pack_start(label, False, False, 4)
827
828 self._updateURL = Gtk.Entry()
829 label.set_mnemonic_widget(self._updateURL)
830 self._updateURL.set_width_chars(40)
831 self._updateURL.set_tooltip_text(xstr("prefs_update_url_tooltip"))
832 self._updateURL.connect("changed", self._updateURLChanged)
833 updateURLBox.pack_start(self._updateURL, True, True, 4)
834
835 updateURLBox.set_margin_top(6)
836 updateURLBox.set_margin_bottom(6)
837 updateURLBox.set_margin_left(4)
838 updateURLBox.set_margin_right(4)
839 frame.add(updateURLBox)
840
841 frame = Gtk.Frame.new()
842 self._xplaneRemote = Gtk.CheckButton(xstr("prefs_xplane_remote"))
843 frame.set_label_widget(self._xplaneRemote)
844 frame.set_label_align(0.025, 0.5)
845 mainBox.pack_start(frame, False, False, 4)
846
847 self._xplaneRemote.set_use_underline(True)
848 self._xplaneRemote.set_tooltip_text(xstr("prefs_xplane_remote_tooltip"))
849 self._xplaneRemote.connect("toggled", self._xplaneRemoteToggled)
850
851 xplaneAddressBox = Gtk.HBox()
852 label = Gtk.Label(xstr("prefs_xplane_address"))
853 label.set_use_underline(True)
854 xplaneAddressBox.pack_start(label, False, False, 4)
855
856 self._xplaneAddress = Gtk.Entry()
857 label.set_mnemonic_widget(self._xplaneAddress)
858 self._xplaneAddress.set_width_chars(40)
859 self._xplaneAddress.set_tooltip_text(xstr("prefs_xplane_address_tooltip"))
860 self._xplaneAddress.connect("changed", self._xplaneAddressChanged)
861 xplaneAddressBox.pack_start(self._xplaneAddress, True, True, 4)
862
863 xplaneAddressBox.set_margin_top(6)
864 xplaneAddressBox.set_margin_bottom(6)
865 xplaneAddressBox.set_margin_left(4)
866 xplaneAddressBox.set_margin_right(4)
867 frame.add(xplaneAddressBox)
868
869 return mainAlignment
870
871 def _setOKButtonSensitivity(self):
872 """Set the sensitive state of the OK button."""
873 sensitive = False
874 try:
875 result = urllib.parse.urlparse(self._updateURL.get_text())
876 sensitive = result.scheme!="" and (result.netloc + result.path)!=""
877 except:
878 pass
879
880 if sensitive:
881 sensitive = not self._xplaneRemote.get_active() or \
882 len(self._xplaneAddress.get_text())>0
883
884 okButton = self.get_widget_for_response(Gtk.ResponseType.ACCEPT)
885 okButton.set_sensitive(sensitive)
886
887 def _autoUpdateToggled(self, button):
888 """Called when the auto update check button is toggled."""
889 if not self._settingFromConfig and not self._warnedAutoUpdate and \
890 not self._autoUpdate.get_active():
891 dialog = Gtk.MessageDialog(parent = self,
892 type = Gtk.MessageType.INFO,
893 message_format = xstr("prefs_update_auto_warning"))
894 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
895 dialog.set_title(self.get_title())
896 dialog.run()
897 dialog.hide()
898 self._warnedAutoUpdate = True
899
900 def _updateURLChanged(self, entry):
901 """Called when the update URL is changed."""
902 self._setOKButtonSensitivity()
903
904 def _xplaneRemoteToggled(self, button):
905 """Called when the X-Plane remote access checkbox is toggled."""
906 self._setOKButtonSensitivity()
907
908 def _xplaneAddressChanged(self, entry):
909 """Called when the X-Plane address is changed."""
910 self._setOKButtonSensitivity()
Note: See TracBrowser for help on using the repository browser.