~timo-jyrinki/ubuntu/trusty/pitivi/merge_debian_0.93-3

« back to all changes in this revision

Viewing changes to pitivi/ui/prefs.py

  • Committer: Timo Jyrinki
  • Author(s): Sebastian Dröge
  • Date: 2014-04-05 13:28:16 UTC
  • mfrom: (1.5.8)
  • Revision ID: timo.jyrinki@canonical.com-20140405132816-wmv1rhbtmlxmx3ag
Tags: 0.93-3
* debian/control:
  + Depend on python-gi (>= 3.10), older versions do not work
    with pitivi (Closes: #732813).
  + Add missing dependency on gir1.2-clutter-gst-2.0 (Closes: #743692).
  + Add suggests on gir1.2-notify-0.7 and gir1.2-gnomedesktop-3.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# PiTiVi , Non-linear video editor
2
 
#
3
 
#       ui/prefs.py
4
 
#
5
 
# Copyright (c) 2005, Edward Hervey <bilboed@bilboed.com>
6
 
#
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.
11
 
#
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.
16
 
#
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.
21
 
 
22
 
"""
23
 
Dialog box for user preferences.
24
 
"""
25
 
 
26
 
import gtk
27
 
import os
28
 
 
29
 
import pitivi.ui.dynamic as dynamic
30
 
 
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 _
35
 
 
36
 
GlobalSettings.addConfigOption('prefsDialogWidth',
37
 
    section="user-interface",
38
 
    key="prefs-dialog-width",
39
 
    default=600)
40
 
 
41
 
GlobalSettings.addConfigOption('prefsDialogHeight',
42
 
    section="user-interface",
43
 
    key="prefs-dialog-height",
44
 
    default=400)
45
 
 
46
 
 
47
 
class PreferencesDialog():
48
 
 
49
 
    prefs = {}
50
 
    original_values = {}
51
 
 
52
 
    def __init__(self, instance):
53
 
        self.settings = instance.settings
54
 
        self.widgets = {}
55
 
        self.resets = {}
56
 
        self._current = None
57
 
        self._createUi()
58
 
        self._fillContents()
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)
63
 
 
64
 
    def run(self):
65
 
        self.dialog.run()
66
 
 
67
 
    def _createUi(self):
68
 
        builder = gtk.Builder()
69
 
        builder.add_from_file(os.path.join(get_ui_dir(), "preferences.ui"))
70
 
        builder.connect_signals(self)
71
 
 
72
 
        # widgets we'll need
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")
80
 
 
81
 
## Public API
82
 
 
83
 
    @classmethod
84
 
    def addPreference(cls, attrname, label, description, section=None,
85
 
        widget_klass=None, **args):
86
 
        """
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.
90
 
 
91
 
        @param label: user-visible name for this option
92
 
        @type label: C{str}
93
 
        @param desc: a user-visible description documenting this option
94
 
        (ignored unless prefs_label is non-null)
95
 
        @type desc: C{str}
96
 
        @param : user-visible category to which this option
97
 
        belongs (ignored unless prefs_label is non-null)
98
 
        @type section: C{str}
99
 
        @param widget_klass: overrides auto-detected widget
100
 
        @type widget_klass: C{class}
101
 
        """
102
 
        if not section:
103
 
            section = "General"
104
 
        if not section in cls.prefs:
105
 
            cls.prefs[section] = {}
106
 
        cls.prefs[section][attrname] = (label, description, widget_klass, args)
107
 
 
108
 
    @classmethod
109
 
    def addPathPreference(cls, attrname, label, description, section=None):
110
 
        """
111
 
        Add an auto-generated user preference that will show up as a
112
 
        gtk.FileChooserButton.
113
 
 
114
 
        @param label: user-visible name for this option
115
 
        @type label: C{str}
116
 
        @param desc: a user-visible description documenting this option
117
 
        (ignored unless prefs_label is non-null)
118
 
        @type desc: C{str}
119
 
        @param section: user-visible category to which this option
120
 
        belongs (ignored unless prefs_label is non-null)
121
 
        @type section: C{str}
122
 
        """
123
 
        cls.addPreference(attrname, label, description, section,
124
 
            dynamic.PathWidget)
125
 
 
126
 
    @classmethod
127
 
    def addNumericPreference(cls, attrname, label, description, section=None,
128
 
        upper=None, lower=None):
129
 
        """
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
132
 
        limits are set.
133
 
 
134
 
        @param label: user-visible name for this option
135
 
        @type label: C{str}
136
 
        @param desc: a user-visible description documenting this option
137
 
        (ignored unless prefs_label is non-null)
138
 
        @type desc: C{str}
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}
146
 
        """
147
 
        cls.addPreference(attrname, label, description, section,
148
 
            dynamic.NumericWidget, upper=upper, lower=lower)
149
 
 
150
 
    @classmethod
151
 
    def addTextPreference(cls, attrname, label, description, section=None,
152
 
        matches=None):
153
 
        """
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
156
 
        limits
157
 
 
158
 
        @param label: user-visible name for this option
159
 
        @type label: C{str}
160
 
        @param desc: a user-visible description documenting this option
161
 
        (ignored unless prefs_label is non-null)
162
 
        @type desc: C{str}
163
 
        @param section: user-visible category to which this option
164
 
        belongs (ignored unless prefs_label is non-null)
165
 
        @type section: C{str}
166
 
        """
167
 
        cls.addPreference(attrname, label, description, section,
168
 
            dynamic.TextWidget, matches=matches)
169
 
 
170
 
    @classmethod
171
 
    def addChoicePreference(cls, attrname, label, description, choices,
172
 
        section=None):
173
 
        """
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
176
 
        choices.
177
 
 
178
 
        @param label: user-visible name for this option
179
 
        @type label: C{str}
180
 
        @param desc: a user-visible description documenting this option
181
 
        (ignored unless prefs_label is non-null)
182
 
        @type desc: C{str}
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}
188
 
        """
189
 
        cls.addPreference(attrname, label, description, section,
190
 
            dynamic.ChoiceWidget, choices=choices)
191
 
 
192
 
    @classmethod
193
 
    def addTogglePreference(cls, attrname, label, description, section=None):
194
 
        """
195
 
        Add an auto-generated user preference that will show up as a
196
 
        gtk.CheckButton.
197
 
 
198
 
        @param label: user-visible name for this option
199
 
        @type label: C{str}
200
 
        @param desc: a user-visible description documenting this option
201
 
        (ignored unless prefs_label is non-null)
202
 
        @type desc: C{str}
203
 
        @param section: user-visible category to which this option
204
 
        belongs (ignored unless prefs_label is non-null)
205
 
        @type section: C{str}
206
 
        """
207
 
        cls.addPreference(attrname, label, description, section,
208
 
            dynamic.ToggleWidget)
209
 
 
210
 
    @classmethod
211
 
    def addColorPreference(cls, attrname, label, description, section=None,
212
 
        value_type=int):
213
 
        """
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
217
 
        on colorspecs.
218
 
 
219
 
        @param label: user-visible name for this option
220
 
        @type label: C{str}
221
 
        @param desc: a user-visible description documenting this option
222
 
        (ignored unless prefs_label is non-null)
223
 
        @type desc: C{str}
224
 
        @param section: user-visible category to which this option
225
 
        belongs (ignored unless prefs_label is non-null)
226
 
        @type section: C{str}
227
 
        """
228
 
        cls.addPreference(attrname, label, description, section,
229
 
            dynamic.ColorWidget, value_type=value_type)
230
 
 
231
 
    @classmethod
232
 
    def addFontPreference(cls, attrname, label, description, section=None):
233
 
        """
234
 
        Add an auto-generated user preference that will show up as a
235
 
        font selector.
236
 
 
237
 
        @param label: user-visible name for this option
238
 
        @type label: C{str}
239
 
        @param desc: a user-visible description documenting this option
240
 
        (ignored unless prefs_label is non-null)
241
 
        @type desc: C{str}
242
 
        @param section: user-visible category to which this option
243
 
        belongs (ignored unless prefs_label is non-null)
244
 
        @type section: C{str}
245
 
        """
246
 
        cls.addPreference(attrname, label, description, section,
247
 
            dynamic.FontWidget)
248
 
 
249
 
## Implementation
250
 
    def _fillContents(self):
251
 
        self.sections = {}
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
260
 
 
261
 
            prefs = {}
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,
267
 
                    attrname)
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))
272
 
                else:
