source: src/mlx/gui/prefs.py@ 1116:7eaafcf70da9

python3
Last change on this file since 1116:7eaafcf70da9 was 1098:cd3cf67ef749, checked in by István Váradi <ivaradi@…>, 16 months ago

The main window can be made resizable (re #369).

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