source: src/mlx/gui/prefs.py@ 1056:5656e1ae9624

python3
Last change on this file since 1056:5656e1ae9624 was 1044:988bfea3e8db, checked in by István Váradi <ivaradi@…>, 3 years ago

RPC is the only communication method (re #357)

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