1
# PiTiVi , Non-linear video editor
5
# Copyright (c) 2005, Edward Hervey <bilboed@bilboed.com>
7
# This program is free software; you can redistribute it and/or
8
# modify it under the terms of the GNU Lesser General Public
9
# License as published by the Free Software Foundation; either
10
# version 2.1 of the License, or (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
# Lesser General Public License for more details.
17
# You should have received a copy of the GNU Lesser General Public
18
# License along with this program; if not, write to the
19
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20
# Boston, MA 02110-1301, USA.
23
Dialog box for user preferences.
29
import pitivi.ui.dynamic as dynamic
31
from pitivi.configure import get_ui_dir
32
from pitivi.settings import GlobalSettings
33
from pitivi.ui.common import SPACING
34
from gettext import gettext as _
36
GlobalSettings.addConfigOption('prefsDialogWidth',
37
section="user-interface",
38
key="prefs-dialog-width",
41
GlobalSettings.addConfigOption('prefsDialogHeight',
42
section="user-interface",
43
key="prefs-dialog-height",
47
class PreferencesDialog():
52
def __init__(self, instance):
53
self.settings = instance.settings
59
min_width, min_height = self.contents.size_request()
60
width = max(min_width, self.settings.prefsDialogWidth)
61
height = max(min_height, self.settings.prefsDialogHeight)
62
self.dialog.set_default_size(width, height)
68
builder = gtk.Builder()
69
builder.add_from_file(os.path.join(get_ui_dir(), "preferences.ui"))
70
builder.connect_signals(self)
73
self.dialog = builder.get_object("dialog1")
74
self.model = builder.get_object("liststore1")
75
self.treeview = builder.get_object("treeview1")
76
self.contents = builder.get_object("box1")
77
self.revert_button = builder.get_object("revertButton")
78
self.factory_settings = builder.get_object("resetButton")
79
self.restart_warning = builder.get_object("restartWarning")
84
def addPreference(cls, attrname, label, description, section=None,
85
widget_klass=None, **args):
87
Add a user preference. The preferences dialog will try
88
to guess the appropriate widget to use based on the type of the
89
option, but you can override this by specifying a custom class.
91
@param label: user-visible name for this option
93
@param desc: a user-visible description documenting this option
94
(ignored unless prefs_label is non-null)
96
@param : user-visible category to which this option
97
belongs (ignored unless prefs_label is non-null)
99
@param widget_klass: overrides auto-detected widget
100
@type widget_klass: C{class}
104
if not section in cls.prefs:
105
cls.prefs[section] = {}
106
cls.prefs[section][attrname] = (label, description, widget_klass, args)
109
def addPathPreference(cls, attrname, label, description, section=None):
111
Add an auto-generated user preference that will show up as a
112
gtk.FileChooserButton.
114
@param label: user-visible name for this option
116
@param desc: a user-visible description documenting this option
117
(ignored unless prefs_label is non-null)
119
@param section: user-visible category to which this option
120
belongs (ignored unless prefs_label is non-null)
121
@type section: C{str}
123
cls.addPreference(attrname, label, description, section,
127
def addNumericPreference(cls, attrname, label, description, section=None,
128
upper=None, lower=None):
130
Add an auto-generated user preference that will show up as either a
131
gtk.SpinButton or a gtk.HScale, depending whether both the upper and lower
134
@param label: user-visible name for this option
136
@param desc: a user-visible description documenting this option
137
(ignored unless prefs_label is non-null)
139
@param section: user-visible category to which this option
140
belongs (ignored unless prefs_label is non-null)
141
@type section: C{str}
142
@param upper: upper limit for this widget, or None
143
@type upper: C{number}
144
@param lower: lower limit for this widget, or None
145
@type lower: C{number}
147
cls.addPreference(attrname, label, description, section,
148
dynamic.NumericWidget, upper=upper, lower=lower)
151
def addTextPreference(cls, attrname, label, description, section=None,
154
Add an auto-generated user preference that will show up as either a
155
gtk.SpinButton or a gtk.HScale, depending on the upper and lower
158
@param label: user-visible name for this option
160
@param desc: a user-visible description documenting this option
161
(ignored unless prefs_label is non-null)
163
@param section: user-visible category to which this option
164
belongs (ignored unless prefs_label is non-null)
165
@type section: C{str}
167
cls.addPreference(attrname, label, description, section,
168
dynamic.TextWidget, matches=matches)
171
def addChoicePreference(cls, attrname, label, description, choices,
174
Add an auto-generated user preference that will show up as either a
175
gtk.ComboBox or a group of radio buttons, depending on the number of
178
@param label: user-visible name for this option
180
@param desc: a user-visible description documenting this option
181
(ignored unless prefs_label is non-null)
183
@param choices: a sequence of (<label>, <value>) pairs
184
@type choices: C{[(str, pyobject), ...]}
185
@param section: user-visible category to which this option
186
belongs (ignored unless prefs_label is non-null)
187
@type section: C{str}
189
cls.addPreference(attrname, label, description, section,
190
dynamic.ChoiceWidget, choices=choices)
193
def addTogglePreference(cls, attrname, label, description, section=None):
195
Add an auto-generated user preference that will show up as a
198
@param label: user-visible name for this option
200
@param desc: a user-visible description documenting this option
201
(ignored unless prefs_label is non-null)
203
@param section: user-visible category to which this option
204
belongs (ignored unless prefs_label is non-null)
205
@type section: C{str}
207
cls.addPreference(attrname, label, description, section,
208
dynamic.ToggleWidget)
211
def addColorPreference(cls, attrname, label, description, section=None,
214
Add an auto-generated user preference for specifying colors. The
215
colors can be returned as either int, a string colorspec, or a
216
gtk.gdk.Color object. See the gtk.gdk.color_parse() function for info
219
@param label: user-visible name for this option
221
@param desc: a user-visible description documenting this option
222
(ignored unless prefs_label is non-null)
224
@param section: user-visible category to which this option
225
belongs (ignored unless prefs_label is non-null)
226
@type section: C{str}
228
cls.addPreference(attrname, label, description, section,
229
dynamic.ColorWidget, value_type=value_type)
232
def addFontPreference(cls, attrname, label, description, section=None):
234
Add an auto-generated user preference that will show up as a
237
@param label: user-visible name for this option
239
@param desc: a user-visible description documenting this option
240
(ignored unless prefs_label is non-null)
242
@param section: user-visible category to which this option
243
belongs (ignored unless prefs_label is non-null)
244
@type section: C{str}
246
cls.addPreference(attrname, label, description, section,
250
def _fillContents(self):
252
for section in sorted(self.prefs):
253
options = self.prefs[section]
254
self.model.append((_(section), section))
255
widgets = gtk.Table()
256
widgets.set_border_width(SPACING)
257
widgets.props.column_spacing = SPACING
258
widgets.props.row_spacing = SPACING / 2
259
self.sections[section] = widgets
262
for attrname in options:
263
label, description, klass, args = options[attrname]
264
widget = klass(**args)
265
widget.setWidgetValue(getattr(self.settings, attrname))
266
widget.connectValueChanged(self._valueChanged, widget,
268
self.widgets[attrname] = widget
269
if isinstance(widget, dynamic.ToggleWidget):
270
# Don't add a semicolon for checkbuttons
271
label_widget = gtk.Label(_(label))
273
label_widget = gtk.Label(_(label) + ":")
275
icon.set_from_stock('gtk-clear', gtk.ICON_SIZE_MENU)
276
revert = gtk.Button()
278
revert.set_tooltip_text(_("Reset to default value"))
279
revert.set_sensitive(not self.settings.isDefault(attrname))
280
revert.connect("clicked", self._resetOptionCb, attrname)
282
self.resets[attrname] = revert
283
prefs[label] = (label_widget, widget, description, revert)
285
# Sort widgets: I think we only want to sort by the non-localized
286
# names, so options appear in the same place across locales ...
287
# but then I may be wrong
289
for y, unlocalized in enumerate(sorted(prefs)):
290
label, widget, description, revert = prefs[unlocalized]
291
if isinstance(widget, dynamic.ToggleWidget):
292
# Avoid the separating the label from the checkbox
293
widget.set_label(label.get_text())
294
widgets.attach(widget, 0, 2, y, y + 1, yoptions=0)
295
widgets.attach(revert, 2, 3, y, y + 1, xoptions=0, yoptions=0)
297
label.set_alignment(1.0, 0.5)
298
label.set_tooltip_text(description)
299
widgets.attach(label, 0, 1, y, y + 1, xoptions=gtk.FILL, yoptions=0)
300
widgets.attach(widget, 1, 2, y, y + 1, yoptions=0)
301
widgets.attach(revert, 2, 3, y, y + 1, xoptions=0, yoptions=0)
303
widget.set_tooltip_text(description)
307
self.contents.pack_start(widgets, True, True)
309
self.treeview.get_selection().select_path((0,))
311
def _treeSelectionChangedCb(self, selection):
312
model, iter = selection.get_selected()
313
new = self.sections[model[iter][1]]
314
if self._current != new:
320
def _clearHistory(self):
321
self.original_values = {}
322
self.revert_button.set_sensitive(False)
324
def _factorySettingsButtonCb(self, unused_button):
325
for section in self.prefs.itervalues():
326
for attrname in section:
327
self._resetOptionCb(self.resets[attrname], attrname)
329
def _revertButtonCb(self, unused_button):
330
for attrname, value in self.original_values.iteritems():
331
self.widgets[attrname].setWidgetValue(value)
332
setattr(self.settings, attrname, value)
334
self.factory_settings.set_sensitive(self._canReset())
336
def _resetOptionCb(self, button, attrname):
337
if not self.settings.isDefault(attrname):
338
self.settings.setDefault(attrname)
339
self.widgets[attrname].setWidgetValue(getattr(self.settings,
341
button.set_sensitive(False)
342
self.factory_settings.set_sensitive(self._canReset())
344
def _acceptButtonCb(self, unused_button):
348
def _valueChanged(self, fake_widget, real_widget, attrname):
349
value = getattr(self.settings, attrname)
350
if attrname not in self.original_values:
351
self.original_values[attrname] = value
352
if attrname + "Changed" not in GlobalSettings.get_signals():
353
self.restart_warning.show()
354
self.revert_button.set_sensitive(True)
356
# convert the value of the widget to whatever type it is currently
357
if value is not None:
358
value = type(value)(real_widget.getWidgetValue())
359
setattr(self.settings, attrname, value)
361
# adjust controls as appropriate
362
self.resets[attrname].set_sensitive(not self.settings.isDefault(
364
self.factory_settings.set_sensitive(True)
366
def _configureCb(self, unused_widget, event):
367
self.settings.prefsDialogWidth = event.width
368
self.settings.prefsDialogHeight = event.height
371
for section in self.prefs.itervalues():
372
for attrname in section:
373
if not self.settings.isDefault(attrname):
377
## Preference Test Cases
381
from pitivi.settings import GlobalSettings
384
('numericPreference1', 10),
385
('numericPreference2', 2.4),
386
('textPreference1', "banana"),
387
('textPreference2', "42"),
388
('aPathPreference', "file:///etc/"),
389
('aChoicePreference', 42),
390
('aLongChoicePreference', "Mauve"),
391
('aTogglePreference', True),
392
('aFontPreference', "Sans 9"),
395
for attrname, default in options:
396
GlobalSettings.addConfigOption(attrname, default=default)
400
PreferencesDialog.addNumericPreference('numericPreference1',
403
description="This option has no upper bound",
406
PreferencesDialog.addNumericPreference('numericPreference2',
407
label="Closed Range",
409
description="This option has both upper and lower bounds",
415
PreferencesDialog.addTextPreference('textPreference1',
418
description="Anything can go in this box")
420
PreferencesDialog.addTextPreference('textPreference2',
421
label="Numbers only",
423
description="This input validates its input with a regex",
424
matches="^-?\d+(\.\d+)?$")
427
PreferencesDialog.addPathPreference('aPathPreference',
430
description="Test the path widget")
432
PreferencesDialog.addChoicePreference('aChoicePreference',
433
label="Swallow Velocity",
435
description="What is the airspeed velocity of a coconut-laden swallow?",
438
("9 furlongs per fortnight", 42),
439
("I don't know that!", None)))
441
PreferencesDialog.addChoicePreference('aLongChoicePreference',
442
label="Favorite Color",
444
description="What is the color of the parrot's plumage?",
447
("Chartreuse", "Chartreuse"),
448
("Magenta", "Magenta"),
450
("Norwegian Blue", "Norwegian Blue"),
451
("Yellow Ochre", "Yellow Ochre")))
453
PreferencesDialog.addTogglePreference('aTogglePreference',
456
description="Test the toggle widget")
458
PreferencesDialog.addFontPreference('aFontPreference',
461
description="Test the font widget")