widgets.py 19.4 KB
Newer Older
1
# Copyright (c) 2011 John Stowers
2 3
# SPDX-License-Identifier: GPL-3.0+
# License-Filename: LICENSES/GPL-3.0
4

5 6
import logging

7
from gi.repository import GLib, Gtk, Gio, Pango
John Stowers's avatar
John Stowers committed
8

9
from gtweak.tweakmodel import Tweak, TweakGroup
10
from gtweak.gsettings import GSettingsSetting, GSettingsFakeSetting, GSettingsMissingError
11
from gtweak.gshellwrapper import GnomeShellFactory
12

13
UI_BOX_SPACING = 4
14
_shell = GnomeShellFactory().get_shell()
15

16

17 18 19 20 21 22 23 24 25
def build_label_beside_widget(txt, *widget, **kwargs):
    """
    Builds a HBox containing widgets.

    Optional Kwargs:
        hbox: Use an existing HBox, not a new one
        info: Informational text to be shown after the label
        warning: Warning text to be shown after the label
    """
26 27 28 29 30
    def make_image(icon, tip):
        image = Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.MENU)
        image.set_tooltip_text(tip)
        return image

31 32 33 34 35 36 37 38
    def show_tooltip_when_ellipsized(label, x, y, keyboard_mode, tooltip):
        layout = label.get_layout()
        if layout.is_ellipsized():
            tooltip.set_text(label.get_text())
            return True
        else:
            return False

39 40 41
    if kwargs.get("hbox"):
        hbox = kwargs.get("hbox")
    else:
42
        hbox = Gtk.Box()
43

44
    hbox.props.spacing = UI_BOX_SPACING
45
    lbl = Gtk.Label(label=txt)
John Stowers's avatar
John Stowers committed
46
    lbl.props.ellipsize = Pango.EllipsizeMode.END
John Stowers's avatar
John Stowers committed
47
    lbl.props.xalign = 0.0
48 49
    lbl.set_has_tooltip(True)
    lbl.connect("query-tooltip", show_tooltip_when_ellipsized)
Alex Muñoz's avatar
Alex Muñoz committed
50
    hbox.pack_start(lbl, True, True, 0)
51 52

    if kwargs.get("info"):
53 54 55 56 57 58 59
        hbox.pack_start(
                make_image("dialog-information-symbolic", kwargs.get("info")),
                False, False, 0)
    if kwargs.get("warning"):
        hbox.pack_start(
                make_image("dialog-warning-symbolic", kwargs.get("warning")),
                False, False, 0)
60 61 62 63

    for w in widget:
        hbox.pack_start(w, False, False, 0)

64 65 66
    # For Atk, indicate that the rightmost widget, usually the switch relates to the
    # label. By convention this is true in the great majority of cases. Settings that
    # construct their own widgets will need to set this themselves
67
    lbl.set_mnemonic_widget(widget[-1])
68

Alex Muñoz's avatar
Alex Muñoz committed
69
    return hbox
John Stowers's avatar
John Stowers committed
70

71

John Stowers's avatar
John Stowers committed
72
def build_combo_box_text(selected, *values):
73 74 75 76
    """
    builds a GtkComboBox and model containing the supplied values.
    @values: a list of 2-tuples (value, name)
    """
John Stowers's avatar
John Stowers committed
77
    store = Gtk.ListStore(str, str)
78
    store.set_sort_column_id(0, Gtk.SortType.ASCENDING)
John Stowers's avatar
John Stowers committed
79 80 81

    selected_iter = None
    for (val, name) in values:
82
        _iter = store.append((val, name))
John Stowers's avatar
John Stowers committed
83 84 85 86 87 88
        if val == selected:
            selected_iter = _iter

    combo = Gtk.ComboBox(model=store)
    renderer = Gtk.CellRendererText()
    combo.pack_start(renderer, True)
89
    combo.add_attribute(renderer, 'markup', 1)
John Stowers's avatar
John Stowers committed
90 91 92 93 94
    if selected_iter:
        combo.set_active_iter(selected_iter)

    return combo