273
 
                    label_widget = gtk.Label(_(label) + ":")
274
 
                icon = gtk.Image()
275
 
                icon.set_from_stock('gtk-clear', gtk.ICON_SIZE_MENU)
276
 
                revert = gtk.Button()
277
 
                revert.add(icon)
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)
281
 
                revert.show_all()
282
 
                self.resets[attrname] = revert
283
 
                prefs[label] = (label_widget, widget, description, revert)
284
 
 
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
288
 
 
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)
296
 
                else:
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)
302
 
                    label.show()
303
 
                widget.set_tooltip_text(description)
304
 
                widget.show()
305
 
                revert.show()
306
 
 
307
 
            self.contents.pack_start(widgets, True, True)
308
 
 
309
 
        self.treeview.get_selection().select_path((0,))
310
 
 
311
 
    def _treeSelectionChangedCb(self, selection):
312
 
        model, iter = selection.get_selected()
313
 
        new = self.sections[model[iter][1]]
314
 
        if self._current != new:
315
 
            if self._current:
316
 
                self._current.hide()
317
 
            new.show()
318
 
            self._current = new
319
 
 
320
 
    def _clearHistory(self):
321
 
        self.original_values = {}
322
 
        self.revert_button.set_sensitive(False)
323
 
 
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)
328
 
 
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)
333
 
        self._clearHistory()
