source: src/mlx/gui/prefs.py@ 1122:84d0a094eb3f

python3
Last change on this file since 1122:84d0a094eb3f was 1098:cd3cf67ef749, checked in by István Váradi <ivaradi@…>, 9 months ago

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

File size: 38.6 KB
RevLine 
[123]1
[919]2from .common import *
[123]3
4from mlx.i18n import xstr
5import mlx.const as const
[166]6import mlx.config as config
[123]7
[919]8import urllib.parse
[123]9
10#------------------------------------------------------------------------------
11
[300]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
[996]22class Hotkey(Gtk.HBox):
[166]23 """A widget to handle a hotkey."""
[167]24
25 # Constant to denote that the status of the Ctrl modifier is changed
26 CHANGED_CTRL = 1
[392]27
[167]28 # Constant to denote that the status of the Shift modifier is changed
29 CHANGED_SHIFT = 2
[392]30
[167]31 # Constant to denote that the value of the key is changed
32 CHANGED_KEY = 3
[392]33
[166]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__()
[392]44
[996]45 label = Gtk.Label(labelText)
[166]46 label.set_use_underline(True)
[996]47 labelAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
[166]48 xscale = 0.0, yscale = 0.0)
[168]49 labelAlignment.set_padding(padding_top = 0, padding_bottom = 0,
50 padding_left = 0, padding_right = 4)
[166]51 labelAlignment.add(label)
[168]52 self.pack_start(labelAlignment, False, False, 0)
[166]53
[996]54 self._ctrl = Gtk.CheckButton("Ctrl")
[166]55 self._ctrl.set_tooltip_text(tooltips[1])
[167]56 self._ctrl.connect("toggled", self._ctrlToggled)
[166]57 self.pack_start(self._ctrl, False, False, 4)
[392]58
[996]59 self._shift = Gtk.CheckButton("Shift")
[166]60 self._shift.set_tooltip_text(tooltips[2])
[167]61 self._shift.connect("toggled", self._shiftToggled)
[166]62 self.pack_start(self._shift, False, False, 4)
63
[996]64 self._hotkeyModel = Gtk.ListStore(str)
[919]65 for keyCode in list(range(ord("0"), ord("9")+1)) + list(range(ord("A"), ord("Z")+1)):
[166]66 self._hotkeyModel.append([chr(keyCode)])
67
[996]68 self._hotkey = Gtk.ComboBox(model = self._hotkeyModel)
69 cell = Gtk.CellRendererText()
[166]70 self._hotkey.pack_start(cell, True)
71 self._hotkey.add_attribute(cell, 'text', 0)
72 self._hotkey.set_tooltip_text(tooltips[0])
[167]73 self._hotkey.connect("changed", self._keyChanged)
[166]74 self.pack_start(self._hotkey, False, False, 4)
75
[174]76 label.set_mnemonic_widget(self._hotkey)
77
[166]78 self._setting = False
79
[167]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."""
[166]88 self._setting = True
[167]89 self._ctrl.set_active(ctrl)
90 self._setting = False
[166]91
[167]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
[166]113
114 hotkeyModel = self._hotkeyModel
115 iter = hotkeyModel.get_iter_first()
116 while iter is not None and \
[167]117 hotkeyModel.get_value(iter, 0)!=key:
[166]118 iter = hotkeyModel.iter_next(iter)
119
120 if iter is None:
121 iter = hotkeyModel.get_iter_first()
122
[392]123 self._hotkey.set_active_iter(iter)
[167]124
[166]125 self._setting = False
126
[167]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
[166]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
[167]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
[995]163GObject.signal_new("hotkey-changed", Hotkey, GObject.SIGNAL_RUN_FIRST,
[167]164 None, (int,))
[166]165
166#------------------------------------------------------------------------------
167
[996]168class Preferences(Gtk.Dialog):
[123]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,
[999]175 Gtk.DialogFlags.MODAL)
[124]176
[999]177 self.add_button(xstr("button_cancel"), Gtk.ResponseType.REJECT)
178 self.add_button(xstr("button_ok"), Gtk.ResponseType.ACCEPT)
[202]179 self.set_resizable(False)
[392]180
[123]181 self._gui = gui
[166]182 self._settingFromConfig = False
[123]183
184 contentArea = self.get_content_area()
185
[996]186 notebook = Gtk.Notebook()
[123]187 contentArea.pack_start(notebook, True, True, 4)
188
189 general = self._buildGeneral()
[996]190 label = Gtk.Label(xstr("prefs_tab_general"))
[123]191 label.set_use_underline(True)
192 label.set_tooltip_text(xstr("prefs_tab_general_tooltip"))
193 notebook.append_page(general, label)
194
[132]195 messages = self._buildMessages()
[996]196 label = Gtk.Label(xstr("prefs_tab_messages"))
[132]197 label.set_use_underline(True)
198 label.set_tooltip_text(xstr("prefs_tab_message_tooltip"))
199 notebook.append_page(messages, label)
200
[166]201 sounds = self._buildSounds()
[996]202 label = Gtk.Label(xstr("prefs_tab_sounds"))
[166]203 label.set_use_underline(True)
204 label.set_tooltip_text(xstr("prefs_tab_sounds_tooltip"))
205 notebook.append_page(sounds, label)
206
[123]207 advanced = self._buildAdvanced()
[996]208 label = Gtk.Label(xstr("prefs_tab_advanced"))
[123]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
[999]224 if response==Gtk.ResponseType.ACCEPT:
[123]225 self._toConfig(config)
226 config.save()
227
228 def _fromConfig(self, config):
229 """Setup the dialog from the given configuration."""
[166]230 self._settingFromConfig = True
231
[123]232 self._setLanguage(config.language)
[1098]233 self._mainWindowResizable.set_active(config.mainWindowResizable)
[147]234 self._hideMinimizedWindow.set_active(config.hideMinimizedWindow)
[249]235 self._quitOnClose.set_active(config.quitOnClose)
[136]236 self._onlineGateSystem.set_active(config.onlineGateSystem)
[139]237 self._onlineACARS.set_active(config.onlineACARS)
[131]238 self._flareTimeFromFS.set_active(config.flareTimeFromFS)
[148]239 self._syncFSTime.set_active(config.syncFSTime)
[183]240 self._usingFS2Crew.set_active(config.usingFS2Crew)
[392]241
[197]242 self._setSmoothing(self._iasSmoothingEnabled, self._iasSmoothingLength,
243 config.iasSmoothingLength)
244 self._setSmoothing(self._vsSmoothingEnabled, self._vsSmoothingLength,
245 config.vsSmoothingLength)
[123]246
[684]247 self._useSimBrief.set_active(config.useSimBrief)
248
[149]249 pirepDirectory = config.pirepDirectory
250 self._pirepDirectory.set_text("" if pirepDirectory is None
251 else pirepDirectory)
[392]252 self._pirepAutoSave.set_active(config.pirepAutoSave)
253 if not pirepDirectory:
254 self._pirepAutoSave.set_sensitive(False)
[149]255
[132]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
[166]265 self._enableSounds.set_active(config.enableSounds)
266 self._pilotControlsSounds.set_active(config.pilotControlsSounds)
267 self._pilotHotkey.set(config.pilotHotkey)
[270]268 self._enableApproachCallouts.set_active(config.enableApproachCallouts)
[166]269 self._speedbrakeAtTD.set_active(config.speedbrakeAtTD)
270
[392]271 self._enableChecklists.set_active(config.enableChecklists)
[166]272 self._checklistHotkey.set(config.checklistHotkey)
273
[1094]274 self._taxiSoundOnPushback.set_active(config.taxiSoundOnPushback)
275
[123]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
[1067]282 self._xplaneRemote.set_active(config.xplaneRemote)
283 self._xplaneAddress.set_text(config.xplaneAddress)
284
[166]285 self._settingFromConfig = False
286
[123]287 def _toConfig(self, config):
288 """Setup the given config from the settings in the dialog."""
289 config.language = self._getLanguage()
[1098]290 config.mainWindowResizable = self._mainWindowResizable.get_active()
[147]291 config.hideMinimizedWindow = self._hideMinimizedWindow.get_active()
[249]292 config.quitOnClose = self._quitOnClose.get_active()
[136]293 config.onlineGateSystem = self._onlineGateSystem.get_active()
[139]294 config.onlineACARS = self._onlineACARS.get_active()
[131]295 config.flareTimeFromFS = self._flareTimeFromFS.get_active()
[148]296 config.syncFSTime = self._syncFSTime.get_active()
[183]297 config.usingFS2Crew = self._usingFS2Crew.get_active()
[197]298 config.iasSmoothingLength = self._getSmoothing(self._iasSmoothingEnabled,
299 self._iasSmoothingLength)
300 config.vsSmoothingLength = self._getSmoothing(self._vsSmoothingEnabled,
301 self._vsSmoothingLength)
[684]302 config.useSimBrief = self._useSimBrief.get_active()
[954]303 config.pirepDirectory = self._pirepDirectory.get_text()
[392]304 config.pirepAutoSave = self._pirepAutoSave.get_active()
[132]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
[166]318 config.enableSounds = self._enableSounds.get_active()
319 config.pilotControlsSounds = self._pilotControlsSounds.get_active()
320 config.pilotHotkey = self._pilotHotkey.get()
[270]321 config.enableApproachCallouts = self._enableApproachCallouts.get_active()
[166]322 config.speedbrakeAtTD = self._speedbrakeAtTD.get_active()
323
324 config.enableChecklists = self._enableChecklists.get_active()
325 config.checklistHotkey = self._checklistHotkey.get()
326
[1094]327 config.taxiSoundOnPushback = self._taxiSoundOnPushback.get_active()
328
[123]329 config.autoUpdate = self._autoUpdate.get_active()
330 config.updateURL = self._updateURL.get_text()
331
[1067]332 config.xplaneRemote = self._xplaneRemote.get_active()
333 config.xplaneAddress = self._xplaneAddress.get_text()
334
[123]335 def _buildGeneral(self):
336 """Build the page for the general settings."""
[996]337 mainAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
[149]338 xscale = 1.0, yscale = 0.0)
[186]339 mainAlignment.set_padding(padding_top = 0, padding_bottom = 8,
[149]340 padding_left = 4, padding_right = 4)
[996]341 mainBox = Gtk.VBox()
[123]342 mainAlignment.add(mainBox)
343
[186]344 guiBox = self._createFrame(mainBox, xstr("prefs_frame_gui"))
[392]345
[996]346 languageBox = Gtk.HBox()
[186]347 guiBox.pack_start(languageBox, False, False, 4)
[123]348
[996]349 label = Gtk.Label(xstr("prefs_language"))
[123]350 label.set_use_underline(True)
351
352 languageBox.pack_start(label, False, False, 4)
353
[996]354 self._languageList = Gtk.ListStore(str, str)
[123]355 for language in const.languages:
356 self._languageList.append([xstr("prefs_lang_" + language),
357 language])
358
359 self._languageComboBox = languageComboBox = \
[996]360 Gtk.ComboBox(model = self._languageList)
361 cell = Gtk.CellRendererText()
[123]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
[131]368 label.set_mnemonic_widget(languageComboBox)
369
[123]370 self._changingLanguage = False
371 self._warnedRestartNeeded = False
372
[1098]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
[996]378 self._hideMinimizedWindow = Gtk.CheckButton(xstr("prefs_hideMinimizedWindow"))
[147]379 self._hideMinimizedWindow.set_use_underline(True)
380 self._hideMinimizedWindow.set_tooltip_text(xstr("prefs_hideMinimizedWindow_tooltip"))
[186]381 guiBox.pack_start(self._hideMinimizedWindow, False, False, 4)
382
[996]383 self._quitOnClose = Gtk.CheckButton(xstr("prefs_quitOnClose"))
[249]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
[392]388 onlineBox = self._createFrame(mainBox, xstr("prefs_frame_online"))
[147]389
[996]390 self._onlineGateSystem = Gtk.CheckButton(xstr("prefs_onlineGateSystem"))
[136]391 self._onlineGateSystem.set_use_underline(True)
392 self._onlineGateSystem.set_tooltip_text(xstr("prefs_onlineGateSystem_tooltip"))
[186]393 onlineBox.pack_start(self._onlineGateSystem, False, False, 4)
[136]394
[996]395 self._onlineACARS = Gtk.CheckButton(xstr("prefs_onlineACARS"))
[139]396 self._onlineACARS.set_use_underline(True)
397 self._onlineACARS.set_tooltip_text(xstr("prefs_onlineACARS_tooltip"))
[186]398 onlineBox.pack_start(self._onlineACARS, False, False, 4)
399
400 simulatorBox = self._createFrame(mainBox, xstr("prefs_frame_simulator"))
[139]401
[996]402 self._flareTimeFromFS = Gtk.CheckButton(xstr("prefs_flaretimeFromFS"))
[131]403 self._flareTimeFromFS.set_use_underline(True)
404 self._flareTimeFromFS.set_tooltip_text(xstr("prefs_flaretimeFromFS_tooltip"))
[186]405 simulatorBox.pack_start(self._flareTimeFromFS, False, False, 4)
[392]406
[996]407 self._syncFSTime = Gtk.CheckButton(xstr("prefs_syncFSTime"))
[148]408 self._syncFSTime.set_use_underline(True)
409 self._syncFSTime.set_tooltip_text(xstr("prefs_syncFSTime_tooltip"))
[186]410 simulatorBox.pack_start(self._syncFSTime, False, False, 4)
[149]411
[996]412 self._usingFS2Crew = Gtk.CheckButton(xstr("prefs_usingFS2Crew"))
[183]413 self._usingFS2Crew.set_use_underline(True)
414 self._usingFS2Crew.set_tooltip_text(xstr("prefs_usingFS2Crew_tooltip"))
[186]415 simulatorBox.pack_start(self._usingFS2Crew, False, False, 4)
[392]416
[197]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)
[183]428
[996]429 self._useSimBrief = Gtk.CheckButton(xstr("prefs_useSimBrief"))
[684]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
[996]434 pirepBox = Gtk.HBox()
[186]435 mainBox.pack_start(pirepBox, False, False, 8)
[149]436
[996]437 label = Gtk.Label(xstr("prefs_pirepDirectory"))
[149]438 label.set_use_underline(True)
439 pirepBox.pack_start(label, False, False, 4)
440
[996]441 self._pirepDirectory = Gtk.Entry()
[149]442 self._pirepDirectory.set_tooltip_text(xstr("prefs_pirepDirectory_tooltip"))
[392]443 self._pirepDirectory.connect("changed", self._pirepDirectoryChanged)
[149]444 label.set_mnemonic_widget(self._pirepDirectory)
445 pirepBox.pack_start(self._pirepDirectory, True, True, 4)
446
[996]447 self._pirepDirectoryButton = Gtk.Button(xstr("button_browse"))
[149]448 self._pirepDirectoryButton.connect("clicked",
449 self._pirepDirectoryButtonClicked)
450 pirepBox.pack_start(self._pirepDirectoryButton, False, False, 4)
[392]451
[996]452 self._pirepAutoSave = Gtk.CheckButton(xstr("prefs_pirepAutoSave"))
[392]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
[123]457 return mainAlignment
458
[186]459 def _createFrame(self, mainBox, label):
460 """Create a frame with an inner alignment and VBox.
461
462 Return the vbox."""
[996]463 frame = Gtk.Frame(label = label)
[186]464 mainBox.pack_start(frame, False, False, 4)
[996]465 alignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
[186]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)
[996]470 vbox = Gtk.VBox()
[186]471 alignment.add(vbox)
472
473 return vbox
474
[197]475 def _createSmoothingBox(self, checkBoxLabel, checkBoxTooltip,
476 maxSeconds = 10):
477 """Create a HBox that contains entry fields for smoothing some value."""
[996]478 smoothingBox = Gtk.HBox()
[197]479
[996]480 smoothingEnabled = Gtk.CheckButton(checkBoxLabel)
[197]481 smoothingEnabled.set_use_underline(True)
482 smoothingEnabled.set_tooltip_text(checkBoxTooltip)
483
484 smoothingBox.pack_start(smoothingEnabled, False, False, 0)
485
[996]486 smoothingLength = Gtk.SpinButton()
[197]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
[996]495 smoothingBox.pack_start(Gtk.Label(xstr("prefs_smoothing_seconds")),
[197]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
[123]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):
[392]519 """Get the language selected by the user."""
[123]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:
[996]527 dialog = Gtk.MessageDialog(parent = self,
[999]528 type = Gtk.MessageType.INFO,
[123]529 message_format = xstr("prefs_restart"))
[999]530 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
[123]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
[197]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))
[392]551
[197]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
[392]561
[149]562 def _pirepDirectoryButtonClicked(self, button):
563 """Called when the PIREP directory button is clicked."""
[996]564 dialog = Gtk.FileChooserDialog(title = WINDOW_TITLE_BASE + " - " +
[149]565 xstr("prefs_pirepDirectory_browser_title"),
[999]566 action = Gtk.FileChooserAction.SELECT_FOLDER,
567 buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
568 Gtk.STOCK_OK, Gtk.ResponseType.OK),
[151]569 parent = self)
570 dialog.set_modal(True)
571 dialog.set_transient_for(self)
[123]572
[149]573 directory = self._pirepDirectory.get_text()
574 if directory:
575 dialog.select_filename(directory)
[392]576
[149]577 result = dialog.run()
578 dialog.hide()
579
[999]580 if result==Gtk.ResponseType.OK:
[954]581 self._pirepDirectory.set_text(dialog.get_filename())
[392]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
[132]591 def _buildMessages(self):
592 """Build the page for the message settings."""
593
[996]594 mainAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
[132]595 xscale = 0.0, yscale = 0.0)
596 mainAlignment.set_padding(padding_top = 16, padding_bottom = 8,
597 padding_left = 4, padding_right = 4)
[996]598 mainBox = Gtk.VBox()
[132]599 mainAlignment.add(mainBox)
600
[996]601 table = Gtk.Table(len(const.messageTypes) + 1, 3)
[132]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)
[392]606
[996]607 label = Gtk.Label(xstr("prefs_msgs_fs"))
[999]608 label.set_justify(Gtk.Justification.CENTER)
[132]609 label.set_alignment(0.5, 1.0)
610 table.attach(label, 1, 2, 0, 1)
[392]611
[996]612 label = Gtk.Label(xstr("prefs_msgs_sound"))
[999]613 label.set_justify(Gtk.Justification.CENTER)
[132]614 label.set_alignment(0.5, 1.0)
615 table.attach(label, 2, 3, 0, 1)
616
617 self._msgFSCheckButtons = {}
[392]618 self._msgSoundCheckButtons = {}
[132]619 row = 1
620 for messageType in const.messageTypes:
621 messageTypeStr = const.messageType2string(messageType)
[996]622 label = Gtk.Label(xstr("prefs_msgs_type_" + messageTypeStr))
[999]623 label.set_justify(Gtk.Justification.CENTER)
[132]624 label.set_use_underline(True)
625 label.set_alignment(0.5, 0.5)
626 table.attach(label, 0, 1, row, row+1)
627
[996]628 fsCheckButton = Gtk.CheckButton()
629 alignment = Gtk.Alignment(xscale = 0.0, yscale = 0.0,
[132]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
[392]634
[996]635 soundCheckButton = Gtk.CheckButton()
636 alignment = Gtk.Alignment(xscale = 0.0, yscale = 0.0,
[132]637 xalign = 0.5, yalign = 0.5)
638 alignment.add(soundCheckButton)
[392]639 table.attach(alignment, 2, 3, row, row+1)
[132]640 self._msgSoundCheckButtons[messageType] = soundCheckButton
641
[996]642 mnemonicWidget = Gtk.Label("")
[132]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
[166]668
669 def _buildSounds(self):
670 """Build the page for the sounds."""
[996]671 mainAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
[166]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
[996]676 mainBox = Gtk.VBox()
[166]677 mainAlignment.add(mainBox)
678
[996]679 backgroundFrame = Gtk.Frame(label = xstr("prefs_sounds_frame_bg"))
[166]680 mainBox.pack_start(backgroundFrame, False, False, 4)
681
[996]682 backgroundAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
[166]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
[996]688 backgroundBox = Gtk.VBox()
[166]689 backgroundAlignment.add(backgroundBox)
690
[996]691 self._enableSounds = Gtk.CheckButton(xstr("prefs_sounds_enable"))
[166]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)
[996]695 alignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
[166]696 xscale = 1.0, yscale = 0.0)
697 alignment.add(self._enableSounds)
698 backgroundBox.pack_start(alignment, False, False, 4)
699
[996]700 self._pilotControlsSounds = Gtk.CheckButton(xstr("prefs_sounds_pilotControls"))
[166]701 self._pilotControlsSounds.set_use_underline(True)
702 self._pilotControlsSounds.set_tooltip_text(xstr("prefs_sounds_pilotControls_tooltip"))
[169]703 self._pilotControlsSounds.connect("toggled", self._pilotControlsSoundsToggled)
[166]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")])
[392]710
[166]711 backgroundBox.pack_start(self._pilotHotkey, False, False, 4)
712
[1094]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
[996]718 self._enableApproachCallouts = Gtk.CheckButton(xstr("prefs_sounds_approachCallouts"))
[270]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)
[392]722
[996]723 self._speedbrakeAtTD = Gtk.CheckButton(xstr("prefs_sounds_speedbrakeAtTD"))
[166]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
[996]728 checklistFrame = Gtk.Frame(label = xstr("prefs_sounds_frame_checklists"))
[166]729 mainBox.pack_start(checklistFrame, False, False, 4)
730
[996]731 checklistAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
[166]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
[996]737 checklistBox = Gtk.VBox()
[166]738 checklistAlignment.add(checklistBox)
[392]739
[996]740 self._enableChecklists = Gtk.CheckButton(xstr("prefs_sounds_enableChecklists"))
[166]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
[167]756 self._pilotHotkey.connect("hotkey-changed", self._reconcileHotkeys,
757 self._checklistHotkey)
758 self._checklistHotkey.connect("hotkey-changed", self._reconcileHotkeys,
759 self._pilotHotkey)
760
[166]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)
[169]767 self._pilotControlsSoundsToggled(self._pilotControlsSounds)
[1094]768 self._taxiSoundOnPushback.set_sensitive(active)
[270]769 self._enableApproachCallouts.set_sensitive(active)
[169]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()
[166]775 self._pilotHotkey.set_sensitive(active)
[167]776 if active and self._checklistHotkey.get_sensitive():
777 self._reconcileHotkeys(self._checklistHotkey, Hotkey.CHANGED_SHIFT,
778 self._pilotHotkey)
[166]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)
[167]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
[166]807
[123]808 def _buildAdvanced(self):
809 """Build the page for the advanced settings."""
810
[996]811 mainAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
[149]812 xscale = 1.0, yscale = 0.0)
813 mainAlignment.set_padding(padding_top = 16, padding_bottom = 8,
814 padding_left = 4, padding_right = 4)
[996]815 mainBox = Gtk.VBox()
[123]816 mainAlignment.add(mainBox)
817
[1067]818 frame = Gtk.Frame.new()
819
[996]820 self._autoUpdate = Gtk.CheckButton(xstr("prefs_update_auto"))
[1067]821 frame.set_label_widget(self._autoUpdate)
822 frame.set_label_align(0.025, 0.5)
823 mainBox.pack_start(frame, False, False, 4)
[123]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
[392]829
[996]830 updateURLBox = Gtk.HBox()
831 label = Gtk.Label(xstr("prefs_update_url"))
[123]832 label.set_use_underline(True)
833 updateURLBox.pack_start(label, False, False, 4)
834
[996]835 self._updateURL = Gtk.Entry()
[123]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)
[149]840 updateURLBox.pack_start(self._updateURL, True, True, 4)
[123]841
[1067]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
[123]876 return mainAlignment
877
878 def _setOKButtonSensitivity(self):
879 """Set the sensitive state of the OK button."""
880 sensitive = False
881 try:
[919]882 result = urllib.parse.urlparse(self._updateURL.get_text())
[123]883 sensitive = result.scheme!="" and (result.netloc + result.path)!=""
884 except:
885 pass
886
[1067]887 if sensitive:
888 sensitive = not self._xplaneRemote.get_active() or \
889 len(self._xplaneAddress.get_text())>0
890
[999]891 okButton = self.get_widget_for_response(Gtk.ResponseType.ACCEPT)
[123]892 okButton.set_sensitive(sensitive)
893
894 def _autoUpdateToggled(self, button):
895 """Called when the auto update check button is toggled."""
[166]896 if not self._settingFromConfig and not self._warnedAutoUpdate and \
[123]897 not self._autoUpdate.get_active():
[996]898 dialog = Gtk.MessageDialog(parent = self,
[999]899 type = Gtk.MessageType.INFO,
[123]900 message_format = xstr("prefs_update_auto_warning"))
[999]901 dialog.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
[123]902 dialog.set_title(self.get_title())
903 dialog.run()
904 dialog.hide()
905 self._warnedAutoUpdate = True
[392]906
[123]907 def _updateURLChanged(self, entry):
908 """Called when the update URL is changed."""
909 self._setOKButtonSensitivity()
[167]910
[1067]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.