95

96 97 98 99 100
def build_horizontal_sizegroup():
    sg = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL)
    sg.props.ignore_hidden = True
    return sg

101

102 103 104 105 106
def build_tight_button(stock_id):
    button = Gtk.Button()
    button.set_relief(Gtk.ReliefStyle.NONE)
    button.set_focus_on_click(False)
    button.add(Gtk.Image.new_from_stock(stock_id, Gtk.IconSize.MENU))
107 108 109 110 111 112 113 114
    data = ".button {\n" \
           "-GtkButton-default-border : 0px;\n" \
           "-GtkButton-default-outside-border : 0px;\n" \
           "-GtkButton-inner-border: 0px;\n" \
           "-GtkWidget-focus-line-width : 0px;\n" \
           "-GtkWidget-focus-padding : 0px;\n" \
           "padding: 0px;\n" \
           "}"
115 116 117
    provider = Gtk.CssProvider()
    provider.load_from_data(data)
    # 600 = GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
118
    button.get_style_context().add_provider(provider, 600)
119 120
    return button

121

John Stowers's avatar
John Stowers committed
122
class _GSettingsTweak(Tweak):
Alex Muñoz's avatar
Alex Muñoz committed
123
    def __init__(self, name, schema_name, key_name, **options):
John Stowers's avatar
John Stowers committed
124 125
        self.schema_name = schema_name
        self.key_name = key_name
Alex Muñoz's avatar
Alex Muñoz committed
126
        self._extra_info = None
127 128
        if 'uid' not in options:
            options['uid'] = key_name
129 130 131
        try:
            self.settings = GSettingsSetting(schema_name, **options)
            Tweak.__init__(self,
Alex Muñoz's avatar
Alex Muñoz committed
132
                name,
133 134
                options.get("description",self.settings.schema_get_description(key_name)),
                **options)
135
        except GSettingsMissingError as e:
136
            self.settings = GSettingsFakeSetting()
137
            Tweak.__init__(self, "", "")
138
            self.loaded = False
139
            logging.info("GSetting missing %s", e)
140 141
        except KeyError:
            self.settings = GSettingsFakeSetting()
142
            Tweak.__init__(self, "", "")
143
            self.loaded = False
John Stowers's avatar
John Stowers committed
144
            logging.info("GSettings missing key %s (key %s)" % (schema_name, key_name))
145

146 147 148 149
        if options.get("logout_required") and self.loaded:
            self.settings.connect("changed::%s" % key_name, self._on_changed_notify_logout)

    def _on_changed_notify_logout(self, settings, key_name):
150
        self.notify_logout()
151

Alex Muñoz's avatar
Alex Muñoz committed
152 153 154 155 156 157
    @property
    def extra_info(self):
        if self._extra_info is None:
            self._extra_info = self.settings.schema_get_summary(self.key_name)
        return self._extra_info

158

159
class _DependableMixin(object):
John Stowers's avatar
John Stowers committed
160

161 162 163 164
    def add_dependency_on_tweak(self, depends, depends_how):
        if isinstance(depends, Tweak):
            self._depends = depends
            if depends_how is None:
165
                depends_how = lambda x, kn: x.get_boolean(kn)
166 167 168 169 170 171
            self._depends_how = depends_how

            sensitive = self._depends_how(
                                depends.settings,
                                depends.key_name,
            )
172
            self.set_sensitive(sensitive)
173 174 175 176

            depends.settings.connect("changed::%s" % depends.key_name, self._on_changed_depend)

    def _on_changed_depend(self, settings, key_name):
177
        sensitive = self._depends_how(settings, key_name)
178
        self.set_sensitive(sensitive)
179

180

181
class ListBoxTweakGroup(Gtk.ListBox, TweakGroup):
182 183 184
    def __init__(self, name, *tweaks, **options):
        if 'uid' not in options:
            options['uid'] = self.__class__.__name__
185 186 187 188
        if 'activatable' not in options:
            activatable = False
        else:
            activatable = options['activatable']
