1# -*- Mode: Python; py-indent-offset: 4 -*-
2# vim: tabstop=4 shiftwidth=4 expandtab
3#
4# Copyright (C) 2009 Johan Dahlin <johan@gnome.org>
5#               2010 Simon van der Linden <svdlinden@src.gnome.org>
6#
7# This library 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 library 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 library; if not, write to the Free Software
19# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
20# USA
21
22import sys
23import warnings
24from collections import abc
25
26from gi.repository import GObject
27from .._ossighelper import wakeup_on_signal, register_sigint_fallback
28from .._gtktemplate import Template
29from ..overrides import override, strip_boolean_result, deprecated_init
30from ..module import get_introspection_module
31from gi import PyGIDeprecationWarning
32
33
34Gtk = get_introspection_module('Gtk')
35
36__all__ = []
37
38
39Template = Template
40__all__.append('Template')
41
42if Gtk._version == '2.0':
43    warn_msg = "You have imported the Gtk 2.0 module.  Because Gtk 2.0 \
44was not designed for use with introspection some of the \
45interfaces and API will fail.  As such this is not supported \
46by the pygobject development team and we encourage you to \
47port your app to Gtk 3 or greater. PyGTK is the recomended \
48python module to use with Gtk 2.0"
49
50    warnings.warn(warn_msg, RuntimeWarning)
51
52
53class PyGTKDeprecationWarning(PyGIDeprecationWarning):
54    pass
55
56
57__all__.append('PyGTKDeprecationWarning')
58
59
60def _construct_target_list(targets):
61    """Create a list of TargetEntry items from a list of tuples in the form (target, flags, info)
62
63    The list can also contain existing TargetEntry items in which case the existing entry
64    is re-used in the return list.
65    """
66    target_entries = []
67    for entry in targets:
68        if not isinstance(entry, Gtk.TargetEntry):
69            entry = Gtk.TargetEntry.new(*entry)
70        target_entries.append(entry)
71    return target_entries
72
73
74__all__.append('_construct_target_list')
75
76
77def _extract_handler_and_args(obj_or_map, handler_name):
78    handler = None
79    if isinstance(obj_or_map, abc.Mapping):
80        handler = obj_or_map.get(handler_name, None)
81    else:
82        handler = getattr(obj_or_map, handler_name, None)
83
84    if handler is None:
85        raise AttributeError('Handler %s not found' % handler_name)
86
87    args = ()
88    if isinstance(handler, abc.Sequence):
89        if len(handler) == 0:
90            raise TypeError("Handler %s tuple can not be empty" % handler)
91        args = handler[1:]
92        handler = handler[0]
93
94    elif not callable(handler):
95        raise TypeError('Handler %s is not a method, function or tuple' % handler)
96
97    return handler, args
98
99
100# Exposed for unit-testing.
101__all__.append('_extract_handler_and_args')
102
103
104def _builder_connect_callback(builder, gobj, signal_name, handler_name, connect_obj, flags, obj_or_map):
105    handler, args = _extract_handler_and_args(obj_or_map, handler_name)
106
107    after = flags & GObject.ConnectFlags.AFTER
108    if connect_obj is not None:
109        if after:
110            gobj.connect_object_after(signal_name, handler, connect_obj, *args)
111        else:
112            gobj.connect_object(signal_name, handler, connect_obj, *args)
113    else:
114        if after:
115            gobj.connect_after(signal_name, handler, *args)
116        else:
117            gobj.connect(signal_name, handler, *args)
118
119
120class _FreezeNotifyManager(object):
121    def __init__(self, obj):
122        self.obj = obj
123
124    def __enter__(self):
125        pass
126
127    def __exit__(self, exc_type, exc_value, traceback):
128        self.obj.thaw_child_notify()
129
130
131class Widget(Gtk.Widget):
132
133    translate_coordinates = strip_boolean_result(Gtk.Widget.translate_coordinates)
134
135    if Gtk._version != "4.0":
136        def freeze_child_notify(self):
137            super(Widget, self).freeze_child_notify()
138            return _FreezeNotifyManager(self)
139
140    if Gtk._version != "4.0":
141        def drag_dest_set_target_list(self, target_list):
142            if (target_list is not None) and (not isinstance(target_list, Gtk.TargetList)):
143                target_list = Gtk.TargetList.new(_construct_target_list(target_list))
144            super(Widget, self).drag_dest_set_target_list(target_list)
145
146    if Gtk._version != "4.0":
147        def drag_source_set_target_list(self, target_list):
148            if (target_list is not None) and (not isinstance(target_list, Gtk.TargetList)):
149                target_list = Gtk.TargetList.new(_construct_target_list(target_list))
150            super(Widget, self).drag_source_set_target_list(target_list)
151
152    def style_get_property(self, property_name, value=None):
153        if value is None:
154            prop = self.find_style_property(property_name)
155            if prop is None:
156                raise ValueError('Class "%s" does not contain style property "%s"' %
157                                 (self, property_name))
158            value = GObject.Value(prop.value_type)
159
160        Gtk.Widget.style_get_property(self, property_name, value)
161        return value.get_value()
162
163
164Widget = override(Widget)
165__all__.append('Widget')
166
167
168class Container(Gtk.Container, Widget):
169
170    def __len__(self):
171        return len(self.get_children())
172
173    def __contains__(self, child):
174        return child in self.get_children()
175
176    def __iter__(self):
177        return iter(self.get_children())
178
179    def __bool__(self):
180        return True
181
182    # alias for Python 2.x object protocol
183    __nonzero__ = __bool__
184
185    if Gtk._version in ("2.0", "3.0"):
186
187        def child_get_property(self, child, property_name, value=None):
188            if value is None:
189                prop = self.find_child_property(property_name)
190                if prop is None:
191                    raise ValueError('Class "%s" does not contain child property "%s"' %
192                                     (self, property_name))
193                value = GObject.Value(prop.value_type)
194
195            Gtk.Container.child_get_property(self, child, property_name, value)
196            return value.get_value()
197
198        def child_get(self, child, *prop_names):
199            """Returns a list of child property values for the given names."""
200            return [self.child_get_property(child, name) for name in prop_names]
201
202        def child_set(self, child, **kwargs):
203            """Set a child properties on the given child to key/value pairs."""
204            for name, value in kwargs.items():
205                name = name.replace('_', '-')
206                self.child_set_property(child, name, value)
207
208        get_focus_chain = strip_boolean_result(Gtk.Container.get_focus_chain)
209
210
211Container = override(Container)
212__all__.append('Container')
213
214
215class Editable(Gtk.Editable):
216
217    def insert_text(self, text, position):
218        return super(Editable, self).insert_text(text, -1, position)
219
220    get_selection_bounds = strip_boolean_result(Gtk.Editable.get_selection_bounds, fail_ret=())
221
222
223Editable = override(Editable)
224__all__.append("Editable")
225
226
227if Gtk._version in ("2.0", "3.0"):
228    class Action(Gtk.Action):
229        __init__ = deprecated_init(Gtk.Action.__init__,
230                                   arg_names=('name', 'label', 'tooltip', 'stock_id'),
231                                   category=PyGTKDeprecationWarning)
232
233    Action = override(Action)
234    __all__.append("Action")
235
236    class RadioAction(Gtk.RadioAction):
237        __init__ = deprecated_init(Gtk.RadioAction.__init__,
238                                   arg_names=('name', 'label', 'tooltip', 'stock_id', 'value'),
239                                   category=PyGTKDeprecationWarning)
240
241    RadioAction = override(RadioAction)
242    __all__.append("RadioAction")
243
244    class ActionGroup(Gtk.ActionGroup):
245        __init__ = deprecated_init(Gtk.ActionGroup.__init__,
246                                   arg_names=('name',),
247                                   category=PyGTKDeprecationWarning)
248
249        def add_actions(self, entries, user_data=None):
250            """
251            The add_actions() method is a convenience method that creates a number
252            of gtk.Action  objects based on the information in the list of action
253            entry tuples contained in entries and adds them to the action group.
254            The entry tuples can vary in size from one to six items with the
255            following information:
256
257                * The name of the action. Must be specified.
258                * The stock id for the action. Optional with a default value of None
259                  if a label is specified.
260                * The label for the action. This field should typically be marked
261                  for translation, see the set_translation_domain() method. Optional
262                  with a default value of None if a stock id is specified.
263                * The accelerator for the action, in the format understood by the
264                  gtk.accelerator_parse() function. Optional with a default value of
265                  None.
266                * The tooltip for the action. This field should typically be marked
267                  for translation, see the set_translation_domain() method. Optional
268                  with a default value of None.
269                * The callback function invoked when the action is activated.
270                  Optional with a default value of None.
271
272            The "activate" signals of the actions are connected to the callbacks and
273            their accel paths are set to <Actions>/group-name/action-name.
274            """
275            try:
276                iter(entries)
277            except (TypeError):
278                raise TypeError('entries must be iterable')
279
280            def _process_action(name, stock_id=None, label=None, accelerator=None, tooltip=None, callback=None):
281                action = Action(name=name, label=label, tooltip=tooltip, stock_id=stock_id)
282                if callback is not None:
283                    if user_data is None:
284                        action.connect('activate', callback)
285                    else:
286                        action.connect('activate', callback, user_data)
287
288                self.add_action_with_accel(action, accelerator)
289
290            for e in entries:
291                # using inner function above since entries can leave out optional arguments
292                _process_action(*e)
293
294        def add_toggle_actions(self, entries, user_data=None):
295            """
296            The add_toggle_actions() method is a convenience method that creates a
297            number of gtk.ToggleAction objects based on the information in the list
298            of action entry tuples contained in entries and adds them to the action
299            group. The toggle action entry tuples can vary in size from one to seven
300            items with the following information:
301
302                * The name of the action. Must be specified.
303                * The stock id for the action. Optional with a default value of None
304                  if a label is specified.
305                * The label for the action. This field should typically be marked
306                  for translation, see the set_translation_domain() method. Optional
307                  with a default value of None if a stock id is specified.
308                * The accelerator for the action, in the format understood by the
309                  gtk.accelerator_parse() function. Optional with a default value of
310                  None.
311                * The tooltip for the action. This field should typically be marked
312                  for translation, see the set_translation_domain() method. Optional
313                  with a default value of None.
314                * The callback function invoked when the action is activated.
315                  Optional with a default value of None.
316                * A flag indicating whether the toggle action is active. Optional
317                  with a default value of False.
318
319            The "activate" signals of the actions are connected to the callbacks and
320            their accel paths are set to <Actions>/group-name/action-name.
321            """
322
323            try:
324                iter(entries)
325            except (TypeError):
326                raise TypeError('entries must be iterable')
327
328            def _process_action(name, stock_id=None, label=None, accelerator=None, tooltip=None, callback=None, is_active=False):
329                action = Gtk.ToggleAction(name=name, label=label, tooltip=tooltip, stock_id=stock_id)
330                action.set_active(is_active)
331                if callback is not None:
332                    if user_data is None:
333                        action.connect('activate', callback)
334                    else:
335                        action.connect('activate', callback, user_data)
336
337                self.add_action_with_accel(action, accelerator)
338
339            for e in entries:
340                # using inner function above since entries can leave out optional arguments
341                _process_action(*e)
342
343        def add_radio_actions(self, entries, value=None, on_change=None, user_data=None):
344            """
345            The add_radio_actions() method is a convenience method that creates a
346            number of gtk.RadioAction objects based on the information in the list
347            of action entry tuples contained in entries and adds them to the action
348            group. The entry tuples can vary in size from one to six items with the
349            following information:
350
351                * The name of the action. Must be specified.
352                * The stock id for the action. Optional with a default value of None
353                  if a label is specified.
354                * The label for the action. This field should typically be marked
355                  for translation, see the set_translation_domain() method. Optional
356                  with a default value of None if a stock id is specified.
357                * The accelerator for the action, in the format understood by the
358                  gtk.accelerator_parse() function. Optional with a default value of
359                  None.
360                * The tooltip for the action. This field should typically be marked
361                  for translation, see the set_translation_domain() method. Optional
362                  with a default value of None.
363                * The value to set on the radio action. Optional with a default
364                  value of 0. Should be specified in applications.
365
366            The value parameter specifies the radio action that should be set
367            active. The "changed" signal of the first radio action is connected to
368            the on_change callback (if specified and not None) and the accel paths
369            of the actions are set to <Actions>/group-name/action-name.
370            """
371            try:
372                iter(entries)
373            except (TypeError):
374                raise TypeError('entries must be iterable')
375
376            first_action = None
377
378            def _process_action(group_source, name, stock_id=None, label=None, accelerator=None, tooltip=None, entry_value=0):
379                action = RadioAction(name=name, label=label, tooltip=tooltip, stock_id=stock_id, value=entry_value)
380
381                if Gtk._version == '3.0':
382                    action.join_group(group_source)
383
384                if value == entry_value:
385                    action.set_active(True)
386
387                self.add_action_with_accel(action, accelerator)
388                return action
389
390            for e in entries:
391                # using inner function above since entries can leave out optional arguments
392                action = _process_action(first_action, *e)
393                if first_action is None:
394                    first_action = action
395
396            if first_action is not None and on_change is not None:
397                if user_data is None:
398                    first_action.connect('changed', on_change)
399                else:
400                    first_action.connect('changed', on_change, user_data)
401
402    ActionGroup = override(ActionGroup)
403    __all__.append('ActionGroup')
404
405    class UIManager(Gtk.UIManager):
406        def add_ui_from_string(self, buffer):
407            if not isinstance(buffer, str):
408                raise TypeError('buffer must be a string')
409
410            length = _get_utf8_length(buffer)
411
412            return Gtk.UIManager.add_ui_from_string(self, buffer, length)
413
414        def insert_action_group(self, buffer, length=-1):
415            return Gtk.UIManager.insert_action_group(self, buffer, length)
416
417    UIManager = override(UIManager)
418    __all__.append('UIManager')
419
420
421class ComboBox(Gtk.ComboBox, Container):
422    get_active_iter = strip_boolean_result(Gtk.ComboBox.get_active_iter)
423
424
425ComboBox = override(ComboBox)
426__all__.append('ComboBox')
427
428
429class Box(Gtk.Box):
430    __init__ = deprecated_init(Gtk.Box.__init__,
431                               arg_names=('homogeneous', 'spacing'),
432                               category=PyGTKDeprecationWarning)
433
434
435Box = override(Box)
436__all__.append('Box')
437
438
439class SizeGroup(Gtk.SizeGroup):
440    __init__ = deprecated_init(Gtk.SizeGroup.__init__,
441                               arg_names=('mode',),
442                               deprecated_defaults={'mode': Gtk.SizeGroupMode.VERTICAL},
443                               category=PyGTKDeprecationWarning)
444
445
446SizeGroup = override(SizeGroup)
447__all__.append('SizeGroup')
448
449
450if Gtk._version in ("2.0", "3.0"):
451    class MenuItem(Gtk.MenuItem):
452        __init__ = deprecated_init(Gtk.MenuItem.__init__,
453                                   arg_names=('label',),
454                                   category=PyGTKDeprecationWarning)
455
456    MenuItem = override(MenuItem)
457    __all__.append('MenuItem')
458
459
460def _get_utf8_length(string):
461    assert isinstance(string, str)
462    if not isinstance(string, bytes):
463        string = string.encode("utf-8")
464    return len(string)
465
466
467class Builder(Gtk.Builder):
468    def connect_signals(self, obj_or_map):
469        """Connect signals specified by this builder to a name, handler mapping.
470
471        Connect signal, name, and handler sets specified in the builder with
472        the given mapping "obj_or_map". The handler/value aspect of the mapping
473        can also contain a tuple in the form of (handler [,arg1 [,argN]])
474        allowing for extra arguments to be passed to the handler. For example:
475
476        .. code-block:: python
477
478            builder.connect_signals({'on_clicked': (on_clicked, arg1, arg2)})
479        """
480        self.connect_signals_full(_builder_connect_callback, obj_or_map)
481
482    def add_from_string(self, buffer):
483        if not isinstance(buffer, str):
484            raise TypeError('buffer must be a string')
485
486        length = _get_utf8_length(buffer)
487
488        return Gtk.Builder.add_from_string(self, buffer, length)
489
490    def add_objects_from_string(self, buffer, object_ids):
491        if not isinstance(buffer, str):
492            raise TypeError('buffer must be a string')
493
494        length = _get_utf8_length(buffer)
495
496        return Gtk.Builder.add_objects_from_string(self, buffer, length, object_ids)
497
498
499Builder = override(Builder)
500__all__.append('Builder')
501
502
503# NOTE: This must come before any other Window/Dialog subclassing, to ensure
504# that we have a correct inheritance hierarchy.
505
506_window_init = deprecated_init(Gtk.Window.__init__,
507                               arg_names=('type',),
508                               category=PyGTKDeprecationWarning,
509                               stacklevel=3)
510
511
512class Window(Gtk.Window):
513    def __init__(self, *args, **kwargs):
514        if not initialized:
515            raise RuntimeError(
516                "Gtk couldn't be initialized. "
517                "Use Gtk.init_check() if you want to handle this case.")
518        _window_init(self, *args, **kwargs)
519
520
521Window = override(Window)
522__all__.append('Window')
523
524
525class Dialog(Gtk.Dialog, Container):
526    _old_arg_names = ('title', 'parent', 'flags', 'buttons', '_buttons_property')
527    _init = deprecated_init(Gtk.Dialog.__init__,
528                            arg_names=('title', 'transient_for', 'flags',
529                                       'add_buttons', 'buttons'),
530                            ignore=('flags', 'add_buttons'),
531                            deprecated_aliases={'transient_for': 'parent',
532                                                'buttons': '_buttons_property'},
533                            category=PyGTKDeprecationWarning)
534
535    def __init__(self, *args, **kwargs):
536
537        new_kwargs = kwargs.copy()
538        old_kwargs = dict(zip(self._old_arg_names, args))
539        old_kwargs.update(kwargs)
540
541        # Increment the warning stacklevel for sub-classes which implement their own __init__.
542        stacklevel = 2
543        if self.__class__ != Dialog and self.__class__.__init__ != Dialog.__init__:
544            stacklevel += 1
545
546        # buttons was overloaded by PyGtk but is needed for Gtk.MessageDialog
547        # as a pass through, so type check the argument and give a deprecation
548        # when it is not of type Gtk.ButtonsType
549        add_buttons = old_kwargs.get('buttons', None)
550        if add_buttons is not None and not isinstance(add_buttons, Gtk.ButtonsType):
551            warnings.warn('The "buttons" argument must be a Gtk.ButtonsType enum value. '
552                          'Please use the "add_buttons" method for adding buttons. '
553                          'See: https://wiki.gnome.org/PyGObject/InitializerDeprecations',
554                          PyGTKDeprecationWarning, stacklevel=stacklevel)
555            new_kwargs.pop('buttons', None)
556        else:
557            add_buttons = None
558
559        flags = old_kwargs.get('flags', 0)
560        if flags:
561            warnings.warn('The "flags" argument for dialog construction is deprecated. '
562                          'Please use initializer keywords: modal=True and/or destroy_with_parent=True. '
563                          'See: https://wiki.gnome.org/PyGObject/InitializerDeprecations',
564                          PyGTKDeprecationWarning, stacklevel=stacklevel)
565
566            if flags & Gtk.DialogFlags.MODAL:
567                new_kwargs['modal'] = True
568
569            if flags & Gtk.DialogFlags.DESTROY_WITH_PARENT:
570                new_kwargs['destroy_with_parent'] = True
571
572        self._init(*args, **new_kwargs)
573
574        if add_buttons:
575            self.add_buttons(*add_buttons)
576
577    def run(self, *args, **kwargs):
578        with register_sigint_fallback(self.destroy):
579            with wakeup_on_signal():
580                return Gtk.Dialog.run(self, *args, **kwargs)
581
582    action_area = property(lambda dialog: dialog.get_action_area())
583    vbox = property(lambda dialog: dialog.get_content_area())
584
585    def add_buttons(self, *args):
586        """
587        The add_buttons() method adds several buttons to the Gtk.Dialog using
588        the button data passed as arguments to the method. This method is the
589        same as calling the Gtk.Dialog.add_button() repeatedly. The button data
590        pairs - button text (or stock ID) and a response ID integer are passed
591        individually. For example:
592
593        .. code-block:: python
594
595            dialog.add_buttons(Gtk.STOCK_OPEN, 42, "Close", Gtk.ResponseType.CLOSE)
596
597        will add "Open" and "Close" buttons to dialog.
598        """
599        def _button(b):
600            while b:
601                try:
602                    t, r = b[0:2]
603                except ValueError:
604                    raise ValueError('Must pass an even number of arguments')
605                b = b[2:]
606                yield t, r
607
608        for text, response in _button(args):
609            self.add_button(text, response)
610
611
612Dialog = override(Dialog)
613__all__.append('Dialog')
614
615
616class MessageDialog(Gtk.MessageDialog, Dialog):
617    __init__ = deprecated_init(Gtk.MessageDialog.__init__,
618                               arg_names=('parent', 'flags', 'message_type',
619                                          'buttons', 'message_format'),
620                               deprecated_aliases={'text': 'message_format',
621                                                   'message_type': 'type'},
622                               category=PyGTKDeprecationWarning)
623
624    def format_secondary_text(self, message_format):
625        self.set_property('secondary-use-markup', False)
626        self.set_property('secondary-text', message_format)
627
628    def format_secondary_markup(self, message_format):
629        self.set_property('secondary-use-markup', True)
630        self.set_property('secondary-text', message_format)
631
632
633MessageDialog = override(MessageDialog)
634__all__.append('MessageDialog')
635
636
637if Gtk._version in ("2.0", "3.0"):
638    class ColorSelectionDialog(Gtk.ColorSelectionDialog):
639        __init__ = deprecated_init(Gtk.ColorSelectionDialog.__init__,
640                                   arg_names=('title',),
641                                   category=PyGTKDeprecationWarning)
642
643    ColorSelectionDialog = override(ColorSelectionDialog)
644    __all__.append('ColorSelectionDialog')
645
646
647class FileChooserDialog(Gtk.FileChooserDialog):
648    __init__ = deprecated_init(Gtk.FileChooserDialog.__init__,
649                               arg_names=('title', 'parent', 'action', 'buttons'),
650                               category=PyGTKDeprecationWarning)
651
652
653FileChooserDialog = override(FileChooserDialog)
654__all__.append('FileChooserDialog')
655
656
657if Gtk._version in ("2.0", "3.0"):
658    class FontSelectionDialog(Gtk.FontSelectionDialog):
659        __init__ = deprecated_init(Gtk.FontSelectionDialog.__init__,
660                                   arg_names=('title',),
661                                   category=PyGTKDeprecationWarning)
662
663    FontSelectionDialog = override(FontSelectionDialog)
664    __all__.append('FontSelectionDialog')
665
666
667if Gtk._version in ("2.0", "3.0"):
668    class RecentChooserDialog(Gtk.RecentChooserDialog):
669        # Note, the "manager" keyword must work across the entire 3.x series because
670        # "recent_manager" is not backwards compatible with PyGObject versions prior to 3.10.
671        __init__ = deprecated_init(Gtk.RecentChooserDialog.__init__,
672                                   arg_names=('title', 'parent', 'recent_manager', 'buttons'),
673                                   deprecated_aliases={'recent_manager': 'manager'},
674                                   category=PyGTKDeprecationWarning)
675
676    RecentChooserDialog = override(RecentChooserDialog)
677    __all__.append('RecentChooserDialog')
678
679
680class IconView(Gtk.IconView):
681    __init__ = deprecated_init(Gtk.IconView.__init__,
682                               arg_names=('model',),
683                               category=PyGTKDeprecationWarning)
684
685    get_item_at_pos = strip_boolean_result(Gtk.IconView.get_item_at_pos)
686    get_visible_range = strip_boolean_result(Gtk.IconView.get_visible_range)
687    get_dest_item_at_pos = strip_boolean_result(Gtk.IconView.get_dest_item_at_pos)
688
689
690IconView = override(IconView)
691__all__.append('IconView')
692
693
694if Gtk._version in ("2.0", "3.0"):
695    class ToolButton(Gtk.ToolButton):
696        __init__ = deprecated_init(Gtk.ToolButton.__init__,
697                                   arg_names=('stock_id',),
698                                   category=PyGTKDeprecationWarning)
699
700    ToolButton = override(ToolButton)
701    __all__.append('ToolButton')
702
703
704class IMContext(Gtk.IMContext):
705    get_surrounding = strip_boolean_result(Gtk.IMContext.get_surrounding)
706
707
708IMContext = override(IMContext)
709__all__.append('IMContext')
710
711
712class RecentInfo(Gtk.RecentInfo):
713    get_application_info = strip_boolean_result(Gtk.RecentInfo.get_application_info)
714
715
716RecentInfo = override(RecentInfo)
717__all__.append('RecentInfo')
718
719
720class TextBuffer(Gtk.TextBuffer):
721
722    def create_tag(self, tag_name=None, **properties):
723        """Creates a tag and adds it to the tag table of the TextBuffer.
724
725        :param str tag_name:
726            Name of the new tag, or None
727        :param **properties:
728            Keyword list of properties and their values
729
730        This is equivalent to creating a Gtk.TextTag and then adding the
731        tag to the buffer's tag table. The returned tag is owned by
732        the buffer's tag table.
733
734        If ``tag_name`` is None, the tag is anonymous.
735
736        If ``tag_name`` is not None, a tag called ``tag_name`` must not already
737        exist in the tag table for this buffer.
738
739        Properties are passed as a keyword list of names and values (e.g.
740        foreground='DodgerBlue', weight=Pango.Weight.BOLD)
741
742        :returns:
743            A new tag.
744        """
745
746        tag = Gtk.TextTag(name=tag_name, **properties)
747        self.get_tag_table().add(tag)
748        return tag
749
750    def create_mark(self, mark_name, where, left_gravity=False):
751        return Gtk.TextBuffer.create_mark(self, mark_name, where, left_gravity)
752
753    def set_text(self, text, length=-1):
754        Gtk.TextBuffer.set_text(self, text, length)
755
756    def insert(self, iter, text, length=-1):
757        if not isinstance(text, str):
758            raise TypeError('text must be a string, not %s' % type(text))
759
760        Gtk.TextBuffer.insert(self, iter, text, length)
761
762    def insert_with_tags(self, iter, text, *tags):
763        start_offset = iter.get_offset()
764        self.insert(iter, text)
765
766        if not tags:
767            return
768
769        start = self.get_iter_at_offset(start_offset)
770
771        for tag in tags:
772            self.apply_tag(tag, start, iter)
773
774    def insert_with_tags_by_name(self, iter, text, *tags):
775        tag_objs = []
776
777        for tag in tags:
778            tag_obj = self.get_tag_table().lookup(tag)
779            if not tag_obj:
780                raise ValueError('unknown text tag: %s' % tag)
781            tag_objs.append(tag_obj)
782
783        self.insert_with_tags(iter, text, *tag_objs)
784
785    def insert_at_cursor(self, text, length=-1):
786        if not isinstance(text, str):
787            raise TypeError('text must be a string, not %s' % type(text))
788
789        Gtk.TextBuffer.insert_at_cursor(self, text, length)
790
791    get_selection_bounds = strip_boolean_result(Gtk.TextBuffer.get_selection_bounds, fail_ret=())
792
793
794TextBuffer = override(TextBuffer)
795__all__.append('TextBuffer')
796
797
798class TextIter(Gtk.TextIter):
799    forward_search = strip_boolean_result(Gtk.TextIter.forward_search)
800    backward_search = strip_boolean_result(Gtk.TextIter.backward_search)
801
802
803TextIter = override(TextIter)
804__all__.append('TextIter')
805
806
807class TreeModel(Gtk.TreeModel):
808    def __len__(self):
809        return self.iter_n_children(None)
810
811    def __bool__(self):
812        return True
813
814    # alias for Python 2.x object protocol
815    __nonzero__ = __bool__
816
817    def _getiter(self, key):
818        if isinstance(key, Gtk.TreeIter):
819            return key
820        elif isinstance(key, int) and key < 0:
821            index = len(self) + key
822            if index < 0:
823                raise IndexError("row index is out of bounds: %d" % key)
824            return self.get_iter(index)
825        else:
826            try:
827                aiter = self.get_iter(key)
828            except ValueError:
829                raise IndexError("could not find tree path '%s'" % key)
830            return aiter
831
832    def sort_new_with_model(self):
833        super_object = super(TreeModel, self)
834        if hasattr(super_object, "sort_new_with_model"):
835            return super_object.sort_new_with_model()
836        else:
837            return TreeModelSort.new_with_model(self)
838
839    def _coerce_path(self, path):
840        if isinstance(path, Gtk.TreePath):
841            return path
842        else:
843            return TreePath(path)
844
845    def __getitem__(self, key):
846        aiter = self._getiter(key)
847        return TreeModelRow(self, aiter)
848
849    def __setitem__(self, key, value):
850        row = self[key]
851        self.set_row(row.iter, value)
852
853    def __delitem__(self, key):
854        aiter = self._getiter(key)
855        self.remove(aiter)
856
857    def __iter__(self):
858        return TreeModelRowIter(self, self.get_iter_first())
859
860    get_iter_first = strip_boolean_result(Gtk.TreeModel.get_iter_first)
861    iter_children = strip_boolean_result(Gtk.TreeModel.iter_children)
862    iter_nth_child = strip_boolean_result(Gtk.TreeModel.iter_nth_child)
863    iter_parent = strip_boolean_result(Gtk.TreeModel.iter_parent)
864    get_iter_from_string = strip_boolean_result(Gtk.TreeModel.get_iter_from_string,
865                                                ValueError, 'invalid tree path')
866
867    def get_iter(self, path):
868        path = self._coerce_path(path)
869        success, aiter = super(TreeModel, self).get_iter(path)
870        if not success:
871            raise ValueError("invalid tree path '%s'" % path)
872        return aiter
873
874    def iter_next(self, aiter):
875        next_iter = aiter.copy()
876        success = super(TreeModel, self).iter_next(next_iter)
877        if success:
878            return next_iter
879
880    def iter_previous(self, aiter):
881        prev_iter = aiter.copy()
882        success = super(TreeModel, self).iter_previous(prev_iter)
883        if success:
884            return prev_iter
885
886    def _convert_row(self, row):
887        # TODO: Accept a dictionary for row
888        # model.append(None,{COLUMN_ICON: icon, COLUMN_NAME: name})
889        if isinstance(row, str):
890            raise TypeError('Expected a list or tuple, but got str')
891
892        n_columns = self.get_n_columns()
893        if len(row) != n_columns:
894            raise ValueError('row sequence has the incorrect number of elements')
895
896        result = []
897        columns = []
898        for cur_col, value in enumerate(row):
899            # do not try to set None values, they are causing warnings
900            if value is None:
901                continue
902            result.append(self._convert_value(cur_col, value))
903            columns.append(cur_col)
904        return (result, columns)
905
906    def set_row(self, treeiter, row):
907        converted_row, columns = self._convert_row(row)
908        for column in columns:
909            self.set_value(treeiter, column, row[column])
910
911    def _convert_value(self, column, value):
912        '''Convert value to a GObject.Value of the expected type'''
913
914        if isinstance(value, GObject.Value):
915            return value
916        return GObject.Value(self.get_column_type(column), value)
917
918    def get(self, treeiter, *columns):
919        n_columns = self.get_n_columns()
920
921        values = []
922        for col in columns:
923            if not isinstance(col, int):
924                raise TypeError("column numbers must be ints")
925
926            if col < 0 or col >= n_columns:
927                raise ValueError("column number is out of range")
928
929            values.append(self.get_value(treeiter, col))
930
931        return tuple(values)
932
933    #
934    # Signals supporting python iterables as tree paths
935    #
936    def row_changed(self, path, iter):
937        return super(TreeModel, self).row_changed(self._coerce_path(path), iter)
938
939    def row_inserted(self, path, iter):
940        return super(TreeModel, self).row_inserted(self._coerce_path(path), iter)
941
942    def row_has_child_toggled(self, path, iter):
943        return super(TreeModel, self).row_has_child_toggled(self._coerce_path(path),
944                                                            iter)
945
946    def row_deleted(self, path):
947        return super(TreeModel, self).row_deleted(self._coerce_path(path))
948
949    def rows_reordered(self, path, iter, new_order):
950        return super(TreeModel, self).rows_reordered(self._coerce_path(path),
951                                                     iter, new_order)
952
953
954TreeModel = override(TreeModel)
955__all__.append('TreeModel')
956
957
958class TreeSortable(Gtk.TreeSortable, ):
959
960    get_sort_column_id = strip_boolean_result(Gtk.TreeSortable.get_sort_column_id, fail_ret=(None, None))
961
962    def set_sort_func(self, sort_column_id, sort_func, user_data=None):
963        super(TreeSortable, self).set_sort_func(sort_column_id, sort_func, user_data)
964
965    def set_default_sort_func(self, sort_func, user_data=None):
966        super(TreeSortable, self).set_default_sort_func(sort_func, user_data)
967
968
969TreeSortable = override(TreeSortable)
970__all__.append('TreeSortable')
971
972
973class TreeModelSort(Gtk.TreeModelSort):
974    __init__ = deprecated_init(Gtk.TreeModelSort.__init__,
975                               arg_names=('model',),
976                               category=PyGTKDeprecationWarning)
977
978    if not hasattr(Gtk.TreeModelSort, "new_with_model"):
979        @classmethod
980        def new_with_model(self, child_model):
981            return TreeModel.sort_new_with_model(child_model)
982
983
984TreeModelSort = override(TreeModelSort)
985__all__.append('TreeModelSort')
986
987
988class ListStore(Gtk.ListStore, TreeModel, TreeSortable):
989    def __init__(self, *column_types):
990        Gtk.ListStore.__init__(self)
991        self.set_column_types(column_types)
992
993    def _do_insert(self, position, row):
994        if row is not None:
995            row, columns = self._convert_row(row)
996            treeiter = self.insert_with_valuesv(position, columns, row)
997        else:
998            treeiter = Gtk.ListStore.insert(self, position)
999
1000        return treeiter
1001
1002    def append(self, row=None):
1003        if row:
1004            return self._do_insert(-1, row)
1005        # gtk_list_store_insert() does not know about the "position == -1"
1006        # case, so use append() here
1007        else:
1008            return Gtk.ListStore.append(self)
1009
1010    def prepend(self, row=None):
1011        return self._do_insert(0, row)
1012
1013    def insert(self, position, row=None):
1014        return self._do_insert(position, row)
1015
1016    def insert_before(self, sibling, row=None):
1017        if row is not None:
1018            if sibling is None:
1019                position = -1
1020            else:
1021                position = self.get_path(sibling).get_indices()[-1]
1022            return self._do_insert(position, row)
1023
1024        return Gtk.ListStore.insert_before(self, sibling)
1025
1026    def insert_after(self, sibling, row=None):
1027        if row is not None:
1028            if sibling is None:
1029                position = 0
1030            else:
1031                position = self.get_path(sibling).get_indices()[-1] + 1
1032            return self._do_insert(position, row)
1033
1034        return Gtk.ListStore.insert_after(self, sibling)
1035
1036    def set_value(self, treeiter, column, value):
1037        value = self._convert_value(column, value)
1038        Gtk.ListStore.set_value(self, treeiter, column, value)
1039
1040    def set(self, treeiter, *args):
1041        def _set_lists(cols, vals):
1042            if len(cols) != len(vals):
1043                raise TypeError('The number of columns do not match the number of values')
1044
1045            columns = []
1046            values = []
1047            for col_num, value in zip(cols, vals):
1048                if not isinstance(col_num, int):
1049                    raise TypeError('TypeError: Expected integer argument for column.')
1050
1051                columns.append(col_num)
1052                values.append(self._convert_value(col_num, value))
1053
1054            Gtk.ListStore.set(self, treeiter, columns, values)
1055
1056        if args:
1057            if isinstance(args[0], int):
1058                _set_lists(args[::2], args[1::2])
1059            elif isinstance(args[0], (tuple, list)):
1060                if len(args) != 2:
1061                    raise TypeError('Too many arguments')
1062                _set_lists(args[0], args[1])
1063            elif isinstance(args[0], dict):
1064                _set_lists(list(args[0]), args[0].values())
1065            else:
1066                raise TypeError('Argument list must be in the form of (column, value, ...), ((columns,...), (values, ...)) or {column: value}.  No -1 termination is needed.')
1067
1068
1069ListStore = override(ListStore)
1070__all__.append('ListStore')
1071
1072
1073class TreeModelRow(object):
1074
1075    def __init__(self, model, iter_or_path):
1076        if not isinstance(model, Gtk.TreeModel):
1077            raise TypeError("expected Gtk.TreeModel, %s found" % type(model).__name__)
1078        self.model = model
1079        if isinstance(iter_or_path, Gtk.TreePath):
1080            self.iter = model.get_iter(iter_or_path)
1081        elif isinstance(iter_or_path, Gtk.TreeIter):
1082            self.iter = iter_or_path
1083        else:
1084            raise TypeError("expected Gtk.TreeIter or Gtk.TreePath, "
1085                            "%s found" % type(iter_or_path).__name__)
1086
1087    @property
1088    def path(self):
1089        return self.model.get_path(self.iter)
1090
1091    @property
1092    def next(self):
1093        return self.get_next()
1094
1095    @property
1096    def previous(self):
1097        return self.get_previous()
1098
1099    @property
1100    def parent(self):
1101        return self.get_parent()
1102
1103    def get_next(self):
1104        next_iter = self.model.iter_next(self.iter)
1105        if next_iter:
1106            return TreeModelRow(self.model, next_iter)
1107
1108    def get_previous(self):
1109        prev_iter = self.model.iter_previous(self.iter)
1110        if prev_iter:
1111            return TreeModelRow(self.model, prev_iter)
1112
1113    def get_parent(self):
1114        parent_iter = self.model.iter_parent(self.iter)
1115        if parent_iter:
1116            return TreeModelRow(self.model, parent_iter)
1117
1118    def __getitem__(self, key):
1119        if isinstance(key, int):
1120            if key >= self.model.get_n_columns():
1121                raise IndexError("column index is out of bounds: %d" % key)
1122            elif key < 0:
1123                key = self._convert_negative_index(key)
1124            return self.model.get_value(self.iter, key)
1125        elif isinstance(key, slice):
1126            start, stop, step = key.indices(self.model.get_n_columns())
1127            alist = []
1128            for i in range(start, stop, step):
1129                alist.append(self.model.get_value(self.iter, i))
1130            return alist
1131        elif isinstance(key, tuple):
1132            return [self[k] for k in key]
1133        else:
1134            raise TypeError("indices must be integers, slice or tuple, not %s"
1135                            % type(key).__name__)
1136
1137    def __setitem__(self, key, value):
1138        if isinstance(key, int):
1139            if key >= self.model.get_n_columns():
1140                raise IndexError("column index is out of bounds: %d" % key)
1141            elif key < 0:
1142                key = self._convert_negative_index(key)
1143            self.model.set_value(self.iter, key, value)
1144        elif isinstance(key, slice):
1145            start, stop, step = key.indices(self.model.get_n_columns())
1146            indexList = range(start, stop, step)
1147            if len(indexList) != len(value):
1148                raise ValueError(
1149                    "attempt to assign sequence of size %d to slice of size %d"
1150                    % (len(value), len(indexList)))
1151
1152            for i, v in enumerate(indexList):
1153                self.model.set_value(self.iter, v, value[i])
1154        elif isinstance(key, tuple):
1155            if len(key) != len(value):
1156                raise ValueError(
1157                    "attempt to assign sequence of size %d to sequence of size %d"
1158                    % (len(value), len(key)))
1159            for k, v in zip(key, value):
1160                self[k] = v
1161        else:
1162            raise TypeError("indices must be an integer, slice or tuple, not %s"
1163                            % type(key).__name__)
1164
1165    def _convert_negative_index(self, index):
1166        new_index = self.model.get_n_columns() + index
1167        if new_index < 0:
1168            raise IndexError("column index is out of bounds: %d" % index)
1169        return new_index
1170
1171    def iterchildren(self):
1172        child_iter = self.model.iter_children(self.iter)
1173        return TreeModelRowIter(self.model, child_iter)
1174
1175
1176__all__.append('TreeModelRow')
1177
1178
1179class TreeModelRowIter(object):
1180
1181    def __init__(self, model, aiter):
1182        self.model = model
1183        self.iter = aiter
1184
1185    def __next__(self):
1186        if not self.iter:
1187            raise StopIteration
1188        row = TreeModelRow(self.model, self.iter)
1189        self.iter = self.model.iter_next(self.iter)
1190        return row
1191
1192    # alias for Python 2.x object protocol
1193    next = __next__
1194
1195    def __iter__(self):
1196        return self
1197
1198
1199__all__.append('TreeModelRowIter')
1200
1201
1202class TreePath(Gtk.TreePath):
1203
1204    def __new__(cls, path=0):
1205        if isinstance(path, int):
1206            path = str(path)
1207        elif not isinstance(path, str):
1208            path = ":".join(str(val) for val in path)
1209
1210        if len(path) == 0:
1211            raise TypeError("could not parse subscript '%s' as a tree path" % path)
1212        try:
1213            return TreePath.new_from_string(path)
1214        except TypeError:
1215            raise TypeError("could not parse subscript '%s' as a tree path" % path)
1216
1217    def __init__(self, *args, **kwargs):
1218        super(TreePath, self).__init__()
1219
1220    def __str__(self):
1221        return self.to_string() or ""
1222
1223    def __lt__(self, other):
1224        return other is not None and self.compare(other) < 0
1225
1226    def __le__(self, other):
1227        return other is not None and self.compare(other) <= 0
1228
1229    def __eq__(self, other):
1230        return other is not None and self.compare(other) == 0
1231
1232    def __ne__(self, other):
1233        return other is None or self.compare(other) != 0
1234
1235    def __gt__(self, other):
1236        return other is None or self.compare(other) > 0
1237
1238    def __ge__(self, other):
1239        return other is None or self.compare(other) >= 0
1240
1241    def __iter__(self):
1242        return iter(self.get_indices())
1243
1244    def __len__(self):
1245        return self.get_depth()
1246
1247    def __getitem__(self, index):
1248        return self.get_indices()[index]
1249
1250
1251TreePath = override(TreePath)
1252__all__.append('TreePath')
1253
1254
1255class TreeStore(Gtk.TreeStore, TreeModel, TreeSortable):
1256    def __init__(self, *column_types):
1257        Gtk.TreeStore.__init__(self)
1258        self.set_column_types(column_types)
1259
1260    def _do_insert(self, parent, position, row):
1261        if row is not None:
1262            row, columns = self._convert_row(row)
1263            treeiter = self.insert_with_values(parent, position, columns, row)
1264        else:
1265            treeiter = Gtk.TreeStore.insert(self, parent, position)
1266
1267        return treeiter
1268
1269    def append(self, parent, row=None):
1270        return self._do_insert(parent, -1, row)
1271
1272    def prepend(self, parent, row=None):
1273        return self._do_insert(parent, 0, row)
1274
1275    def insert(self, parent, position, row=None):
1276        return self._do_insert(parent, position, row)
1277
1278    def insert_before(self, parent, sibling, row=None):
1279        if row is not None:
1280            if sibling is None:
1281                position = -1
1282            else:
1283                if parent is None:
1284                    parent = self.iter_parent(sibling)
1285                position = self.get_path(sibling).get_indices()[-1]
1286            return self._do_insert(parent, position, row)
1287
1288        return Gtk.TreeStore.insert_before(self, parent, sibling)
1289
1290    def insert_after(self, parent, sibling, row=None):
1291        if row is not None:
1292            if sibling is None:
1293                position = 0
1294            else:
1295                if parent is None:
1296                    parent = self.iter_parent(sibling)
1297                position = self.get_path(sibling).get_indices()[-1] + 1
1298            return self._do_insert(parent, position, row)
1299
1300        return Gtk.TreeStore.insert_after(self, parent, sibling)
1301
1302    def set_value(self, treeiter, column, value):
1303        value = self._convert_value(column, value)
1304        Gtk.TreeStore.set_value(self, treeiter, column, value)
1305
1306    def set(self, treeiter, *args):
1307        def _set_lists(cols, vals):
1308            if len(cols) != len(vals):
1309                raise TypeError('The number of columns do not match the number of values')
1310
1311            columns = []
1312            values = []
1313            for col_num, value in zip(cols, vals):
1314                if not isinstance(col_num, int):
1315                    raise TypeError('TypeError: Expected integer argument for column.')
1316
1317                columns.append(col_num)
1318                values.append(self._convert_value(col_num, value))
1319
1320            Gtk.TreeStore.set(self, treeiter, columns, values)
1321
1322        if args:
1323            if isinstance(args[0], int):
1324                _set_lists(args[::2], args[1::2])
1325            elif isinstance(args[0], (tuple, list)):
1326                if len(args) != 2:
1327                    raise TypeError('Too many arguments')
1328                _set_lists(args[0], args[1])
1329            elif isinstance(args[0], dict):
1330                _set_lists(args[0].keys(), args[0].values())
1331            else:
1332                raise TypeError('Argument list must be in the form of (column, value, ...), ((columns,...), (values, ...)) or {column: value}.  No -1 termination is needed.')
1333
1334
1335TreeStore = override(TreeStore)
1336__all__.append('TreeStore')
1337
1338
1339class TreeView(Gtk.TreeView, Container):
1340    __init__ = deprecated_init(Gtk.TreeView.__init__,
1341                               arg_names=('model',),
1342                               category=PyGTKDeprecationWarning)
1343
1344    get_path_at_pos = strip_boolean_result(Gtk.TreeView.get_path_at_pos)
1345    get_visible_range = strip_boolean_result(Gtk.TreeView.get_visible_range)
1346    get_dest_row_at_pos = strip_boolean_result(Gtk.TreeView.get_dest_row_at_pos)
1347
1348    def enable_model_drag_source(self, start_button_mask, targets, actions):
1349        target_entries = _construct_target_list(targets)
1350        super(TreeView, self).enable_model_drag_source(start_button_mask,
1351                                                       target_entries,
1352                                                       actions)
1353
1354    def enable_model_drag_dest(self, targets, actions):
1355        target_entries = _construct_target_list(targets)
1356        super(TreeView, self).enable_model_drag_dest(target_entries,
1357                                                     actions)
1358
1359    def scroll_to_cell(self, path, column=None, use_align=False, row_align=0.0, col_align=0.0):
1360        if not isinstance(path, Gtk.TreePath):
1361            path = TreePath(path)
1362        super(TreeView, self).scroll_to_cell(path, column, use_align, row_align, col_align)
1363
1364    def set_cursor(self, path, column=None, start_editing=False):
1365        if not isinstance(path, Gtk.TreePath):
1366            path = TreePath(path)
1367        super(TreeView, self).set_cursor(path, column, start_editing)
1368
1369    def get_cell_area(self, path, column=None):
1370        if not isinstance(path, Gtk.TreePath):
1371            path = TreePath(path)
1372        return super(TreeView, self).get_cell_area(path, column)
1373
1374    def insert_column_with_attributes(self, position, title, cell, **kwargs):
1375        column = TreeViewColumn()
1376        column.set_title(title)
1377        column.pack_start(cell, False)
1378        self.insert_column(column, position)
1379        column.set_attributes(cell, **kwargs)
1380
1381
1382TreeView = override(TreeView)
1383__all__.append('TreeView')
1384
1385
1386class TreeViewColumn(Gtk.TreeViewColumn):
1387    def __init__(self, title='',
1388                 cell_renderer=None,
1389                 **attributes):
1390        Gtk.TreeViewColumn.__init__(self, title=title)
1391        if cell_renderer:
1392            self.pack_start(cell_renderer, True)
1393
1394        for (name, value) in attributes.items():
1395            self.add_attribute(cell_renderer, name, value)
1396
1397    cell_get_position = strip_boolean_result(Gtk.TreeViewColumn.cell_get_position)
1398
1399    def set_cell_data_func(self, cell_renderer, func, func_data=None):
1400        super(TreeViewColumn, self).set_cell_data_func(cell_renderer, func, func_data)
1401
1402    def set_attributes(self, cell_renderer, **attributes):
1403        Gtk.CellLayout.clear_attributes(self, cell_renderer)
1404
1405        for (name, value) in attributes.items():
1406            Gtk.CellLayout.add_attribute(self, cell_renderer, name, value)
1407
1408
1409TreeViewColumn = override(TreeViewColumn)
1410__all__.append('TreeViewColumn')
1411
1412
1413class TreeSelection(Gtk.TreeSelection):
1414
1415    def select_path(self, path):
1416        if not isinstance(path, Gtk.TreePath):
1417            path = TreePath(path)
1418        super(TreeSelection, self).select_path(path)
1419
1420    def get_selected(self):
1421        success, model, aiter = super(TreeSelection, self).get_selected()
1422        if success:
1423            return (model, aiter)
1424        else:
1425            return (model, None)
1426
1427    # for compatibility with PyGtk
1428
1429    def get_selected_rows(self):
1430        rows, model = super(TreeSelection, self).get_selected_rows()
1431        return (model, rows)
1432
1433
1434TreeSelection = override(TreeSelection)
1435__all__.append('TreeSelection')
1436
1437
1438class Button(Gtk.Button, Container):
1439    _init = deprecated_init(Gtk.Button.__init__,
1440                            arg_names=('label', 'stock', 'use_stock', 'use_underline'),
1441                            ignore=('stock',),
1442                            category=PyGTKDeprecationWarning,
1443                            stacklevel=3)
1444
1445    def __init__(self, *args, **kwargs):
1446        # Doubly deprecated initializer, the stock keyword is non-standard.
1447        # Simply give a warning that stock items are deprecated even though
1448        # we want to deprecate the non-standard keyword as well here from
1449        # the overrides.
1450        if 'stock' in kwargs and kwargs['stock']:
1451            warnings.warn('Stock items are deprecated. '
1452                          'Please use: Gtk.Button.new_with_mnemonic(label)',
1453                          PyGTKDeprecationWarning, stacklevel=2)
1454            new_kwargs = kwargs.copy()
1455            new_kwargs['label'] = new_kwargs['stock']
1456            new_kwargs['use_stock'] = True
1457            new_kwargs['use_underline'] = True
1458            del new_kwargs['stock']
1459            Gtk.Button.__init__(self, **new_kwargs)
1460        else:
1461            self._init(*args, **kwargs)
1462
1463    if hasattr(Gtk.Widget, "set_focus_on_click"):
1464        def set_focus_on_click(self, *args, **kwargs):
1465            # Gtk.Widget.set_focus_on_click should be used instead but it's
1466            # no obvious how because of the shadowed method, so override here
1467            return Gtk.Widget.set_focus_on_click(self, *args, **kwargs)
1468
1469    if hasattr(Gtk.Widget, "get_focus_on_click"):
1470        def get_focus_on_click(self, *args, **kwargs):
1471            # Gtk.Widget.get_focus_on_click should be used instead but it's
1472            # no obvious how because of the shadowed method, so override here
1473            return Gtk.Widget.get_focus_on_click(self, *args, **kwargs)
1474
1475
1476Button = override(Button)
1477__all__.append('Button')
1478
1479
1480class LinkButton(Gtk.LinkButton):
1481    __init__ = deprecated_init(Gtk.LinkButton.__init__,
1482                               arg_names=('uri', 'label'),
1483                               category=PyGTKDeprecationWarning)
1484
1485
1486LinkButton = override(LinkButton)
1487__all__.append('LinkButton')
1488
1489
1490class Label(Gtk.Label):
1491    __init__ = deprecated_init(Gtk.Label.__init__,
1492                               arg_names=('label',),
1493                               category=PyGTKDeprecationWarning)
1494
1495
1496Label = override(Label)
1497__all__.append('Label')
1498
1499
1500class Adjustment(Gtk.Adjustment):
1501    _init = deprecated_init(Gtk.Adjustment.__init__,
1502                            arg_names=('value', 'lower', 'upper',
1503                                       'step_increment', 'page_increment', 'page_size'),
1504                            deprecated_aliases={'page_increment': 'page_incr',
1505                                                'step_increment': 'step_incr'},
1506                            category=PyGTKDeprecationWarning,
1507                            stacklevel=3)
1508
1509    def __init__(self, *args, **kwargs):
1510        self._init(*args, **kwargs)
1511
1512        # The value property is set between lower and (upper - page_size).
1513        # Just in case lower, upper or page_size was still 0 when value
1514        # was set, we set it again here.
1515        if 'value' in kwargs:
1516            self.set_value(kwargs['value'])
1517        elif len(args) >= 1:
1518            self.set_value(args[0])
1519
1520
1521Adjustment = override(Adjustment)
1522__all__.append('Adjustment')
1523
1524
1525if Gtk._version in ("2.0", "3.0"):
1526    class Table(Gtk.Table, Container):
1527        __init__ = deprecated_init(Gtk.Table.__init__,
1528                                   arg_names=('n_rows', 'n_columns', 'homogeneous'),
1529                                   deprecated_aliases={'n_rows': 'rows', 'n_columns': 'columns'},
1530                                   category=PyGTKDeprecationWarning)
1531
1532        def attach(self, child, left_attach, right_attach, top_attach, bottom_attach, xoptions=Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, xpadding=0, ypadding=0):
1533            Gtk.Table.attach(self, child, left_attach, right_attach, top_attach, bottom_attach, xoptions, yoptions, xpadding, ypadding)
1534
1535    Table = override(Table)
1536    __all__.append('Table')
1537
1538
1539class ScrolledWindow(Gtk.ScrolledWindow):
1540    __init__ = deprecated_init(Gtk.ScrolledWindow.__init__,
1541                               arg_names=('hadjustment', 'vadjustment'),
1542                               category=PyGTKDeprecationWarning)
1543
1544
1545ScrolledWindow = override(ScrolledWindow)
1546__all__.append('ScrolledWindow')
1547
1548
1549if Gtk._version in ("2.0", "3.0"):
1550    class HScrollbar(Gtk.HScrollbar):
1551        __init__ = deprecated_init(Gtk.HScrollbar.__init__,
1552                                   arg_names=('adjustment',),
1553                                   category=PyGTKDeprecationWarning)
1554
1555    HScrollbar = override(HScrollbar)
1556    __all__.append('HScrollbar')
1557
1558    class VScrollbar(Gtk.VScrollbar):
1559        __init__ = deprecated_init(Gtk.VScrollbar.__init__,
1560                                   arg_names=('adjustment',),
1561                                   category=PyGTKDeprecationWarning)
1562
1563    VScrollbar = override(VScrollbar)
1564    __all__.append('VScrollbar')
1565
1566
1567class Paned(Gtk.Paned):
1568    def pack1(self, child, resize=False, shrink=True):
1569        super(Paned, self).pack1(child, resize, shrink)
1570
1571    def pack2(self, child, resize=True, shrink=True):
1572        super(Paned, self).pack2(child, resize, shrink)
1573
1574
1575Paned = override(Paned)
1576__all__.append('Paned')
1577
1578
1579if Gtk._version in ("2.0", "3.0"):
1580    class Arrow(Gtk.Arrow):
1581        __init__ = deprecated_init(Gtk.Arrow.__init__,
1582                                   arg_names=('arrow_type', 'shadow_type'),
1583                                   category=PyGTKDeprecationWarning)
1584
1585    Arrow = override(Arrow)
1586    __all__.append('Arrow')
1587
1588    class IconSet(Gtk.IconSet):
1589        def __new__(cls, pixbuf=None):
1590            if pixbuf is not None:
1591                warnings.warn('Gtk.IconSet(pixbuf) has been deprecated. Please use: '
1592                              'Gtk.IconSet.new_from_pixbuf(pixbuf)',
1593                              PyGTKDeprecationWarning, stacklevel=2)
1594                iconset = Gtk.IconSet.new_from_pixbuf(pixbuf)
1595            else:
1596                iconset = Gtk.IconSet.__new__(cls)
1597            return iconset
1598
1599        def __init__(self, *args, **kwargs):
1600            return super(IconSet, self).__init__()
1601
1602    IconSet = override(IconSet)
1603    __all__.append('IconSet')
1604
1605
1606class Viewport(Gtk.Viewport):
1607    __init__ = deprecated_init(Gtk.Viewport.__init__,
1608                               arg_names=('hadjustment', 'vadjustment'),
1609                               category=PyGTKDeprecationWarning)
1610
1611
1612Viewport = override(Viewport)
1613__all__.append('Viewport')
1614
1615
1616class TreeModelFilter(Gtk.TreeModelFilter):
1617    def set_visible_func(self, func, data=None):
1618        super(TreeModelFilter, self).set_visible_func(func, data)
1619
1620    def set_value(self, iter, column, value):
1621        # Delegate to child model
1622        iter = self.convert_iter_to_child_iter(iter)
1623        self.get_model().set_value(iter, column, value)
1624
1625
1626TreeModelFilter = override(TreeModelFilter)
1627__all__.append('TreeModelFilter')
1628
1629if Gtk._version == '3.0':
1630    class Menu(Gtk.Menu):
1631        def popup(self, parent_menu_shell, parent_menu_item, func, data, button, activate_time):
1632            self.popup_for_device(None, parent_menu_shell, parent_menu_item, func, data, button, activate_time)
1633    Menu = override(Menu)
1634    __all__.append('Menu')
1635
1636if Gtk._version in ("2.0", "3.0"):
1637    _Gtk_main_quit = Gtk.main_quit
1638
1639    @override(Gtk.main_quit)
1640    def main_quit(*args):
1641        _Gtk_main_quit()
1642
1643    _Gtk_main = Gtk.main
1644
1645    @override(Gtk.main)
1646    def main(*args, **kwargs):
1647        with register_sigint_fallback(Gtk.main_quit):
1648            with wakeup_on_signal():
1649                return _Gtk_main(*args, **kwargs)
1650
1651
1652if Gtk._version in ("2.0", "3.0"):
1653    stock_lookup = strip_boolean_result(Gtk.stock_lookup)
1654    __all__.append('stock_lookup')
1655
1656if Gtk._version == "4.0":
1657    initialized = Gtk.init_check()
1658else:
1659    initialized, argv = Gtk.init_check(sys.argv)
1660    sys.argv = list(argv)
1661