334
 
        self.factory_settings.set_sensitive(self._canReset())
335
 
 
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,
340
 
            attrname))
341
 
        button.set_sensitive(False)
342
 
        self.factory_settings.set_sensitive(self._canReset())
343
 
 
344
 
    def _acceptButtonCb(self, unused_button):
345
 
        self._clearHistory()
346
 
        self.dialog.hide()
347
 
 
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)
355
 
 
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)
360
 
 
361
 
        # adjust controls as appropriate
362
 
        self.resets[attrname].set_sensitive(not self.settings.isDefault(
363
 
            attrname))
364
 
        self.factory_settings.set_sensitive(True)
365
 
 
366
 
    def _configureCb(self, unused_widget, event):
367
 
        self.settings.prefsDialogWidth = event.width
368
 
        self.settings.prefsDialogHeight = event.height
369
 
 
370
 
    def _canReset(self):
371
 
        for section in self.prefs.itervalues():
372
 
            for attrname in section:
373
 
                if not self.settings.isDefault(attrname):
374
 
                    return True
375
 
        return False
376
 
 
377
 
## Preference Test Cases
378
 
 
379
 
if False:
380
 
 
381
 
    from pitivi.settings import GlobalSettings
382
 
 
383
 
    options = (
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"),
393
 
    )
394
 
 
395
 
    for attrname, default in options:
396
 
        GlobalSettings.addConfigOption(attrname, default=default)
397
 
 
398
 
## Numeric
399
 
 
400
 
    PreferencesDialog.addNumericPreference('numericPreference1',
401
 
        label="Open Range",
402
 
        section="Test",
403
 
        description="This option has no upper bound",
404
 
        lower=-10)
405
 
 
406
 
    PreferencesDialog.addNumericPreference('numericPreference2',
407
 
        label="Closed Range",
408
 
        section="Test",
409
 
        description="This option has both upper and lower bounds",
410
 
        lower=-10,
411
 
        upper=10000)
412
 
 
413
 
## Text
414
 
 
415
 
    PreferencesDialog.addTextPreference('textPreference1',
416
 
        label="Unfiltered",
417
 
        section="Test",
418
 
        description="Anything can go in this box")
419
 
 
420
 
    PreferencesDialog.addTextPreference('textPreference2',
421
 
        label="Numbers only",
422
 
        section="Test",
423
 
        description="This input validates its input with a regex",
424
 
        matches="^-?\d+(\.\d+)?$")
425
 
 
426
 
## other
427
 
    PreferencesDialog.addPathPreference('aPathPreference',
428
 
        label="Test Path",
429
 
        section="Test",
430
 
        description="Test the path widget")
431
 
 
432
 
    PreferencesDialog.addChoicePreference('aChoicePreference',
433
 
        label="Swallow Velocity",
434
 
        section="Test",
435
 
        description="What is the airspeed velocity of a coconut-laden swallow?",
436
 
        choices=(
437
 
            ("42 Knots", 32),
438
 
            ("9 furlongs per fortnight", 42),
439
 
            ("I don't know that!", None)))
440
 
 
441
 
    PreferencesDialog.addChoicePreference('aLongChoicePreference',
442
 
        label="Favorite Color",
443
 
        section="Test",
444
 
        description="What is the color of the parrot's plumage?",
445
 
        choices=(
446
 
            ("Mauve", "Mauve"),
447
 
            ("Chartreuse", "Chartreuse"),
448
 
            ("Magenta", "Magenta"),
449
 
            ("Pink", "Pink"),
450
 
            ("Norwegian Blue", "Norwegian Blue"),
451
 
            ("Yellow Ochre", "Yellow Ochre")))
452
 
 
453
 
    PreferencesDialog.addTogglePreference('aTogglePreference',
454
 
        label="Test Toggle",
455
 
        section="Test",
456
 
        description="Test the toggle widget")
457
 
 
458
 
    PreferencesDialog.addFontPreference('aFontPreference',
459
 
        label="Foo Font",
460
 
        section="Test",
461
 
        description="Test the font widget")