189
        Gtk.ListBox.__init__(self,
190 191
                        selection_mode=Gtk.SelectionMode.NONE,
                        name=options['uid'])
192
        self.get_style_context().add_class(
193
                        options.get('css_class', 'tweak-group'))
194
        self.props.margin = 20
195 196 197 198
        self.props.vexpand = False
        self.props.valign = Gtk.Align.START

        self._sg = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL)
199 200
        self._sg.props.ignore_hidden = True

201 202 203
        TweakGroup.__init__(self, name, **options)

        for t in tweaks:
204
            self.add_tweak_row(t, activatable)
205

206 207
    # FIXME: need to add remove_tweak_row and remove_tweak (which clears
    # the search cache etc)
208

209
    def add_tweak_row(self, t, activatable=False, position=None):
210
        if self.add_tweak(t):
211 212 213 214 215 216 217 218
            if isinstance(t, Gtk.ListBoxRow):
                row = t
            else:
                row = Gtk.ListBoxRow(name=t.uid)
                row.get_style_context().add_class("tweak")
                if isinstance(t, Title):
                    row.get_style_context().add_class("title")
                row.add(t)
219
            row.set_activatable(activatable)
220 221 222 223
            if position is None:
                self.add(row)
            else:
                self.insert(row, position)
224 225
            if t.widget_for_size_group:
                self._sg.add_widget(t.widget_for_size_group)
226
            return row
227

228

229
class GSettingsCheckTweak(Gtk.Box, _GSettingsTweak, _DependableMixin):
John Stowers's avatar
John Stowers committed
230
    def __init__(self, name, schema_name, key_name, **options):
231
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
Alex Muñoz's avatar
Alex Muñoz committed
232
        _GSettingsTweak.__init__(self, name, schema_name, key_name, **options)
John Stowers's avatar
John Stowers committed
233

234
        widget = Gtk.CheckButton.new_with_label(name)
John Stowers's avatar
John Stowers committed
235 236
        self.settings.bind(
                key_name,
237
                widget,
John Stowers's avatar
John Stowers committed
238
                "active", Gio.SettingsBindFlags.DEFAULT)
239
        self.add(widget)
John Stowers's avatar
John Stowers committed
240 241 242 243 244 245 246
        self.widget_for_size_group = None

        self.add_dependency_on_tweak(
                options.get("depends_on"),
                options.get("depends_how")
        )

247

248
class GSettingsSwitchTweak(Gtk.Box, _GSettingsTweak, _DependableMixin):
249
    def __init__(self, name, schema_name, key_name, **options):
250
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
Alex Muñoz's avatar
Alex Muñoz committed
251
        _GSettingsTweak.__init__(self, name, schema_name, key_name, **options)
John Stowers's avatar
John Stowers committed
252

John Stowers's avatar
John Stowers committed
253 254
        w = Gtk.Switch()
        self.settings.bind(key_name, w, "active", Gio.SettingsBindFlags.DEFAULT)
255

256 257 258 259 260
        self.add_dependency_on_tweak(
                options.get("depends_on"),
                options.get("depends_how")
        )

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
        vbox1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        vbox1.props.spacing = UI_BOX_SPACING
        lbl = Gtk.Label(label=name)
        lbl.props.ellipsize = Pango.EllipsizeMode.END
        lbl.props.xalign = 0.0
        vbox1.pack_start(lbl, True, True, 0)

        if options.get("desc"):
            description = options.get("desc")
            lbl_desc = Gtk.Label()
            lbl_desc.props.xalign = 0.0
            lbl_desc.set_line_wrap(True)
            lbl_desc.get_style_context().add_class("dim-label")
            lbl_desc.set_markup("<span size='small'>"+GLib.markup_escape_text(description)+"</span>")
            vbox1.pack_start(lbl_desc, True, True, 0)

        vbox2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        vbox2_upper = Gtk.Box()
        vbox2_lower = Gtk.Box()
        vbox2.pack_start(vbox2_upper, True, True, 0)
        vbox2.pack_start(w, False, False, 0)
        vbox2.pack_start(vbox2_lower, True, True, 0)

        self.pack_start(vbox1, True, True, 0)
        self.pack_start(vbox2, False, False, 0)
        self.widget_for_size_group = None

