source: src/mlx/gui/prefs.py

python3
Last change on this file was 1123:f0334593281d, checked in by István Váradi <ivaradi@…>, 5 months ago

Support for an alternative sound set

File size: 39.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._mainWindowResizable.set_active(config.mainWindowResizable)
234 self._hideMinimizedWindow.set_active(config.hideMinimizedWindow)
235 self._quitOnClose.set_active(config.quitOnClose)
236 self._onlineGateSystem.set_active(config.onlineGateSystem)
237 self._onlineACARS.set_active(config.onlineACARS)
238 self._flareTimeFromFS.set_active(config.flareTimeFromFS)
239 self._syncFSTime.set_active(config.syncFSTime)
240 self._usingFS2Crew.set_active(config.usingFS2Crew)
241
242 self._setSmoothing(self._iasSmoothingEnabled, self._iasSmoothingLength,
243 config.iasSmoothingLength)
244 self._setSmoothing(self._vsSmoothingEnabled, self._vsSmoothingLength,
245 config.vsSmoothingLength)
246
247 self._useSimBrief.set_active(config.useSimBrief)
248
249 pirepDirectory = config.pirepDirectory
250 self._pirepDirectory.set_text("" if pirepDirectory is None
251 else pirepDirectory)
252 self._pirepAutoSave.set_active(config.pirepAutoSave)
253 if not pirepDirectory:
254 self._pirepAutoSave.set_sensitive(False)
255
256 for messageType in const.messageTypes:
257 level = config.getMessageTypeLevel(messageType)
258 button = self._msgFSCheckButtons[messageType]
259 button.set_active(level == const.MESSAGELEVEL_FS or
260 level == const.MESSAGELEVEL_BOTH)
261 button = self._msgSoundCheckButtons[messageType]
262 button.set_active(level == const.MESSAGELEVEL_SOUND or
263 level == const.MESSAGELEVEL_BOTH)
264
265 self._enableSounds.set_active(config.enableSounds)
266 self._pilotControlsSounds.set_active(config.pilotControlsSounds)
267 self._pilotHotkey.set(config.pilotHotkey)
268 self._enableApproachCallouts.set_active(config.enableApproachCallouts)
269 self._speedbrakeAtTD.set_active(config.speedbrakeAtTD)
270 self._alternativeSoundSet.set_active(config.soundSet!=0)
271
272 self._enableChecklists.set_active(config.enableChecklists)
273 self._checklistHotkey.set(config.checklistHotkey)
274
275 self._taxiSoundOnPushback.set_active(config.taxiSoundOnPushback)
276
277 self._autoUpdate.set_active(config.autoUpdate)
278 if not config.autoUpdate:
279 self._warnedAutoUpdate = True
280
281 self._updateURL.set_text(config.updateURL)
282
283 self._xplaneRemote.set_active(config.xplaneRemote)
284 self._xplaneAddress.set_text(config.xplaneAddress)
285
286 self._settingFromConfig = False
287
288 def _toConfig(self, config):
289 """Setup the given config from the settings in the dialog."""
290 config.language = self._getLanguage()
291 config.mainWindowResizable = self._mainWindowResizable.get_active()
292 config.hideMinimizedWindow = self._hideMinimizedWindow.get_active()
293 config.quitOnClose = self._quitOnClose.get_active()
294 config.onlineGateSystem = self._onlineGateSystem.get_active()
295 config.onlineACARS = self._onlineACARS.get_active()
296 config.flareTimeFromFS = self._flareTimeFromFS.get_active()
297 config.syncFSTime = self._syncFSTime.get_active()
298 config.usingFS2Crew = self._usingFS2Crew.get_active()
299 config.iasSmoothingLength = self._getSmoothing(self._iasSmoothingEnabled,
300 self._iasSmoothingLength)
301 config.vsSmoothingLength = self._getSmoothing(self._vsSmoothingEnabled,
302 self._vsSmoothingLength)
303 config.useSimBrief = self._useSimBrief.get_active()
304 config.pirepDirectory = self._pirepDirectory.get_text()
305 config.pirepAutoSave = self._pirepAutoSave.get_active()
306
307 for messageType in const.messageTypes:
308 fsButtonActive = self._msgFSCheckButtons[messageType].get_active()
309 soundButtonActive = self._msgSoundCheckButtons[messageType].get_active()
310 if fsButtonActive:
311 level = const.MESSAGELEVEL_BOTH if soundButtonActive \
312 else const.MESSAGELEVEL_FS
313 elif soundButtonActive:
314 level = const.MESSAGELEVEL_SOUND
315 else:
316 level = const.MESSAGELEVEL_NONE
317 config.setMessageTypeLevel(messageType, level)
318
319 config.enableSounds = self._enableSounds.get_active()
320 config.pilotControlsSounds = self._pilotControlsSounds.get_active()
321 config.pilotHotkey = self._pilotHotkey.get()
322 config.enableApproachCallouts = self._enableApproachCallouts.get_active()
323 config.speedbrakeAtTD = self._speedbrakeAtTD.get_active()
324 config.soundSet = 1 if self._alternativeSoundSet.get_active() else 0
325
326 config.enableChecklists = self._enableChecklists.get_active()
327 config.checklistHotkey = self._checklistHotkey.get()
328
329 config.taxiSoundOnPushback = self._taxiSoundOnPushback.get_active()
330
331 config.autoUpdate = self._autoUpdate.get_active()
332 config.updateURL = self._updateURL.get_text()
333
334 config.xplaneRemote = self._xplaneRemote.get_active()
335 config.xplaneAddress = self._xplaneAddress.get_text()
336
337 def _buildGeneral(self):
338 """Build the page for the general settings."""
339 mainAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
340 xscale = 1.0, yscale = 0.0)
341 mainAlignment.set_padding(padding_top = 0, padding_bottom = 8,
342 padding_left = 4, padding_right = 4)
343 mainBox = Gtk.VBox()
344 mainAlignment.add(mainBox)
345
346 guiBox = self._createFrame(mainBox, xstr("prefs_frame_gui"))
347
348 languageBox = Gtk.HBox()
349 guiBox.pack_start(languageBox, False, False, 4)
350
351 label = Gtk.Label(xstr("prefs_language"))
352 label.set_use_underline(True)
353
354 languageBox.pack_start(label, False, False, 4)
355
356 self._languageList = Gtk.ListStore(str, str)
357 for language in const.languages:
358 self._languageList.append([xstr("prefs_lang_" + language),
359 language])
360
361 self._languageComboBox = languageComboBox = \
362 Gtk.ComboBox(model = self._languageList)
363 cell = Gtk.CellRendererText()
364 languageComboBox.pack_start(cell, True)
365 languageComboBox.add_attribute(cell, 'text', 0)
366 languageComboBox.set_tooltip_text(xstr("prefs_language_tooltip"))
367 languageComboBox.connect("changed", self._languageChanged)
368 languageBox.pack_start(languageComboBox, False, False, 4)
369
370 label.set_mnemonic_widget(languageComboBox)
371
372 self._changingLanguage = False
373 self._warnedRestartNeeded = False
374
375 self._mainWindowResizable = Gtk.CheckButton(xstr("prefs_mainWindowResizable"))
376 self._mainWindowResizable.set_use_underline(True)
377 self._mainWindowResizable.set_tooltip_text(xstr("prefs_mainWindowResizable_tooltip"))
378 guiBox.pack_start(self._mainWindowResizable, False, False, 4)
379
380 self._hideMinimizedWindow = Gtk.CheckButton(xstr("prefs_hideMinimizedWindow"))
381 self._hideMinimizedWindow.set_use_underline(True)
382 self._hideMinimizedWindow.set_tooltip_text(xstr("prefs_hideMinimizedWindow_tooltip"))
383 guiBox.pack_start(self._hideMinimizedWindow, False, False, 4)
384
385 self._quitOnClose = Gtk.CheckButton(xstr("prefs_quitOnClose"))
386 self._quitOnClose.set_use_underline(True)
387 self._quitOnClose.set_tooltip_text(xstr("prefs_quitOnClose_tooltip"))
388 guiBox.pack_start(self._quitOnClose, False, False, 4)
389
390 onlineBox = self._createFrame(mainBox, xstr("prefs_frame_online"))
391
392 self._onlineGateSystem = Gtk.CheckButton(xstr("prefs_onlineGateSystem"))
393 self._onlineGateSystem.set_use_underline(True)
394 self._onlineGateSystem.set_tooltip_text(xstr("prefs_onlineGateSystem_tooltip"))
395 onlineBox.pack_start(self._onlineGateSystem, False, False, 4)
396
397 self._onlineACARS = Gtk.CheckButton(xstr("prefs_onlineACARS"))
398 self._onlineACARS.set_use_underline(True)
399 self._onlineACARS.set_tooltip_text(xstr("prefs_onlineACARS_tooltip"))
400 onlineBox.pack_start(self._onlineACARS, False, False, 4)
401
402 simulatorBox = self._createFrame(mainBox, xstr("prefs_frame_simulator"))
403
404 self._flareTimeFromFS = Gtk.CheckButton(xstr("prefs_flaretimeFromFS"))
405 self._flareTimeFromFS.set_use_underline(True)
406 self._flareTimeFromFS.set_tooltip_text(xstr("prefs_flaretimeFromFS_tooltip"))
407 simulatorBox.pack_start(self._flareTimeFromFS, False, False, 4)
408
409 self._syncFSTime = Gtk.CheckButton(xstr("prefs_syncFSTime"))
410 self._syncFSTime.set_use_underline(True)
411 self._syncFSTime.set_tooltip_text(xstr("prefs_syncFSTime_tooltip"))
412 simulatorBox.pack_start(self._syncFSTime, False, False, 4)
413
414 self._usingFS2Crew = Gtk.CheckButton(xstr("prefs_usingFS2Crew"))
415 self._usingFS2Crew.set_use_underline(True)
416 self._usingFS2Crew.set_tooltip_text(xstr("prefs_usingFS2Crew_tooltip"))
417 simulatorBox.pack_start(self._usingFS2Crew, False, False, 4)
418
419 (iasSmoothingBox, self._iasSmoothingEnabled,
420 self._iasSmoothingLength) = \
421 self._createSmoothingBox(xstr("prefs_iasSmoothingEnabled"),
422 xstr("prefs_iasSmoothingEnabledTooltip"))
423 simulatorBox.pack_start(iasSmoothingBox, False, False, 4)
424
425 (vsSmoothingBox, self._vsSmoothingEnabled,
426 self._vsSmoothingLength) = \
427 self._createSmoothingBox(xstr("prefs_vsSmoothingEnabled"),
428 xstr("prefs_vsSmoothingEnabledTooltip"))
429 simulatorBox.pack_start(vsSmoothingBox, False, False, 4)
430
431 self._useSimBrief = Gtk.CheckButton(xstr("prefs_useSimBrief"))
432 self._useSimBrief.set_use_underline(True)
433 self._useSimBrief.set_tooltip_text(xstr("prefs_useSimBrief_tooltip"))
434 mainBox.pack_start(self._useSimBrief, False, False, 0)
435
436 pirepBox = Gtk.HBox()
437 mainBox.pack_start(pirepBox, False, False, 8)
438
439 label = Gtk.Label(xstr("prefs_pirepDirectory"))
440 label.set_use_underline(True)
441 pirepBox.pack_start(label, False, False, 4)
442
443 self._pirepDirectory = Gtk.Entry()
444 self._pirepDirectory.set_tooltip_text(xstr("prefs_pirepDirectory_tooltip"))
445 self._pirepDirectory.connect("changed", self._pirepDirectoryChanged)
446 label.set_mnemonic_widget(self._pirepDirectory)
447 pirepBox.pack_start(self._pirepDirectory, True, True, 4)
448
449 self._pirepDirectoryButton = Gtk.Button(xstr("button_browse"))
450 self._pirepDirectoryButton.connect("clicked",
451 self._pirepDirectoryButtonClicked)
452 pirepBox.pack_start(self._pirepDirectoryButton, False, False, 4)
453
454 self._pirepAutoSave = Gtk.CheckButton(xstr("prefs_pirepAutoSave"))
455 self._pirepAutoSave.set_use_underline(True)
456 self._pirepAutoSave.set_tooltip_text(xstr("prefs_pirepAutoSave_tooltip"))
457 mainBox.pack_start(self._pirepAutoSave, False, False, 0)
458
459 return mainAlignment
460
461 def _createFrame(self, mainBox, label):
462 """Create a frame with an inner alignment and VBox.
463
464 Return the vbox."""
465 frame = Gtk.Frame(label = label)
466 mainBox.pack_start(frame, False, False, 4)
467 alignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
468 xscale = 1.0, yscale = 0.0)
469 alignment.set_padding(padding_top = 4, padding_bottom = 0,
470 padding_left = 0, padding_right = 0)
471 frame.add(alignment)
472 vbox = Gtk.VBox()
473 alignment.add(vbox)
474
475 return vbox
476
477 def _createSmoothingBox(self, checkBoxLabel, checkBoxTooltip,
478 maxSeconds = 10):
479 """Create a HBox that contains entry fields for smoothing some value."""
480 smoothingBox = Gtk.HBox()
481
482 smoothingEnabled = Gtk.CheckButton(checkBoxLabel)
483 smoothingEnabled.set_use_underline(True)
484 smoothingEnabled.set_tooltip_text(checkBoxTooltip)
485
486 smoothingBox.pack_start(smoothingEnabled, False, False, 0)
487
488 smoothingLength = Gtk.SpinButton()
489 smoothingLength.set_numeric(True)
490 smoothingLength.set_range(2, maxSeconds)
491 smoothingLength.set_increments(1, 1)
492 smoothingLength.set_alignment(1.0)
493 smoothingLength.set_width_chars(2)
494
495 smoothingBox.pack_start(smoothingLength, False, False, 0)
496
497 smoothingBox.pack_start(Gtk.Label(xstr("prefs_smoothing_seconds")),
498 False, False, 4)
499
500 smoothingEnabled.connect("toggled", self._smoothingToggled,
501 smoothingLength)
502 smoothingLength.set_sensitive(False)
503
504 return (smoothingBox, smoothingEnabled, smoothingLength)
505
506 def _setLanguage(self, language):
507 """Set the language to the given one."""
508 iter = self._languageList.get_iter_first()
509 while iter is not None:
510 (lang,) = self._languageList.get(iter, 1)
511 if (not language and lang=="$system") or \
512 lang==language:
513 self._changingLanguage = True
514 self._languageComboBox.set_active_iter(iter)
515 self._changingLanguage = False
516 break
517 else:
518 iter = self._languageList.iter_next(iter)
519
520 def _getLanguage(self):
521 """Get the language selected by the user."""
522 iter = self._languageComboBox.get_active_iter()
523 (lang,) = self._languageList.get(iter, 1)
524 return "" if lang=="$system" else lang
525
526 def _languageChanged(self, comboBox):
527 """Called when the language has changed."""
528 if not self._changingLanguage and not self._warnedRestartNeeded:
529 dialog = Gtk.MessageDialog(parent = self,
530 type = Gtk.MessageType.INFO,
531 message_format = xstr("prefs_restart"))
532 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
533 dialog.set_title(self.get_title())
534 dialog.format_secondary_markup(xstr("prefs_language_restart_sec"))
535 dialog.run()
536 dialog.hide()
537 self._warnedRestartNeeded = True
538
539 def _smoothingToggled(self, smoothingEnabled, smoothingLength):
540 """Called when a smoothing enabled check box is toggled."""
541 sensitive = smoothingEnabled.get_active()
542 smoothingLength.set_sensitive(sensitive)
543 if sensitive:
544 smoothingLength.grab_focus()
545
546 def _setSmoothing(self, smoothingEnabled, smoothingLength, smoothing):
547 """Set the smoothing controls from the given value.
548
549 If the value is less than 2, smoothing is disabled. The smoothing
550 length is the absolute value of the value."""
551 smoothingEnabled.set_active(smoothing>=2)
552 smoothingLength.set_value(abs(smoothing))
553
554 def _getSmoothing(self, smoothingEnabled, smoothingLength):
555 """Get the smoothing value from the given controls.
556
557 The returned value is the value of smoothingLength multiplied by -1, if
558 smoothing is disabled."""
559 value = smoothingLength.get_value_as_int()
560 if not smoothingEnabled.get_active():
561 value *= -1
562 return value
563
564 def _pirepDirectoryButtonClicked(self, button):
565 """Called when the PIREP directory button is clicked."""
566 dialog = Gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
567 xstr("prefs_pirepDirectory_browser_title"),
568 action = Gtk.FileChooserAction.SELECT_FOLDER,
569 buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
570 Gtk.STOCK_OK, Gtk.ResponseType.OK),
571 parent = self)
572 dialog.set_modal(True)
573 dialog.set_transient_for(self)
574
575 directory = self._pirepDirectory.get_text()
576 if directory:
577 dialog.select_filename(directory)
578
579 result = dialog.run()
580 dialog.hide()
581
582 if result==Gtk.ResponseType.OK:
583 self._pirepDirectory.set_text(dialog.get_filename())
584
585 def _pirepDirectoryChanged(self, entry):
586 """Called when the PIREP directory is changed."""
587 if self._pirepDirectory.get_text():
588 self._pirepAutoSave.set_sensitive(True)
589 else:
590 self._pirepAutoSave.set_active(False)
591 self._pirepAutoSave.set_sensitive(False)
592
593 def _buildMessages(self):
594 """Build the page for the message settings."""
595
596 mainAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
597 xscale = 0.0, yscale = 0.0)
598 mainAlignment.set_padding(padding_top = 16, padding_bottom = 8,
599 padding_left = 4, padding_right = 4)
600 mainBox = Gtk.VBox()
601 mainAlignment.add(mainBox)
602
603 table = Gtk.Table(len(const.messageTypes) + 1, 3)
604 table.set_row_spacings(8)
605 table.set_col_spacings(32)
606 table.set_homogeneous(False)
607 mainBox.pack_start(table, False, False, 4)
608
609 label = Gtk.Label(xstr("prefs_msgs_fs"))
610 label.set_justify(Gtk.Justification.CENTER)
611 label.set_alignment(0.5, 1.0)
612 table.attach(label, 1, 2, 0, 1)
613
614 label = Gtk.Label(xstr("prefs_msgs_sound"))
615 label.set_justify(Gtk.Justification.CENTER)
616 label.set_alignment(0.5, 1.0)
617 table.attach(label, 2, 3, 0, 1)
618
619 self._msgFSCheckButtons = {}
620 self._msgSoundCheckButtons = {}
621 row = 1
622 for messageType in const.messageTypes:
623 messageTypeStr = const.messageType2string(messageType)
624 label = Gtk.Label(xstr("prefs_msgs_type_" + messageTypeStr))
625 label.set_justify(Gtk.Justification.CENTER)
626 label.set_use_underline(True)
627 label.set_alignment(0.5, 0.5)
628 table.attach(label, 0, 1, row, row+1)
629
630 fsCheckButton = Gtk.CheckButton()
631 alignment = Gtk.Alignment(xscale = 0.0, yscale = 0.0,
632 xalign = 0.5, yalign = 0.5)
633 alignment.add(fsCheckButton)
634 table.attach(alignment, 1, 2, row, row+1)
635 self._msgFSCheckButtons[messageType] = fsCheckButton
636
637 soundCheckButton = Gtk.CheckButton()
638 alignment = Gtk.Alignment(xscale = 0.0, yscale = 0.0,
639 xalign = 0.5, yalign = 0.5)
640 alignment.add(soundCheckButton)
641 table.attach(alignment, 2, 3, row, row+1)
642 self._msgSoundCheckButtons[messageType] = soundCheckButton
643
644 mnemonicWidget = Gtk.Label("")
645 table.attach(mnemonicWidget, 3, 4, row, row+1)
646 label.set_mnemonic_widget(mnemonicWidget)
647 mnemonicWidget.connect("mnemonic-activate",
648 self._msgLabelActivated,
649 messageType)
650
651 row += 1
652
653 return mainAlignment
654
655 def _msgLabelActivated(self, button, cycle_group, messageType):
656 """Called when the mnemonic of a label is activated.
657
658 It cycles the corresponding options."""
659 fsCheckButton = self._msgFSCheckButtons[messageType]
660 soundCheckButton = self._msgSoundCheckButtons[messageType]
661
662 num = 1 if fsCheckButton.get_active() else 0
663 num += 2 if soundCheckButton.get_active() else 0
664 num += 1
665
666 fsCheckButton.set_active((num&0x01)==0x01)
667 soundCheckButton.set_active((num&0x02)==0x02)
668
669 return True
670
671 def _buildSounds(self):
672 """Build the page for the sounds."""
673 mainAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
674 xscale = 1.0, yscale = 1.0)
675 mainAlignment.set_padding(padding_top = 8, padding_bottom = 8,
676 padding_left = 4, padding_right = 4)
677
678 mainBox = Gtk.VBox()
679 mainAlignment.add(mainBox)
680
681 backgroundFrame = Gtk.Frame(label = xstr("prefs_sounds_frame_bg"))
682 mainBox.pack_start(backgroundFrame, False, False, 4)
683
684 backgroundAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
685 xscale = 1.0, yscale = 0.0)
686 backgroundAlignment.set_padding(padding_top = 4, padding_bottom = 4,
687 padding_left = 4, padding_right = 4)
688 backgroundFrame.add(backgroundAlignment)
689
690 backgroundBox = Gtk.VBox()
691 backgroundAlignment.add(backgroundBox)
692
693 self._enableSounds = Gtk.CheckButton(xstr("prefs_sounds_enable"))
694 self._enableSounds.set_use_underline(True)
695 self._enableSounds.set_tooltip_text(xstr("prefs_sounds_enable_tooltip"))
696 self._enableSounds.connect("toggled", self._enableSoundsToggled)
697 alignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
698 xscale = 1.0, yscale = 0.0)
699 alignment.add(self._enableSounds)
700 backgroundBox.pack_start(alignment, False, False, 4)
701
702 self._pilotControlsSounds = Gtk.CheckButton(xstr("prefs_sounds_pilotControls"))
703 self._pilotControlsSounds.set_use_underline(True)
704 self._pilotControlsSounds.set_tooltip_text(xstr("prefs_sounds_pilotControls_tooltip"))
705 self._pilotControlsSounds.connect("toggled", self._pilotControlsSoundsToggled)
706 backgroundBox.pack_start(self._pilotControlsSounds, False, False, 4)
707
708 self._pilotHotkey = Hotkey(xstr("prefs_sounds_pilotHotkey"),
709 [xstr("prefs_sounds_pilotHotkey_tooltip"),
710 xstr("prefs_sounds_pilotHotkeyCtrl_tooltip"),
711 xstr("prefs_sounds_pilotHotkeyShift_tooltip")])
712
713 backgroundBox.pack_start(self._pilotHotkey, False, False, 4)
714
715 self._taxiSoundOnPushback = Gtk.CheckButton(xstr("prefs_sounds_taxiSoundOnPushback"))
716 self._taxiSoundOnPushback.set_use_underline(True)
717 self._taxiSoundOnPushback.set_tooltip_text(xstr("prefs_sounds_taxiSoundOnPushback_tooltip"))
718 backgroundBox.pack_start(self._taxiSoundOnPushback, False, False, 4)
719
720 self._enableApproachCallouts = Gtk.CheckButton(xstr("prefs_sounds_approachCallouts"))
721 self._enableApproachCallouts.set_use_underline(True)
722 self._enableApproachCallouts.set_tooltip_text(xstr("prefs_sounds_approachCallouts_tooltip"))
723 backgroundBox.pack_start(self._enableApproachCallouts, False, False, 4)
724
725 self._speedbrakeAtTD = Gtk.CheckButton(xstr("prefs_sounds_speedbrakeAtTD"))
726 self._speedbrakeAtTD.set_use_underline(True)
727 self._speedbrakeAtTD.set_tooltip_text(xstr("prefs_sounds_speedbrakeAtTD_tooltip"))
728 backgroundBox.pack_start(self._speedbrakeAtTD, False, False, 4)
729
730 self._alternativeSoundSet = Gtk.CheckButton(xstr("prefs_sounds_alternativeSoundSet"))
731 self._alternativeSoundSet.set_use_underline(True)
732 self._alternativeSoundSet.set_tooltip_text(
733 xstr("prefs_sounds_alternativeSoundSet_tooltip"))
734 backgroundBox.pack_start(self._alternativeSoundSet, False, False, 4)
735
736 checklistFrame = Gtk.Frame(label = xstr("prefs_sounds_frame_checklists"))
737 mainBox.pack_start(checklistFrame, False, False, 4)
738
739 checklistAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
740 xscale = 1.0, yscale = 0.0)
741 checklistAlignment.set_padding(padding_top = 4, padding_bottom = 4,
742 padding_left = 4, padding_right = 4)
743 checklistFrame.add(checklistAlignment)
744
745 checklistBox = Gtk.VBox()
746 checklistAlignment.add(checklistBox)
747
748 self._enableChecklists = Gtk.CheckButton(xstr("prefs_sounds_enableChecklists"))
749 self._enableChecklists.set_use_underline(True)
750 self._enableChecklists.set_tooltip_text(xstr("prefs_sounds_enableChecklists_tooltip"))
751 self._enableChecklists.connect("toggled", self._enableChecklistsToggled)
752 checklistBox.pack_start(self._enableChecklists, False, False, 4)
753
754 self._checklistHotkey = Hotkey(xstr("prefs_sounds_checklistHotkey"),
755 [xstr("prefs_sounds_checklistHotkey_tooltip"),
756 xstr("prefs_sounds_checklistHotkeyCtrl_tooltip"),
757 xstr("prefs_sounds_checklistHotkeyShift_tooltip")])
758
759 checklistBox.pack_start(self._checklistHotkey, False, False, 4)
760
761 self._enableSoundsToggled(self._enableSounds)
762 self._enableChecklistsToggled(self._enableChecklists)
763
764 self._pilotHotkey.connect("hotkey-changed", self._reconcileHotkeys,
765 self._checklistHotkey)
766 self._checklistHotkey.connect("hotkey-changed", self._reconcileHotkeys,
767 self._pilotHotkey)
768
769 return mainAlignment
770
771 def _enableSoundsToggled(self, button):
772 """Called when the enable sounds button is toggled."""
773 active = button.get_active()
774 self._pilotControlsSounds.set_sensitive(active)
775 self._pilotControlsSoundsToggled(self._pilotControlsSounds)
776 self._taxiSoundOnPushback.set_sensitive(active)
777 self._enableApproachCallouts.set_sensitive(active)
778 self._speedbrakeAtTD.set_sensitive(active)
779 self._alternativeSoundSet.set_sensitive(active)
780
781 def _pilotControlsSoundsToggled(self, button):
782 """Called when the enable sounds button is toggled."""
783 active = button.get_active() and self._enableSounds.get_active()
784 self._pilotHotkey.set_sensitive(active)
785 if active and self._checklistHotkey.get_sensitive():
786 self._reconcileHotkeys(self._checklistHotkey, Hotkey.CHANGED_SHIFT,
787 self._pilotHotkey)
788
789 def _enableChecklistsToggled(self, button):
790 """Called when the enable checklists button is toggled."""
791 active = button.get_active()
792 self._checklistHotkey.set_sensitive(active)
793 if active and self._pilotHotkey.get_sensitive():
794 self._reconcileHotkeys(self._pilotHotkey, Hotkey.CHANGED_SHIFT,
795 self._checklistHotkey)
796
797 def _reconcileHotkeys(self, changedHotkey, what, otherHotkey):
798 """Reconcile the given hotkeys so that they are different.
799
800 changedHotkey is the hotkey that has changed. what is one of the
801 Hotkey.CHANGED_XXX constants denoting what has changed. otherHotkey is
802 the other hotkey that must be reconciled.
803
804 If the other hotkey is not sensitive or is not equal to the changed
805 one, nothing happens.
806
807 Otherwise, if the status of the Ctrl modifier has changed, the status
808 of the Ctrl modifier on the other hotkey will be negated. Similarly, if
809 the Shift modifier has changed. If the key has changed, the Shift
810 modifier is negated in the other hotkey."""
811 if otherHotkey.get_sensitive() and changedHotkey==otherHotkey:
812 if what==Hotkey.CHANGED_CTRL:
813 otherHotkey.ctrl = not changedHotkey.ctrl
814 elif what==Hotkey.CHANGED_SHIFT or what==Hotkey.CHANGED_KEY:
815 otherHotkey.shift = not changedHotkey.shift
816
817 def _buildAdvanced(self):
818 """Build the page for the advanced settings."""
819
820 mainAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
821 xscale = 1.0, yscale = 0.0)
822 mainAlignment.set_padding(padding_top = 16, padding_bottom = 8,
823 padding_left = 4, padding_right = 4)
824 mainBox = Gtk.VBox()
825 mainAlignment.add(mainBox)
826
827 frame = Gtk.Frame.new()
828
829 self._autoUpdate = Gtk.CheckButton(xstr("prefs_update_auto"))
830 frame.set_label_widget(self._autoUpdate)
831 frame.set_label_align(0.025, 0.5)
832 mainBox.pack_start(frame, False, False, 4)
833
834 self._autoUpdate.set_use_underline(True)
835 self._autoUpdate.connect("toggled", self._autoUpdateToggled)
836 self._autoUpdate.set_tooltip_text(xstr("prefs_update_auto_tooltip"))
837 self._warnedAutoUpdate = False
838
839 updateURLBox = Gtk.HBox()
840 label = Gtk.Label(xstr("prefs_update_url"))
841 label.set_use_underline(True)
842 updateURLBox.pack_start(label, False, False, 4)
843
844 self._updateURL = Gtk.Entry()
845 label.set_mnemonic_widget(self._updateURL)
846 self._updateURL.set_width_chars(40)
847 self._updateURL.set_tooltip_text(xstr("prefs_update_url_tooltip"))
848 self._updateURL.connect("changed", self._updateURLChanged)
849 updateURLBox.pack_start(self._updateURL, True, True, 4)
850
851 updateURLBox.set_margin_top(6)
852 updateURLBox.set_margin_bottom(6)
853 updateURLBox.set_margin_left(4)
854 updateURLBox.set_margin_right(4)
855 frame.add(updateURLBox)
856
857 frame = Gtk.Frame.new()
858 self._xplaneRemote = Gtk.CheckButton(xstr("prefs_xplane_remote"))
859 frame.set_label_widget(self._xplaneRemote)
860 frame.set_label_align(0.025, 0.5)
861 mainBox.pack_start(frame, False, False, 4)
862
863 self._xplaneRemote.set_use_underline(True)
864 self._xplaneRemote.set_tooltip_text(xstr("prefs_xplane_remote_tooltip"))
865 self._xplaneRemote.connect("toggled", self._xplaneRemoteToggled)
866
867 xplaneAddressBox = Gtk.HBox()
868 label = Gtk.Label(xstr("prefs_xplane_address"))
869 label.set_use_underline(True)
870 xplaneAddressBox.pack_start(label, False, False, 4)
871
872 self._xplaneAddress = Gtk.Entry()
873 label.set_mnemonic_widget(self._xplaneAddress)
874 self._xplaneAddress.set_width_chars(40)
875 self._xplaneAddress.set_tooltip_text(xstr("prefs_xplane_address_tooltip"))
876 self._xplaneAddress.connect("changed", self._xplaneAddressChanged)
877 xplaneAddressBox.pack_start(self._xplaneAddress, True, True, 4)
878
879 xplaneAddressBox.set_margin_top(6)
880 xplaneAddressBox.set_margin_bottom(6)
881 xplaneAddressBox.set_margin_left(4)
882 xplaneAddressBox.set_margin_right(4)
883 frame.add(xplaneAddressBox)
884
885 return mainAlignment
886
887 def _setOKButtonSensitivity(self):
888 """Set the sensitive state of the OK button."""
889 sensitive = False
890 try:
891 result = urllib.parse.urlparse(self._updateURL.get_text())
892 sensitive = result.scheme!="" and (result.netloc + result.path)!=""
893 except:
894 pass
895
896 if sensitive:
897 sensitive = not self._xplaneRemote.get_active() or \
898 len(self._xplaneAddress.get_text())>0
899
900 okButton = self.get_widget_for_response(Gtk.ResponseType.ACCEPT)
901 okButton.set_sensitive(sensitive)
902
903 def _autoUpdateToggled(self, button):
904 """Called when the auto update check button is toggled."""
905 if not self._settingFromConfig and not self._warnedAutoUpdate and \
906 not self._autoUpdate.get_active():
907 dialog = Gtk.MessageDialog(parent = self,
908 type = Gtk.MessageType.INFO,
909 message_format = xstr("prefs_update_auto_warning"))
910 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
911 dialog.set_title(self.get_title())
912 dialog.run()
913 dialog.hide()
914 self._warnedAutoUpdate = True
915
916 def _updateURLChanged(self, entry):
917 """Called when the update URL is changed."""
918 self._setOKButtonSensitivity()
919
920 def _xplaneRemoteToggled(self, button):
921 """Called when the X-Plane remote access checkbox is toggled."""
922 self._setOKButtonSensitivity()
923
924 def _xplaneAddressChanged(self, entry):
925 """Called when the X-Plane address is changed."""
926 self._setOKButtonSensitivity()
Note: See TracBrowser for help on using the repository browser.