7945cc8fc8695b7dd36c2a6fabdd685bac163964
[platform/upstream/pygobject2.git] / gi / overrides / Gtk.py
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
22 import sys
23 from gi import _gobject
24 from gi.repository import GObject
25 from ..overrides import override
26 from ..importer import modules
27
28 if sys.version_info >= (3, 0):
29     _basestring = str
30     _callable = lambda c: hasattr(c, '__call__')
31 else:
32     _basestring = basestring
33     _callable = callable
34
35 Gtk = modules['Gtk']._introspection_module
36 __all__ = []
37
38 if Gtk._version == '2.0':
39     import warnings
40     warn_msg = "You have imported the Gtk 2.0 module.  Because Gtk 2.0 \
41 was not designed for use with introspection some of the \
42 interfaces and API will fail.  As such this is not supported \
43 by the pygobject development team and we encourage you to \
44 port your app to Gtk 3 or greater. PyGTK is the recomended \
45 python module to use with Gtk 2.0"
46
47     warnings.warn(warn_msg, RuntimeWarning)
48
49
50 class Widget(Gtk.Widget):
51
52     def translate_coordinates(self, dest_widget, src_x, src_y):
53         success, dest_x, dest_y = super(Widget, self).translate_coordinates(
54             dest_widget, src_x, src_y)
55         if success:
56             return (dest_x, dest_y,)
57
58     def render_icon(self, stock_id, size, detail=None):
59         return super(Widget, self).render_icon(stock_id, size, detail)
60
61 Widget = override(Widget)
62 __all__.append('Widget')
63
64 class Container(Gtk.Container, Widget):
65
66     def __len__(self):
67         return len(self.get_children())
68
69     def __contains__(self, child):
70         return child in self.get_children()
71
72     def __iter__(self):
73         return iter(self.get_children())
74
75     def __bool__(self):
76         return True
77
78     # alias for Python 2.x object protocol
79     __nonzero__ = __bool__
80
81     def get_focus_chain(self):
82         success, widgets = super(Container, self).get_focus_chain()
83         if success:
84             return widgets
85
86 Container = override(Container)
87 __all__.append('Container')
88
89 class Editable(Gtk.Editable):
90
91     def insert_text(self, text, position):
92         pos = super(Editable, self).insert_text(text, -1, position)
93
94         return pos
95
96     def get_selection_bounds(self):
97         success, start_pos, end_pos = super(Editable, self).get_selection_bounds()
98         if success:
99             return (start_pos, end_pos,)
100         else:
101             return tuple()
102
103 Editable = override(Editable)
104 __all__.append("Editable")
105
106 class Action(Gtk.Action):
107     def __init__(self, name, label, tooltip, stock_id, **kwds):
108         Gtk.Action.__init__(self, name=name, label=label, tooltip=tooltip, stock_id=stock_id, **kwds)
109
110 Action = override(Action)
111 __all__.append("Action")
112
113 class RadioAction(Gtk.RadioAction):
114     def __init__(self, name, label, tooltip, stock_id, value, **kwds):
115         Gtk.RadioAction.__init__(self, name=name, label=label, tooltip=tooltip, stock_id=stock_id, value=value, **kwds)
116
117 RadioAction = override(RadioAction)
118 __all__.append("RadioAction")
119
120 class ActionGroup(Gtk.ActionGroup):
121     def __init__(self, name, **kwds):
122         super(ActionGroup, self).__init__(name = name, **kwds)
123
124     def add_actions(self, entries, user_data=None):
125         """
126         The add_actions() method is a convenience method that creates a number
127         of gtk.Action  objects based on the information in the list of action
128         entry tuples contained in entries and adds them to the action group.
129         The entry tuples can vary in size from one to six items with the
130         following information:
131
132             * The name of the action. Must be specified.
133             * The stock id for the action. Optional with a default value of None
134               if a label is specified.
135             * The label for the action. This field should typically be marked
136               for translation, see the set_translation_domain() method. Optional
137               with a default value of None if a stock id is specified.
138             * The accelerator for the action, in the format understood by the
139               gtk.accelerator_parse() function. Optional with a default value of
140               None.
141             * The tooltip for the action. This field should typically be marked
142               for translation, see the set_translation_domain() method. Optional
143               with a default value of None.
144             * The callback function invoked when the action is activated.
145               Optional with a default value of None.
146
147         The "activate" signals of the actions are connected to the callbacks and
148         their accel paths are set to <Actions>/group-name/action-name.
149         """
150         try:
151             iter(entries)
152         except (TypeError):
153             raise TypeError('entries must be iterable')
154
155         def _process_action(name, stock_id=None, label=None, accelerator=None, tooltip=None, callback=None):
156             action = Action(name, label, tooltip, stock_id)
157             if callback is not None:
158                 if user_data is None:
159                     action.connect('activate', callback)
160                 else:
161                     action.connect('activate', callback, user_data)
162
163             self.add_action_with_accel(action, accelerator)
164
165         for e in entries:
166             # using inner function above since entries can leave out optional arguments
167             _process_action(*e)
168
169     def add_toggle_actions(self, entries, user_data=None):
170         """
171         The add_toggle_actions() method is a convenience method that creates a
172         number of gtk.ToggleAction objects based on the information in the list
173         of action entry tuples contained in entries and adds them to the action
174         group. The toggle action entry tuples can vary in size from one to seven
175         items with the following information:
176
177             * The name of the action. Must be specified.
178             * The stock id for the action. Optional with a default value of None
179               if a label is specified.
180             * The label for the action. This field should typically be marked
181               for translation, see the set_translation_domain() method. Optional
182               with a default value of None if a stock id is specified.
183             * The accelerator for the action, in the format understood by the
184               gtk.accelerator_parse() function. Optional with a default value of
185               None.
186             * The tooltip for the action. This field should typically be marked
187               for translation, see the set_translation_domain() method. Optional
188               with a default value of None.
189             * The callback function invoked when the action is activated.
190               Optional with a default value of None.
191             * A flag indicating whether the toggle action is active. Optional
192               with a default value of False.
193
194         The "activate" signals of the actions are connected to the callbacks and
195         their accel paths are set to <Actions>/group-name/action-name.
196         """
197
198         try:
199             iter(entries)
200         except (TypeError):
201             raise TypeError('entries must be iterable')
202
203         def _process_action(name, stock_id=None, label=None, accelerator=None, tooltip=None, callback=None, is_active=False):
204             action = Gtk.ToggleAction(name, label, tooltip, stock_id)
205             action.set_active(is_active)
206             if callback is not None:
207                 if user_data is None:
208                     action.connect('activate', callback)
209                 else:
210                     action.connect('activate', callback, user_data)
211
212             self.add_action_with_accel(action, accelerator)
213
214         for e in entries:
215             # using inner function above since entries can leave out optional arguments
216             _process_action(*e)
217
218
219     def add_radio_actions(self, entries, value=None, on_change=None, user_data=None):
220         """
221         The add_radio_actions() method is a convenience method that creates a
222         number of gtk.RadioAction objects based on the information in the list
223         of action entry tuples contained in entries and adds them to the action
224         group. The entry tuples can vary in size from one to six items with the
225         following information:
226
227             * The name of the action. Must be specified.
228             * The stock id for the action. Optional with a default value of None
229               if a label is specified.
230             * The label for the action. This field should typically be marked
231               for translation, see the set_translation_domain() method. Optional
232               with a default value of None if a stock id is specified.
233             * The accelerator for the action, in the format understood by the
234               gtk.accelerator_parse() function. Optional with a default value of
235               None.
236             * The tooltip for the action. This field should typically be marked
237               for translation, see the set_translation_domain() method. Optional
238               with a default value of None.
239             * The value to set on the radio action. Optional with a default
240               value of 0. Should be specified in applications.
241
242         The value parameter specifies the radio action that should be set
243         active. The "changed" signal of the first radio action is connected to
244         the on_change callback (if specified and not None) and the accel paths
245         of the actions are set to <Actions>/group-name/action-name.
246         """
247         try:
248             iter(entries)
249         except (TypeError):
250             raise TypeError('entries must be iterable')
251
252         first_action = None
253
254         def _process_action(group_source, name, stock_id=None, label=None, accelerator=None, tooltip=None, entry_value=0):
255             action = RadioAction(name, label, tooltip, stock_id, entry_value)
256
257             # FIXME: join_group is a patch to Gtk+ 3.0
258             #        otherwise we can't effectively add radio actions to a
259             #        group.  Should we depend on 3.0 and error out here
260             #        or should we offer the functionality via a compat
261             #        C module?
262             if hasattr(action, 'join_group'):
263                 action.join_group(group_source)
264
265             if value == entry_value:
266                 action.set_active(True)
267
268             self.add_action_with_accel(action, accelerator)
269             return action
270
271         for e in entries:
272             # using inner function above since entries can leave out optional arguments
273             action = _process_action(first_action, *e)
274             if first_action is None:
275                 first_action = action
276
277         if first_action is not None and on_change is not None:
278             if user_data is None:
279                 action.connect('changed', on_change)
280             else:
281                 action.connect('changed', on_change, user_data)
282
283 ActionGroup = override(ActionGroup)
284 __all__.append('ActionGroup')
285
286 class UIManager(Gtk.UIManager):
287     def add_ui_from_string(self, buffer):
288         if not isinstance(buffer, _basestring):
289             raise TypeError('buffer must be a string')
290
291         length = len(buffer)
292
293         return Gtk.UIManager.add_ui_from_string(self, buffer, length)
294
295     def insert_action_group(self, buffer, length=-1):
296         return Gtk.UIManager.insert_action_group(self, buffer, length)
297
298 UIManager = override(UIManager)
299 __all__.append('UIManager')
300
301 class ComboBox(Gtk.ComboBox, Container):
302
303     def get_active_iter(self):
304         success, aiter = super(ComboBox, self).get_active_iter()
305         if success:
306             return aiter
307
308 ComboBox = override(ComboBox)
309 __all__.append('ComboBox')
310
311
312 class Box(Gtk.Box):
313     def __init__(self, homogeneous=False, spacing=0, **kwds):
314         super(Box, self).__init__(**kwds)
315         self.set_homogeneous(homogeneous)
316         self.set_spacing(spacing)
317
318 Box = override(Box)
319 __all__.append('Box')
320
321 class SizeGroup(Gtk.SizeGroup):
322     def __init__(self, mode=Gtk.SizeGroupMode.VERTICAL):
323         super(SizeGroup, self).__init__(mode=mode)
324
325 SizeGroup = override(SizeGroup)
326 __all__.append('SizeGroup')
327
328
329 class MenuItem(Gtk.MenuItem):
330     def __init__(self, label=None):
331         super(MenuItem, self).__init__(label=label)
332
333 MenuItem = override(MenuItem)
334 __all__.append('MenuItem')
335
336
337 class Builder(Gtk.Builder):
338
339     def connect_signals(self, obj_or_map):
340         def _full_callback(builder, gobj, signal_name, handler_name, connect_obj, flags, obj_or_map):
341             handler = None
342             if isinstance(obj_or_map, dict):
343                 handler = obj_or_map.get(handler_name, None)
344             else:
345                 handler = getattr(obj_or_map, handler_name, None)
346
347             if handler is None:
348                 raise AttributeError('Handler %s not found' % handler_name)
349
350             if not _callable(handler):
351                 raise TypeError('Handler %s is not a method or function' % handler_name)
352
353             after = flags or GObject.ConnectFlags.AFTER
354             if connect_obj is not None:
355                 if after:
356                     gobj.connect_object_after(signal_name, handler, connect_obj)
357                 else:
358                     gobj.connect_object(signal_name, handler, connect_obj)
359             else:
360                 if after:
361                     gobj.connect_after(signal_name, handler)
362                 else:
363                     gobj.connect(signal_name, handler)
364
365         self.connect_signals_full(_full_callback, obj_or_map)
366
367     def add_from_string(self, buffer):
368         if not isinstance(buffer, _basestring):
369             raise TypeError('buffer must be a string')
370
371         length = len(buffer)
372
373         return Gtk.Builder.add_from_string(self, buffer, length)
374
375     def add_objects_from_string(self, buffer, object_ids):
376         if not isinstance(buffer, _basestring):
377             raise TypeError('buffer must be a string')
378
379         length = len(buffer)
380
381         return Gtk.Builder.add_objects_from_string(self, buffer, length, object_ids)
382
383 Builder = override(Builder)
384 __all__.append('Builder')
385
386
387 class Dialog(Gtk.Dialog, Container):
388
389     def __init__(self,
390                  title=None,
391                  parent=None,
392                  flags=0,
393                  buttons=None,
394                  _buttons_property=None,
395                  **kwds):
396
397         # buttons is overloaded by PyGtk so we have to do the same here
398         # this breaks some subclasses of Dialog so add a _buttons_property
399         # keyword to work around this
400         if _buttons_property is not None:
401             kwds['buttons'] = _buttons_property
402
403         Gtk.Dialog.__init__(self, **kwds)
404         if title:
405             self.set_title(title)
406         if parent:
407             self.set_transient_for(parent)
408         if flags & Gtk.DialogFlags.MODAL:
409             self.set_modal(True)
410         if flags & Gtk.DialogFlags.DESTROY_WITH_PARENT:
411             self.set_destroy_with_parent(True)
412
413         # NO_SEPARATOR has been removed from Gtk 3
414         try:
415             if flags & Gtk.DialogFlags.NO_SEPARATOR:
416                 self.set_has_separator(False)
417         except AttributeError:
418             import warnings
419             warnings.warn("Gtk.DialogFlags.NO_SEPARATOR has been depricated since Gtk+-3.0", DeprecationWarning)
420
421         if buttons is not None:
422             self.add_buttons(*buttons)
423
424     action_area = property(lambda dialog: dialog.get_action_area())
425     vbox = property(lambda dialog: dialog.get_content_area())
426
427     def add_buttons(self, *args):
428         """
429         The add_buttons() method adds several buttons to the Gtk.Dialog using
430         the button data passed as arguments to the method. This method is the
431         same as calling the Gtk.Dialog.add_button() repeatedly. The button data
432         pairs - button text (or stock ID) and a response ID integer are passed
433         individually. For example:
434
435         >>> dialog.add_buttons(Gtk.STOCK_OPEN, 42, "Close", Gtk.ResponseType.CLOSE)
436
437         will add "Open" and "Close" buttons to dialog.
438         """
439         def _button(b):
440             while b:
441                 t, r = b[0:2]
442                 b = b[2:]
443                 yield t, r
444
445         try:
446             for text, response in _button(args):
447                 self.add_button(text, response)
448         except (IndexError):
449             raise TypeError('Must pass an even number of arguments')
450
451 Dialog = override(Dialog)
452 __all__.append('Dialog')
453
454 class MessageDialog(Gtk.MessageDialog, Dialog):
455     def __init__(self,
456                  parent=None,
457                  flags=0,
458                  message_type=Gtk.MessageType.INFO,
459                  buttons=Gtk.ButtonsType.NONE,
460                  message_format=None,
461                  **kwds):
462
463         if message_format != None:
464             kwds['text'] = message_format
465
466         # type keyword is used for backwards compat with PyGTK
467         if 'type' in kwds:
468             import warnings
469             warnings.warn("The use of the keyword type as a parameter of the Gtk.MessageDialog constructor has been depricated. Please use message_type instead.", DeprecationWarning)
470             message_type = kwds.pop('type')
471
472         Gtk.MessageDialog.__init__(self,
473                                    _buttons_property=buttons,
474                                    message_type=message_type,
475                                    **kwds)
476         Dialog.__init__(self, parent=parent, flags=flags)
477
478     def format_secondary_text(self, message_format):
479         self.set_property('secondary-use-markup', False)
480         self.set_property('secondary-text', message_format)
481
482     def format_secondary_markup(self, message_format):
483         self.set_property('secondary-use-markup', True)
484         self.set_property('secondary-text', message_format)
485
486 MessageDialog = override(MessageDialog)
487 __all__.append('MessageDialog')
488
489 class AboutDialog(Gtk.AboutDialog, Dialog):
490     def __init__(self, **kwds):
491         Gtk.AboutDialog.__init__(self, **kwds)
492         Dialog.__init__(self)
493
494 AboutDialog = override(AboutDialog)
495 __all__.append('AboutDialog')
496
497 class ColorSelectionDialog(Gtk.ColorSelectionDialog, Dialog):
498     def __init__(self, title=None, **kwds):
499         Gtk.ColorSelectionDialog.__init__(self, **kwds)
500         Dialog.__init__(self, title=title)
501
502 ColorSelectionDialog = override(ColorSelectionDialog)
503 __all__.append('ColorSelectionDialog')
504
505 class FileChooserDialog(Gtk.FileChooserDialog, Dialog):
506     def __init__(self,
507                  title=None,
508                  parent=None,
509                  action=Gtk.FileChooserAction.OPEN,
510                  buttons=None,
511                  **kwds):
512         Gtk.FileChooserDialog.__init__(self,
513                                        action=action,
514                                        **kwds)
515         Dialog.__init__(self,
516                         title=title,
517                         parent=parent,
518                         buttons=buttons)
519
520 FileChooserDialog = override(FileChooserDialog)
521 __all__.append('FileChooserDialog')
522
523 class FontSelectionDialog(Gtk.FontSelectionDialog, Dialog):
524     def __init__(self, title=None, **kwds):
525         Gtk.FontSelectionDialog.__init__(self, **kwds)
526         Dialog.__init__(self, title=title)
527
528 FontSelectionDialog = override(FontSelectionDialog)
529 __all__.append('FontSelectionDialog')
530
531 class RecentChooserDialog(Gtk.RecentChooserDialog, Dialog):
532     def __init__(self,
533                  title=None,
534                  parent=None,
535                  manager=None,
536                  buttons=None,
537                  **kwds):
538
539         Gtk.RecentChooserDialog.__init__(self, recent_manager=manager, **kwds)
540         Dialog.__init__(self,
541                         title=title,
542                         parent=parent,
543                         buttons=buttons)
544
545 RecentChooserDialog = override(RecentChooserDialog)
546 __all__.append('RecentChooserDialog')
547
548 class IconView(Gtk.IconView):
549
550     def get_item_at_pos(self, x, y):
551         success, path, cell = super(IconView, self).get_item_at_pos(x, y)
552         if success:
553             return (path, cell,)
554
555     def get_visible_range(self):
556         success, start_path, end_path = super(IconView, self).get_visible_range()
557         if success:
558             return (start_path, end_path,)
559
560     def get_dest_item_at_pos(self, drag_x, drag_y):
561         success, path, pos = super(IconView, self).get_dest_item_at_pos(drag_x, drag_y)
562         if success:
563             return path, pos
564
565 IconView = override(IconView)
566 __all__.append('IconView')
567
568 class IMContext(Gtk.IMContext):
569
570     def get_surrounding(self):
571         success, text, cursor_index = super(IMContext, self).get_surrounding()
572         if success:
573             return (text, cursor_index,)
574
575 IMContext = override(IMContext)
576 __all__.append('IMContext')
577
578 class RecentInfo(Gtk.RecentInfo):
579
580     def get_application_info(self, app_name):
581         success, app_exec, count, time = super(RecentInfo, self).get_application_info(app_name)
582         if success:
583             return (app_exec, count, time,)
584
585 RecentInfo = override(RecentInfo)
586 __all__.append('RecentInfo')
587
588 class TextBuffer(Gtk.TextBuffer):
589     def _get_or_create_tag_table(self):
590         table = self.get_tag_table()
591         if table is None:
592             table = Gtk.TextTagTable()
593             self.set_tag_table(table)
594
595         return table
596
597     def create_tag(self, tag_name=None, **properties):
598         """
599         @tag_name: name of the new tag, or None
600         @properties: keyword list of properties and their values
601
602         Creates a tag and adds it to the tag table of the TextBuffer.
603         Equivalent to creating a Gtk.TextTag and then adding the
604         tag to the buffer's tag table. The returned tag is owned by
605         the buffer's tag table.
606
607         If @tag_name is None, the tag is anonymous.
608
609         If @tag_name is not None, a tag called @tag_name must not already
610         exist in the tag table for this buffer.
611
612         Properties are passed as a keyword list of names and values (e.g.
613         foreground = 'DodgerBlue', weight = Pango.Weight.BOLD)
614
615         Return value: a new tag
616         """
617
618         tag = Gtk.TextTag(name=tag_name, **properties)
619         self._get_or_create_tag_table().add(tag)
620         return tag
621
622     def create_mark(self, mark_name, where, left_gravity=False):
623         return Gtk.TextBuffer.create_mark(self, mark_name, where, left_gravity)
624
625     def set_text(self, text, length=-1):
626         Gtk.TextBuffer.set_text(self, text, length)
627
628     def insert(self, iter, text, length=-1):
629         if not isinstance(text , _basestring):
630             raise TypeError('text must be a string, not %s' % type(text))
631
632         Gtk.TextBuffer.insert(self, iter, text, length)
633
634     def insert_with_tags(self, iter, text, *tags):
635         start_offset = iter.get_offset()
636         self.insert(iter, text)
637
638         if not tags:
639             return
640
641         start = self.get_iter_at_offset(start_offset)
642
643         for tag in tags:
644             self.apply_tag(tag, start, iter)
645
646     def insert_with_tags_by_name(self, iter, text, *tags):
647         if not tags:
648             return
649
650         tag_objs = []
651
652         for tag in tags:
653             tag_obj = self.get_tag_table().lookup(tag)
654             if not tag_obj:
655                 raise ValueError('unknown text tag: %s' % tag)
656             tag_objs.append(tag_obj)
657
658         self.insert_with_tags(iter, text, *tag_objs)
659
660     def insert_at_cursor(self, text, length=-1):
661         if not isinstance(text , _basestring):
662             raise TypeError('text must be a string, not %s' % type(text))
663
664         Gtk.TextBuffer.insert_at_cursor(self, text, length)
665
666     def get_selection_bounds(self):
667         success, start, end = super(TextBuffer, self).get_selection_bounds()
668         if success:
669             return (start, end)
670         else:
671             return ()
672
673 TextBuffer = override(TextBuffer)
674 __all__.append('TextBuffer')
675
676 class TextIter(Gtk.TextIter):
677
678     def forward_search(self, string, flags, limit):
679         success, match_start, match_end = super(TextIter, self).forward_search(string,
680             flags, limit)
681         if success:
682             return (match_start, match_end)
683         else:
684             return None
685
686     def backward_search(self, string, flags, limit):
687         success, match_start, match_end = super(TextIter, self).backward_search(string,
688             flags, limit)
689         if success:
690             return (match_start, match_end)
691         else:
692             return None
693
694     def begins_tag(self, tag=None):
695         return super(TextIter, self).begins_tag(tag)
696
697     def ends_tag(self, tag=None):
698         return super(TextIter, self).ends_tag(tag)
699
700     def toggles_tag(self, tag=None):
701         return super(TextIter, self).toggles_tag(tag)
702
703 TextIter = override(TextIter)
704 __all__.append('TextIter')
705
706 class TreeModel(Gtk.TreeModel):
707     def __len__(self):
708         return self.iter_n_children(None)
709
710     def __bool__(self):
711         return True
712
713     # alias for Python 2.x object protocol
714     __nonzero__ = __bool__
715
716     def __getitem__(self, key):
717         if isinstance(key, Gtk.TreeIter):
718             return TreeModelRow(self, key)
719         elif isinstance(key, int) and key < 0:
720             index = len(self) + key
721             if index < 0:
722                 raise IndexError("row index is out of bounds: %d" % key)
723             try:
724                 aiter = self.get_iter(index)
725             except ValueError:
726                 raise IndexError("could not find tree path '%s'" % key)
727             return TreeModelRow(self, aiter)
728         else:
729             try:
730                 aiter = self.get_iter(key)
731             except ValueError:
732                 raise IndexError("could not find tree path '%s'" % key)
733             return TreeModelRow(self, aiter)
734
735     def __setitem__(self, key, value):
736         row = self[key]
737         self.set_row(row.iter, value)
738
739     def __iter__(self):
740         return TreeModelRowIter(self, self.get_iter_first())
741
742     def get_iter(self, path):
743         if not isinstance(path, Gtk.TreePath):
744             path = TreePath(path)
745
746         success, aiter = super(TreeModel, self).get_iter(path)
747         if not success:
748             raise ValueError("invalid tree path '%s'" % path)
749         return aiter
750
751     def get_iter_first(self):
752         success, aiter = super(TreeModel, self).get_iter_first()
753         if success:
754             return aiter
755
756     def get_iter_from_string(self, path_string):
757         success, aiter = super(TreeModel, self).get_iter_from_string(path_string)
758         if not success:
759             raise ValueError("invalid tree path '%s'" % path_string)
760         return aiter
761
762     def iter_next(self, aiter):
763         next_iter = aiter.copy()
764         success = super(TreeModel, self).iter_next(next_iter)
765         if success:
766             return next_iter
767
768     def iter_children(self, aiter):
769         success, child_iter = super(TreeModel, self).iter_children(aiter)
770         if success:
771             return child_iter
772
773     def iter_nth_child(self, parent, n):
774         success, child_iter = super(TreeModel, self).iter_nth_child(parent, n)
775         if success:
776             return child_iter
777
778     def iter_parent(self, aiter):
779         success, parent_iter = super(TreeModel, self).iter_parent(aiter)
780         if success:
781             return parent_iter
782
783     def set_row(self, treeiter, row):
784         # TODO: Accept a dictionary for row
785         # model.append(None,{COLUMN_ICON: icon, COLUMN_NAME: name})
786         if isinstance(row, str):
787             raise TypeError('Expected a list or tuple, but got str')
788
789         n_columns = self.get_n_columns()
790         if len(row) != n_columns:
791             raise ValueError('row sequence has the incorrect number of elements')
792
793         for i in range(n_columns):
794             value = row[i]
795             if value is None:
796                continue  # None means skip this row
797
798             self.set_value(treeiter, i, value)
799
800     def _convert_value(self, treeiter, column, value):
801             if value is None:
802                 return
803
804             # we may need to convert to a basic type
805             type_ = self.get_column_type(column)
806             if type_ == GObject.TYPE_STRING:
807                 if isinstance(value, str):
808                     value = str(value)
809                 elif sys.version_info < (3, 0):
810                     if isinstance(value, unicode):
811                         value = value.encode('UTF-8')
812                     else:
813                         raise ValueError('Expected string or unicode for column %i but got %s%s' % (column, value, type(value)))
814                 else:
815                     raise ValueError('Expected a string for column %i but got %s' % (column, type(value)))
816             elif type_ == GObject.TYPE_FLOAT or type_ == GObject.TYPE_DOUBLE:
817                 if isinstance(value, float):
818                     value = float(value)
819                 else:
820                     raise ValueError('Expected a float for column %i but got %s' % (column, type(value)))
821             elif type_ == GObject.TYPE_LONG or type_ == GObject.TYPE_INT:
822                 if isinstance(value, int):
823                     value = int(value)
824                 elif sys.version_info < (3, 0):
825                     if isinstance(value, long):
826                         value = long(value)
827                     else:
828                         raise ValueError('Expected an long for column %i but got %s' % (column, type(value)))
829                 else:
830                     raise ValueError('Expected an integer for column %i but got %s' % (column, type(value)))
831             elif type_ == GObject.TYPE_BOOLEAN:
832                 cmp_classes = [int]
833                 if sys.version_info < (3, 0):
834                     cmp_classes.append(long)
835
836                 if isinstance(value, tuple(cmp_classes)):
837                     value = bool(value)
838                 else:
839                     raise ValueError('Expected a bool for column %i but got %s' % (column, type(value)))
840             else:
841                 # use GValues directly to marshal to the correct type
842                 # standard object checks should take care of validation
843                 # so we don't have to do it here
844                 value_container = GObject.Value()
845                 value_container.init(type_)
846                 if type_ == GObject.TYPE_CHAR:
847                     value_container.set_char(value)
848                     value = value_container
849                 elif type_ == GObject.TYPE_UCHAR:
850                     value_container.set_uchar(value)
851                     value = value_container
852                 elif type_ == GObject.TYPE_UNICHAR:
853                     cmp_classes = [str]
854                     if sys.version_info < (3, 0):
855                         cmp_classes.append(unicode)
856
857                     if isinstance(value, tuple(cmp_classes)):
858                         value = ord(value[0])
859
860                     value_container.set_uint(value)
861                     value = value_container
862                 elif type_ == GObject.TYPE_UINT:
863                     value_container.set_uint(value)
864                     value = value_container
865                 elif type_ == GObject.TYPE_ULONG:
866                     value_container.set_ulong(value)
867                     value = value_container
868                 elif type_ == GObject.TYPE_INT64:
869                     value_container.set_int64(value)
870                     value = value_container
871                 elif type_ == GObject.TYPE_UINT64:
872                     value_container.set_uint64(value)
873                     value = value_container
874                 elif type_ == GObject.TYPE_PYOBJECT:
875                     value_container.set_boxed(value)
876                     value = value_container
877
878             return value
879
880     def get(self, treeiter, *columns):
881         n_columns = self.get_n_columns()
882
883         values = []
884         for col in columns:
885             if not isinstance(col, int):
886                 raise TypeError("column numbers must be ints")
887
888             if col < 0 or col >= n_columns:
889                 raise ValueError("column number is out of range")
890
891             values.append(self.get_value(treeiter, col))
892
893         return tuple(values)
894
895     def filter_new(self, root=None):
896         return super(TreeModel, self).filter_new(root)
897
898 TreeModel = override(TreeModel)
899 __all__.append('TreeModel')
900
901 class TreeSortable(Gtk.TreeSortable, ):
902
903     def get_sort_column_id(self):
904         success, sort_column_id, order = super(TreeSortable, self).get_sort_column_id()
905         if success:
906             return (sort_column_id, order,)
907         else:
908             return (None, None,)
909
910     def set_sort_func(self, sort_column_id, sort_func, user_data=None):
911         super(TreeSortable, self).set_sort_func(sort_column_id, sort_func, user_data)
912
913     def set_default_sort_func(self, sort_func, user_data=None):
914         super(TreeSortable, self).set_default_sort_func(sort_func, user_data)
915
916 TreeSortable = override(TreeSortable)
917 __all__.append('TreeSortable')
918
919 class ListStore(Gtk.ListStore, TreeModel, TreeSortable):
920     def __init__(self, *column_types):
921         Gtk.ListStore.__init__(self)
922         self.set_column_types(column_types)
923
924     def append(self, row=None):
925         treeiter = Gtk.ListStore.append(self)
926
927         if row is not None:
928             self.set_row(treeiter, row)
929
930         return treeiter
931
932     def insert(self, position, row=None):
933         treeiter = Gtk.ListStore.insert(self, position)
934
935         if row is not None:
936             self.set_row(treeiter, row)
937
938         return treeiter
939
940     def insert_before(self, sibling, row=None):
941         treeiter = Gtk.ListStore.insert_before(self, sibling)
942
943         if row is not None:
944             self.set_row(treeiter, row)
945
946         return treeiter
947
948     def insert_after(self, sibling, row=None):
949         treeiter = Gtk.ListStore.insert_after(self, sibling)
950
951         if row is not None:
952             self.set_row(treeiter, row)
953
954         return treeiter
955
956     def prepend(self, row=None):
957         treeiter = Gtk.ListStore.prepend(self)
958
959         if row is not None:
960             self.set_row(treeiter, row)
961
962         return treeiter
963
964     def set_value(self, treeiter, column, value):
965         value = self._convert_value(treeiter, column, value)
966         Gtk.ListStore.set_value(self, treeiter, column, value)
967
968     def set(self, treeiter, *args):
969
970         def _set_lists(columns, values):
971             if len(columns) != len(values):
972                 raise TypeError('The number of columns do not match the number of values')
973             for col_num, val in zip(columns, values):
974                 if not isinstance(col_num, int):
975                     raise TypeError('TypeError: Expected integer argument for column.')
976                 self.set_value(treeiter, col_num, val)
977
978         if args:
979             if isinstance(args[0], int):
980                 columns = args[::2]
981                 values = args[1::2]
982                 _set_lists(columns, values)
983             elif isinstance(args[0], (tuple, list)):
984                 if len(args) != 2:
985                     raise TypeError('Too many arguments');
986                 _set_lists(args[0], args[1])
987             elif isinstance(args[0], dict):
988                 columns = args[0].keys()
989                 values = args[0].values()
990                 _set_lists(columns, values)
991             else:
992                 raise TypeError('Argument list must be in the form of (column, value, ...), ((columns,...), (values, ...)) or {column: value}.  No -1 termination is needed.')
993
994 ListStore = override(ListStore)
995 __all__.append('ListStore')
996
997 class TreeModelRow(object):
998
999     def __init__(self, model, iter_or_path):
1000         if not isinstance(model, Gtk.TreeModel):
1001             raise TypeError("expected Gtk.TreeModel, %s found" % type(model).__name__)
1002         self.model = model
1003         if isinstance(iter_or_path, Gtk.TreePath):
1004             self.iter = model.get_iter(iter_or_path)
1005         elif isinstance(iter_or_path, Gtk.TreeIter):
1006             self.iter = iter_or_path
1007         else:
1008             raise TypeError("expected Gtk.TreeIter or Gtk.TreePath, \
1009                 %s found" % type(iter_or_path).__name__)
1010
1011     @property
1012     def path(self):
1013         return self.model.get_path(self.iter)
1014
1015     @property
1016     def next(self):
1017         return self.get_next()
1018
1019     @property
1020     def parent(self):
1021         return self.get_parent()
1022
1023     def get_next(self):
1024         next_iter = self.model.iter_next(self.iter)
1025         if next_iter:
1026             return TreeModelRow(self.model, next_iter)
1027
1028     def get_parent(self):
1029         parent_iter = self.model.iter_parent(self.iter)
1030         if parent_iter:
1031             return TreeModelRow(self.model, parent_iter)
1032
1033     def __getitem__(self, key):
1034         if isinstance(key, int):
1035             if key >= self.model.get_n_columns():
1036                 raise IndexError("column index is out of bounds: %d" % key)
1037             elif key < 0:
1038                 key = self._convert_negative_index(key)
1039             return self.model.get_value(self.iter, key)
1040         elif isinstance(key, slice):
1041             start, stop, step = key.indices(self.model.get_n_columns())
1042             alist = []
1043             for i in range(start, stop, step):
1044                 alist.append(self.model.get_value(self.iter, i))
1045             return alist
1046         else:
1047             raise TypeError("indices must be integers, not %s" % type(key).__name__)
1048
1049     def __setitem__(self, key, value):
1050         if isinstance(key, int):
1051             if key >= self.model.get_n_columns():
1052                 raise IndexError("column index is out of bounds: %d" % key)
1053             elif key < 0:
1054                 key = self._convert_negative_index(key)
1055             self.model.set_value(self.iter, key, value)
1056         elif isinstance(key, slice):
1057             start, stop, step = key.indices(self.model.get_n_columns())
1058             indexList = range(start, stop, step)
1059             if len(indexList) != len(value):
1060                 raise ValueError(
1061                     "attempt to assign sequence of size %d to slice of size %d"
1062                         % (len(value), len(indexList)))
1063
1064             for i,v in enumerate(indexList):
1065                 self.model.set_value(self.iter, v, value[i])
1066         else:
1067             raise TypeError("index must be an integer or slice, not %s" % type(key).__name__)
1068
1069     def _convert_negative_index(self, index):
1070         new_index = self.model.get_n_columns() + index
1071         if new_index < 0:
1072             raise IndexError("column index is out of bounds: %d" % index)
1073         return new_index
1074
1075     def iterchildren(self):
1076         child_iter = self.model.iter_children(self.iter)
1077         return TreeModelRowIter(self.model, child_iter)
1078
1079 __all__.append('TreeModelRow')
1080
1081 class TreeModelRowIter(object):
1082
1083     def __init__(self, model, aiter):
1084         self.model = model
1085         self.iter = aiter
1086
1087     def __next__(self):
1088         if not self.iter:
1089             raise StopIteration
1090         row = TreeModelRow(self.model, self.iter)
1091         self.iter = self.model.iter_next(self.iter)
1092         return row
1093
1094     # alias for Python 2.x object protocol
1095     next = __next__
1096
1097     def __iter__(self):
1098         return self
1099
1100 __all__.append('TreeModelRowIter')
1101
1102 class TreePath(Gtk.TreePath):
1103
1104     def __new__(cls, path=0):
1105         if isinstance(path, int):
1106             path = str(path)
1107         elif isinstance(path, tuple):
1108             path = ":".join(str(val) for val in path)
1109
1110         if len(path) == 0:
1111             raise TypeError("could not parse subscript '%s' as a tree path" % path)
1112         try:
1113             return TreePath.new_from_string(path)
1114         except TypeError:
1115             raise TypeError("could not parse subscript '%s' as a tree path" % path)
1116
1117     def __str__(self):
1118         return self.to_string()
1119
1120     def __lt__(self, other):
1121         return not other is None and self.compare(other) < 0
1122
1123     def __le__(self, other):
1124         return not other is None and self.compare(other) <= 0
1125
1126     def __eq__(self, other):
1127         return not other is None and self.compare(other) == 0
1128
1129     def __ne__(self, other):
1130         return other is None or self.compare(other) != 0
1131
1132     def __gt__(self, other):
1133         return other is None or self.compare(other) > 0
1134
1135     def __ge__(self, other):
1136         return other is None or self.compare(other) >= 0
1137
1138 TreePath = override(TreePath)
1139 __all__.append('TreePath')
1140
1141 class TreeStore(Gtk.TreeStore, TreeModel, TreeSortable):
1142
1143     def __init__(self, *column_types):
1144         Gtk.TreeStore.__init__(self)
1145         self.set_column_types(column_types)
1146
1147     def append(self, parent, row=None):
1148         treeiter = Gtk.TreeStore.append(self, parent)
1149
1150         if row is not None:
1151             self.set_row(treeiter, row)
1152
1153         return treeiter
1154
1155     def insert(self, parent, position, row=None):
1156         treeiter = Gtk.TreeStore.insert(self, parent, position)
1157
1158         if row is not None:
1159             self.set_row(treeiter, row)
1160
1161         return treeiter
1162
1163     def insert_before(self, parent, sibling, row=None):
1164         treeiter = Gtk.TreeStore.insert_before(self, parent, sibling)
1165
1166         if row is not None:
1167             self.set_row(treeiter, row)
1168
1169         return treeiter
1170
1171     def insert_after(self, parent, sibling, row=None):
1172         treeiter = Gtk.TreeStore.insert_after(self, parent, sibling)
1173
1174         if row is not None:
1175             self.set_row(treeiter, row)
1176
1177         return treeiter
1178
1179     def set_value(self, treeiter, column, value):
1180         value = self._convert_value(treeiter, column, value)
1181         Gtk.TreeStore.set_value(self, treeiter, column, value)
1182
1183     def set(self, treeiter, *args):
1184
1185         def _set_lists(columns, values):
1186             if len(columns) != len(values):
1187                 raise TypeError('The number of columns do not match the number of values')
1188             for col_num, val in zip(columns, values):
1189                 if not isinstance(col_num, int):
1190                     raise TypeError('TypeError: Expected integer argument for column.')
1191                 self.set_value(treeiter, col_num, val)
1192
1193         if args:
1194             if isinstance(args[0], int):
1195                 columns = args[::2]
1196                 values = args[1::2]
1197                 _set_lists(columns, values)
1198             elif isinstance(args[0], (tuple, list)):
1199                 if len(args) != 2:
1200                     raise TypeError('Too many arguments');
1201                 _set_lists(args[0], args[1])
1202             elif isinstance(args[0], dict):
1203                 columns = args[0].keys()
1204                 values = args[0].values()
1205                 _set_lists(columns, values)
1206             else:
1207                 raise TypeError('Argument list must be in the form of (column, value, ...), ((columns,...), (values, ...)) or {column: value}.  No -1 termination is needed.')
1208
1209 TreeStore = override(TreeStore)
1210 __all__.append('TreeStore')
1211
1212 class TreeView(Gtk.TreeView, Container):
1213
1214     def __init__(self, model=None):
1215         Gtk.TreeView.__init__(self)
1216         if model:
1217             self.set_model(model)
1218
1219     def get_path_at_pos(self, x, y):
1220         success, path, column, cell_x, cell_y = super(TreeView, self).get_path_at_pos(x, y)
1221         if success:
1222             return (path, column, cell_x, cell_y,)
1223
1224     def get_dest_row_at_pos(self, drag_x, drag_y):
1225         success, path, pos = super(TreeView, self).get_dest_row_at_pos(drag_x, drag_y)
1226         if success:
1227             return (path, pos,)
1228
1229     def _construct_target_list(self, targets):
1230         # FIXME: this should most likely be part of Widget or a global helper
1231         #        function
1232         target_entries = []
1233         for t in targets:
1234             entry = Gtk.TargetEntry.new(*t)
1235             target_entries.append(entry)
1236         return target_entries
1237
1238     def enable_model_drag_source(self, start_button_mask, targets, actions):
1239         target_entries = self._construct_target_list(targets)
1240         super(TreeView, self).enable_model_drag_source(start_button_mask,
1241                                                        target_entries,
1242                                                        actions)
1243
1244     def enable_model_drag_dest(self, targets, actions):
1245         target_entries = self._construct_target_list(targets)
1246         super(TreeView, self).enable_model_drag_dest(target_entries,
1247                                                      actions)
1248
1249     def scroll_to_cell(self, path, column=None, use_align=False, row_align=0.0, col_align=0.0):
1250         if not isinstance(path, Gtk.TreePath):
1251             path = TreePath(path)
1252         super(TreeView, self).scroll_to_cell(path, column, use_align, row_align, col_align)
1253
1254
1255 TreeView = override(TreeView)
1256 __all__.append('TreeView')
1257
1258 class TreeViewColumn(Gtk.TreeViewColumn):
1259     def __init__(self, title='',
1260                  cell_renderer=None,
1261                  **attributes):
1262         Gtk.TreeViewColumn.__init__(self, title=title)
1263         if cell_renderer:
1264             self.pack_start(cell_renderer, True)
1265
1266         for (name, value) in attributes.items():
1267             self.add_attribute(cell_renderer, name, value)
1268
1269     def cell_get_position(self, cell_renderer):
1270         success, start_pos, width = super(TreeViewColumn, self).cell_get_position(cell_renderer)
1271         if success:
1272             return (start_pos, width,)
1273
1274     def set_cell_data_func(self, cell_renderer, func, func_data=None):
1275         super(TreeViewColumn, self).set_cell_data_func(cell_renderer, func, func_data)
1276
1277 TreeViewColumn = override(TreeViewColumn)
1278 __all__.append('TreeViewColumn')
1279
1280 class TreeSelection(Gtk.TreeSelection):
1281
1282     def select_path(self, path):
1283         if not isinstance(path, Gtk.TreePath):
1284             path = TreePath(path)
1285         super(TreeSelection, self).select_path(path)
1286
1287     def get_selected(self):
1288         success, model, aiter = super(TreeSelection, self).get_selected()
1289         if success:
1290             return (model, aiter)
1291         else:
1292             return (model, None)
1293
1294     # for compatibility with PyGtk
1295     def get_selected_rows(self):
1296         rows, model = super(TreeSelection, self).get_selected_rows()
1297         return (model, rows)
1298
1299
1300 TreeSelection = override(TreeSelection)
1301 __all__.append('TreeSelection')
1302
1303 class Button(Gtk.Button, Container):
1304     def __init__(self, label=None, stock=None, use_underline=False, **kwds):
1305         if stock:
1306             label = stock
1307             use_stock = True
1308             use_underline = True
1309         else:
1310             use_stock = False
1311         Gtk.Button.__init__(self, label=label, use_stock=use_stock,
1312                             use_underline=use_underline, **kwds)
1313 Button = override(Button)
1314 __all__.append('Button')
1315
1316 class LinkButton(Gtk.LinkButton):
1317     def __init__(self, uri, label=None, **kwds):
1318         Gtk.LinkButton.__init__(self, uri=uri, label=label, **kwds)
1319
1320 LinkButton = override(LinkButton)
1321 __all__.append('LinkButton')
1322
1323 class Label(Gtk.Label):
1324     def __init__(self, label=None, **kwds):
1325         Gtk.Label.__init__(self, label=label, **kwds)
1326
1327 Label = override(Label)
1328 __all__.append('Label')
1329
1330 class Adjustment(Gtk.Adjustment):
1331     def __init__(self, *args, **kwds):
1332         arg_names = ('value', 'lower', 'upper',
1333                         'step_increment', 'page_increment', 'page_size')
1334         new_args = dict(zip(arg_names, args))
1335         new_args.update(kwds)
1336         Gtk.Adjustment.__init__(self, **new_args)
1337
1338         # The value property is set between lower and (upper - page_size).
1339         # Just in case lower, upper or page_size was still 0 when value
1340         # was set, we set it again here.
1341         if 'value' in new_args:
1342             self.set_value(new_args['value'])
1343
1344 Adjustment = override(Adjustment)
1345 __all__.append('Adjustment')
1346
1347 class Table(Gtk.Table, Container):
1348     def __init__(self, rows=1, columns=1, homogeneous=False, **kwds):
1349         if 'n_rows' in kwds:
1350             rows = kwds.pop('n_rows')
1351
1352         if 'n_columns' in kwds:
1353             columns = kwds.pop('n_columns')
1354             
1355         Gtk.Table.__init__(self, n_rows=rows, n_columns=columns, homogeneous=homogeneous, **kwds)
1356
1357     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):
1358         Gtk.Table.attach(self, child, left_attach, right_attach, top_attach, bottom_attach, xoptions, yoptions, xpadding, ypadding)
1359
1360 Table = override(Table)
1361 __all__.append('Table')
1362
1363 class ScrolledWindow(Gtk.ScrolledWindow):
1364     def __init__(self, hadjustment=None, vadjustment=None, **kwds):
1365         Gtk.ScrolledWindow.__init__(self, hadjustment=hadjustment, vadjustment=vadjustment, **kwds)
1366
1367 ScrolledWindow = override(ScrolledWindow)
1368 __all__.append('ScrolledWindow')
1369
1370 class Paned(Gtk.Paned):
1371     def pack1(self, child, resize=False, shrink=True):
1372         super(Paned, self).pack1(child, resize, shrink)
1373
1374     def pack2(self, child, resize=True, shrink=True):
1375         super(Paned, self).pack2(child, resize, shrink)
1376
1377 Paned = override(Paned)
1378 __all__.append('Paned')
1379
1380 if Gtk._version != '2.0':
1381     class Menu(Gtk.Menu):
1382         def popup(self, parent_menu_shell, parent_menu_item, func, data, button, activate_time):
1383             self.popup_for_device(None, parent_menu_shell, parent_menu_item, func, data, button, activate_time)
1384     Menu = override(Menu)
1385     __all__.append('Menu')
1386
1387 _Gtk_main_quit = Gtk.main_quit
1388 @override(Gtk.main_quit)
1389 def main_quit(*args):
1390     _Gtk_main_quit()
1391
1392 _Gtk_stock_lookup = Gtk.stock_lookup
1393 @override(Gtk.stock_lookup)
1394 def stock_lookup(*args):
1395     success, item = _Gtk_stock_lookup(*args)
1396     if not success:
1397         return None
1398
1399     return item
1400
1401 initialized, argv = Gtk.init_check(sys.argv)
1402 sys.argv = list(argv)
1403 if not initialized:
1404     raise RuntimeError("Gtk couldn't be initialized")