288

289
class GSettingsFontButtonTweak(Gtk.Box, _GSettingsTweak, _DependableMixin):
290
    def __init__(self, name, schema_name, key_name, **options):
291
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
Alex Muñoz's avatar
Alex Muñoz committed
292
        _GSettingsTweak.__init__(self, name, schema_name, key_name, **options)
John Stowers's avatar
John Stowers committed
293 294

        w = Gtk.FontButton()
295
        w.set_use_font(True)
John Stowers's avatar
John Stowers committed
296
        self.settings.bind(key_name, w, "font-name", Gio.SettingsBindFlags.DEFAULT)
297
        build_label_beside_widget(name, w, hbox=self)
298
        self.widget_for_size_group = w
John Stowers's avatar
John Stowers committed
299

300

301
class GSettingsRangeTweak(Gtk.Box, _GSettingsTweak, _DependableMixin):
Alex Muñoz's avatar
Alex Muñoz committed
302
    def __init__(self, name, schema_name, key_name, **options):
303
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
Alex Muñoz's avatar
Alex Muñoz committed
304
        _GSettingsTweak.__init__(self, name, schema_name, key_name, **options)
305

306
        # returned variant is range:(min, max)
307 308 309 310
        _min, _max = self.settings.get_range(key_name)[1]

        w = Gtk.HScale.new_with_range(_min, _max, options.get('adjustment_step', 1))
        self.settings.bind(key_name, w.get_adjustment(), "value", Gio.SettingsBindFlags.DEFAULT)
311 312

        build_label_beside_widget(self.name, w, hbox=self)
313 314
        self.widget_for_size_group = w

315

316
class GSettingsSpinButtonTweak(Gtk.Box, _GSettingsTweak, _DependableMixin):
317
    def __init__(self, name, schema_name, key_name, **options):
318
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
Alex Muñoz's avatar
Alex Muñoz committed
319
        _GSettingsTweak.__init__(self, name, schema_name, key_name, **options)
320

321
        # returned variant is range:(min, max)
322 323
        _min, _max = self.settings.get_range(key_name)[1]

324
        adjustment = Gtk.Adjustment(value=0, lower=_min, upper=_max, step_increment=options.get('adjustment_step', 1))
325 326 327 328
        w = Gtk.SpinButton()
        w.set_adjustment(adjustment)
        w.set_digits(options.get('digits', 0))
        self.settings.bind(key_name, adjustment, "value", Gio.SettingsBindFlags.DEFAULT)
329

330
        build_label_beside_widget(name, w, hbox=self)
331
        self.widget_for_size_group = w
332

Alex Muñoz's avatar
Alex Muñoz committed
333 334 335 336
        self.add_dependency_on_tweak(
                options.get("depends_on"),
                options.get("depends_how")
        )
337

338

339
class GSettingsComboEnumTweak(Gtk.Box, _GSettingsTweak, _DependableMixin):
340
    def __init__(self, name, schema_name, key_name, **options):
341
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
Alex Muñoz's avatar
Alex Muñoz committed
342
        _GSettingsTweak.__init__(self, name, schema_name, key_name, **options)
John Stowers's avatar
John Stowers committed
343 344 345 346 347

        _type, values = self.settings.get_range(key_name)
        value = self.settings.get_string(key_name)
        self.settings.connect('changed::'+self.key_name, self._on_setting_changed)

348
        w = build_combo_box_text(value, *[(v, v.replace("-", " ").title()) for v in values])
John Stowers's avatar
John Stowers committed
349 350 351
        w.connect('changed', self._on_combo_changed)
        self.combo = w

352
        build_label_beside_widget(name, w, hbox=self)
John Stowers's avatar
John Stowers committed
353 354
        self.widget_for_size_group = w

355
    def _values_are_different(self):
