source: src/mlx/gui/prefs.py@ 1068:928cd01e06c6

python3
Last change on this file since 1068:928cd01e06c6 was 1067:9ce88d0b881d, checked in by István Váradi <ivaradi@…>, 2 years ago

Support for connecting to X-Plane remotely

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