356 357
        # to stop bouncing back and forth between changed signals. I suspect there must be a nicer
        # Gio.settings_bind way to fix this
John Stowers's avatar
John Stowers committed
358
        return self.settings.get_string(self.key_name) != \
359
               self.combo.get_model().get_value(self.combo.get_active_iter(), 0)
John Stowers's avatar
John Stowers committed
360 361

    def _on_setting_changed(self, setting, key):
362 363 364 365 366
        assert key == self.key_name
        val = self.settings.get_string(key)
        model = self.combo.get_model()
        for row in model:
            if val == row[0]:
367
                self.combo.set_active_iter(row.iter)
368
                break
John Stowers's avatar
John Stowers committed
369 370

    def _on_combo_changed(self, combo):
371 372
        val = self.combo.get_model().get_value(self.combo.get_active_iter(), 0)
        if self._values_are_different():
373
            self.settings.set_string(self.key_name, val)
John Stowers's avatar
John Stowers committed
374

375

376
class GSettingsComboTweak(Gtk.Box, _GSettingsTweak, _DependableMixin):
377
    def __init__(self, name, schema_name, key_name, key_options, **options):
378
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
Alex Muñoz's avatar
Alex Muñoz committed
379
        _GSettingsTweak.__init__(self, name, schema_name, key_name, **options)
380

381 382
        # check key_options is iterable
        # and if supplied, check it is a list of 2-tuples
383 384 385
        assert len(key_options) >= 0
        if len(key_options):
            assert len(key_options[0]) == 2
Alex Muñoz's avatar
Alex Muñoz committed
386
        self._key_options = key_options
387

388
        self.combo = build_combo_box_text(
389
                    self.settings.get_string(self.key_name),
390
                    *key_options)
391
        self.combo.connect('changed', self._on_combo_changed)
392 393
        self.settings.connect('changed::'+self.key_name, self._on_setting_changed)

394
        build_label_beside_widget(name, self.combo, hbox=self)
395 396 397 398 399 400 401 402 403 404 405 406
        self.widget_for_size_group = self.combo

    def _on_setting_changed(self, setting, key):
        assert key == self.key_name
        val = self.settings.get_string(key)
        model = self.combo.get_model()
        for row in model:
            if val == row[0]:
                self.combo.set_active_iter(row.iter)
                return

        self.combo.set_active(-1)
407 408 409 410 411

    def _on_combo_changed(self, combo):
        _iter = combo.get_active_iter()
        if _iter:
            value = combo.get_model().get_value(_iter, 0)
412
            self.settings.set_string(self.key_name, value)
413

Alex Muñoz's avatar
Alex Muñoz committed
414 415 416
    @property
    def extra_info(self):
        if self._extra_info is None:
417 418
            self._extra_info = self.settings.schema_get_summary(self.key_name)
            self._extra_info += " " + " ".join(op[0] for op in self._key_options)
Alex Muñoz's avatar
Alex Muñoz committed
419 420
        return self._extra_info

421

422 423
class FileChooserButton(Gtk.FileChooserButton):
    def __init__(self, title, local_only, mimetypes):
424 425
        Gtk.FileChooserButton.__init__(self, title=title)

426 427 428 429 430
        if mimetypes:
            f = Gtk.FileFilter()
            for m in mimetypes:
                f.add_mime_type(m)
            self.set_filter(f)
431

432
        # self.set_width_chars(15)
433
        self.set_local_only(local_only)
434 435
        self.set_action(Gtk.FileChooserAction.OPEN)

436

437
class GSettingsFileChooserButtonTweak(Gtk.Box, _GSettingsTweak, _DependableMixin):
438
    def __init__(self, name, schema_name, key_name, local_only, mimetypes, **options):
439
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
Alex Muñoz's avatar
Alex Muñoz committed
440
        _GSettingsTweak.__init__(self, name, schema_name, key_name, **options)
441 442 443

        self.settings.connect('changed::'+self.key_name, self._on_setting_changed)

444
        self.filechooser = FileChooserButton(name, local_only, mimetypes)
445 446 447
        self.filechooser.set_uri(self.settings.get_string(self.key_name))
        self.filechooser.connect("file-set", self._on_file_set)

448
        build_label_beside_widget(name, self.filechooser, hbox=self)
449 450 451 452 453 454 455 456 457 458 459 460
        self.widget_for_size_group = self.filechooser

    def _values_are_different(self):
        return self.settings.get_string(self.key_name) != self.filechooser.get_uri()

    def _on_setting_changed(self, setting, key):
        self.filechooser.set_uri(self.settings.get_string(key))

    def _on_file_set(self, chooser):
        uri = self.filechooser.get_uri()
        if uri and self._values_are_different():
            self.settings.set_string(self.key_name, uri)
461

462

463 464 465
class GetterSetterSwitchTweak(Gtk.Box, Tweak):
    def __init__(self, name, **options):
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
466
        Tweak.__init__(self, name, options.get("description", ""), **options)
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482

        sw = Gtk.Switch()
        sw.set_active(self.get_active())
        sw.connect("notify::active", self._on_toggled)

        build_label_beside_widget(name, sw, hbox=self)

    def _on_toggled(self, sw, pspec):
        self.set_active(sw.get_active())

    def get_active(self):
        raise NotImplementedError()

    def set_active(self, v):
        raise NotImplementedError()

483

484
class Title(Gtk.Box, Tweak):
485
    def __init__(self, name, desc, **options):
486
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
487
        Tweak.__init__(self, name, desc, **options)
488
        widget = Gtk.Label()
489
        widget.set_markup("<b>"+GLib.markup_escape_text(name)+"</b>")
490
        widget.props.xalign = 0.0
491 492
        if not options.get("top"):
            widget.set_margin_top(10)
493
        self.add(widget)
494

495

Alex Muñoz's avatar
Alex Muñoz committed
496
class GSettingsSwitchTweakValue(Gtk.Box, _GSettingsTweak):
497

Alex Muñoz's avatar
Alex Muñoz committed
498 499
    def __init__(self, name, schema_name, key_name, **options):
        Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
Alex Muñoz's avatar
Alex Muñoz committed
500
        _GSettingsTweak.__init__(self, name, schema_name, key_name, **options)
Alex Muñoz's avatar
Alex Muñoz committed
501 502 503 504

        sw = Gtk.Switch()
        sw.set_active(self.get_active())
        sw.connect("notify::active", self._on_toggled)
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531

        vbox1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        vbox1.props.spacing = UI_BOX_SPACING
        lbl = Gtk.Label(label=name)
        lbl.props.ellipsize = Pango.EllipsizeMode.END
        lbl.props.xalign = 0.0
        vbox1.pack_start(lbl, True, True, 0)

        if options.get("desc"):
            description = options.get("desc")
            lbl_desc = Gtk.Label()
            lbl_desc.props.xalign = 0.0
            lbl_desc.set_line_wrap(True)
            lbl_desc.get_style_context().add_class("dim-label")
            lbl_desc.set_markup("<span size='small'>"+GLib.markup_escape_text(description)+"</span>")
            vbox1.pack_start(lbl_desc, True, True, 0)

        vbox2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        vbox2_upper = Gtk.Box()
        vbox2_lower = Gtk.Box()
        vbox2.pack_start(vbox2_upper, True, True, 0)
        vbox2.pack_start(sw, False, False, 0)
        vbox2.pack_start(vbox2_lower, True, True, 0)

        self.pack_start(vbox1, True, True, 0)
        self.pack_start(vbox2, False, False, 0)
        self.widget_for_size_group = None
Alex Muñoz's avatar
Alex Muñoz committed
532 533 534

    def _on_toggled(self, sw, pspec):
        self.set_active(sw.get_active())
535

Alex Muñoz's avatar
Alex Muñoz committed
536 537
    def set_active(self, v):
        raise NotImplementedError()
538

Alex Muñoz's avatar
Alex Muñoz committed
539 540
    def get_active(self):
        raise NotImplementedError()