Imported Upstream version 3.25.1
[platform/upstream/pygobject2.git] / tests / test_overrides_gtk.py
1 # -*- Mode: Python; py-indent-offset: 4 -*-
2 # coding: UTF-8
3 # vim: tabstop=4 shiftwidth=4 expandtab
4
5 import contextlib
6 import unittest
7 import time
8 import sys
9 import warnings
10
11 from helper import ignore_gi_deprecation_warnings, capture_glib_warnings
12
13 import gi.overrides
14 import gi.types
15 from gi.repository import GLib, GObject
16
17 try:
18     from gi.repository import Gtk, GdkPixbuf, Gdk
19     PyGTKDeprecationWarning = Gtk.PyGTKDeprecationWarning
20 except ImportError:
21     Gtk = None
22     PyGTKDeprecationWarning = None
23     GdkPixbuf = None
24     Gdk = None
25
26
27 @contextlib.contextmanager
28 def realized(widget):
29     """Makes sure the widget is realized.
30
31     view = Gtk.TreeView()
32     with realized(view):
33         do_something(view)
34     """
35
36     if isinstance(widget, Gtk.Window):
37         toplevel = widget
38     else:
39         toplevel = widget.get_parent_window()
40
41     if toplevel is None:
42         window = Gtk.Window()
43         window.add(widget)
44
45     widget.realize()
46     while Gtk.events_pending():
47         Gtk.main_iteration()
48     assert widget.get_realized()
49     yield widget
50
51     if toplevel is None:
52         window.remove(widget)
53         window.destroy()
54
55     while Gtk.events_pending():
56         Gtk.main_iteration()
57
58
59 @unittest.skipUnless(Gtk, 'Gtk not available')
60 @ignore_gi_deprecation_warnings
61 class TestGtk(unittest.TestCase):
62     def test_container(self):
63         box = Gtk.Box()
64         self.assertTrue(isinstance(box, Gtk.Box))
65         self.assertTrue(isinstance(box, Gtk.Container))
66         self.assertTrue(isinstance(box, Gtk.Widget))
67         self.assertTrue(box)
68         label = Gtk.Label()
69         label2 = Gtk.Label()
70         box.add(label)
71         box.add(label2)
72         self.assertTrue(label in box)
73         self.assertTrue(label2 in box)
74         self.assertEqual(len(box), 2)
75         self.assertTrue(box)
76         labels = [x for x in box]
77         self.assertEqual(labels, [label, label2])
78
79     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
80     def test_actions(self):
81         self.assertEqual(Gtk.Action, gi.overrides.Gtk.Action)
82         action = Gtk.Action(name="test", label="Test", tooltip="Test Action", stock_id=Gtk.STOCK_COPY)
83         self.assertEqual(action.get_name(), "test")
84         self.assertEqual(action.get_label(), "Test")
85         self.assertEqual(action.get_tooltip(), "Test Action")
86         self.assertEqual(action.get_stock_id(), Gtk.STOCK_COPY)
87
88         self.assertEqual(Gtk.RadioAction, gi.overrides.Gtk.RadioAction)
89         action = Gtk.RadioAction(name="test", label="Test", tooltip="Test Action", stock_id=Gtk.STOCK_COPY, value=1)
90         self.assertEqual(action.get_name(), "test")
91         self.assertEqual(action.get_label(), "Test")
92         self.assertEqual(action.get_tooltip(), "Test Action")
93         self.assertEqual(action.get_stock_id(), Gtk.STOCK_COPY)
94         self.assertEqual(action.get_current_value(), 1)
95
96     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
97     def test_actiongroup(self):
98         self.assertEqual(Gtk.ActionGroup, gi.overrides.Gtk.ActionGroup)
99
100         action_group = Gtk.ActionGroup(name='TestActionGroup')
101         callback_data = "callback data"
102
103         def test_action_callback_data(action, user_data):
104             self.assertEqual(user_data, callback_data)
105
106         def test_radio_action_callback_data(action, current, user_data):
107             self.assertEqual(user_data, callback_data)
108
109         action_group.add_actions([
110             ('test-action1', None, 'Test Action 1',
111              None, None, test_action_callback_data),
112             ('test-action2', Gtk.STOCK_COPY, 'Test Action 2',
113              None, None, test_action_callback_data)], callback_data)
114         action_group.add_toggle_actions([
115             ('test-toggle-action1', None, 'Test Toggle Action 1',
116              None, None, test_action_callback_data, False),
117             ('test-toggle-action2', Gtk.STOCK_COPY, 'Test Toggle Action 2',
118              None, None, test_action_callback_data, True)], callback_data)
119         action_group.add_radio_actions([
120             ('test-radio-action1', None, 'Test Radio Action 1'),
121             ('test-radio-action2', Gtk.STOCK_COPY, 'Test Radio Action 2')], 1,
122             test_radio_action_callback_data,
123             callback_data)
124
125         expected_results = [('test-action1', Gtk.Action),
126                             ('test-action2', Gtk.Action),
127                             ('test-toggle-action1', Gtk.ToggleAction),
128                             ('test-toggle-action2', Gtk.ToggleAction),
129                             ('test-radio-action1', Gtk.RadioAction),
130                             ('test-radio-action2', Gtk.RadioAction)]
131
132         for action in action_group.list_actions():
133             a = (action.get_name(), type(action))
134             self.assertTrue(a in expected_results)
135             expected_results.remove(a)
136             action.activate()
137
138     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
139     def test_uimanager(self):
140         self.assertEqual(Gtk.UIManager, gi.overrides.Gtk.UIManager)
141         ui = Gtk.UIManager()
142         ui.add_ui_from_string("""<ui>
143     <menubar name="menubar1"></menubar>
144 </ui>
145 """
146 )
147         menubar = ui.get_widget("/menubar1")
148         self.assertEqual(type(menubar), Gtk.MenuBar)
149
150         ag = Gtk.ActionGroup(name="ag1")
151         ui.insert_action_group(ag)
152         ag2 = Gtk.ActionGroup(name="ag2")
153         ui.insert_action_group(ag2)
154         groups = ui.get_action_groups()
155         self.assertEqual(ag, groups[-2])
156         self.assertEqual(ag2, groups[-1])
157
158     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
159     def test_uimanager_nonascii(self):
160         ui = Gtk.UIManager()
161         ui.add_ui_from_string(b'<ui><menubar name="menub\xc3\xa6r1" /></ui>'.decode('UTF-8'))
162         mi = ui.get_widget("/menubær1")
163         self.assertEqual(type(mi), Gtk.MenuBar)
164
165     def test_window(self):
166         # standard Window
167         w = Gtk.Window()
168         self.assertEqual(w.get_property('type'), Gtk.WindowType.TOPLEVEL)
169
170         # type works as keyword argument
171         w = Gtk.Window(type=Gtk.WindowType.POPUP)
172         self.assertEqual(w.get_property('type'), Gtk.WindowType.POPUP)
173
174         class TestWindow(Gtk.Window):
175             __gtype_name__ = "TestWindow"
176
177         # works from builder
178         builder = Gtk.Builder()
179         builder.add_from_string('''
180 <interface>
181   <object class="GtkWindow" id="win">
182     <property name="type">popup</property>
183   </object>
184   <object class="TestWindow" id="testwin">
185   </object>
186   <object class="TestWindow" id="testpop">
187     <property name="type">popup</property>
188   </object>
189 </interface>''')
190         self.assertEqual(builder.get_object('win').get_property('type'),
191                          Gtk.WindowType.POPUP)
192         self.assertEqual(builder.get_object('testwin').get_property('type'),
193                          Gtk.WindowType.TOPLEVEL)
194         self.assertEqual(builder.get_object('testpop').get_property('type'),
195                          Gtk.WindowType.POPUP)
196
197     def test_dialog_classes(self):
198         self.assertEqual(Gtk.Dialog, gi.overrides.Gtk.Dialog)
199         self.assertEqual(Gtk.FileChooserDialog, gi.overrides.Gtk.FileChooserDialog)
200         self.assertEqual(Gtk.RecentChooserDialog, gi.overrides.Gtk.RecentChooserDialog)
201         if Gtk._version != "4.0":
202             self.assertEqual(Gtk.ColorSelectionDialog, gi.overrides.Gtk.ColorSelectionDialog)
203             self.assertEqual(Gtk.FontSelectionDialog, gi.overrides.Gtk.FontSelectionDialog)
204
205     def test_dialog_base(self):
206         dialog = Gtk.Dialog(title='Foo', modal=True)
207         self.assertTrue(isinstance(dialog, Gtk.Dialog))
208         self.assertTrue(isinstance(dialog, Gtk.Window))
209         self.assertEqual('Foo', dialog.get_title())
210         self.assertTrue(dialog.get_modal())
211
212     def test_dialog_deprecations(self):
213         with warnings.catch_warnings(record=True) as warn:
214             warnings.simplefilter('always')
215             dialog = Gtk.Dialog(title='Foo', flags=Gtk.DialogFlags.MODAL)
216             self.assertTrue(dialog.get_modal())
217             self.assertEqual(len(warn), 1)
218             self.assertTrue(issubclass(warn[0].category, PyGTKDeprecationWarning))
219             self.assertRegexpMatches(str(warn[0].message),
220                                      '.*flags.*modal.*')
221
222         with warnings.catch_warnings(record=True) as warn:
223             warnings.simplefilter('always')
224             dialog = Gtk.Dialog(title='Foo', flags=Gtk.DialogFlags.DESTROY_WITH_PARENT)
225             self.assertTrue(dialog.get_destroy_with_parent())
226             self.assertEqual(len(warn), 1)
227             self.assertTrue(issubclass(warn[0].category, PyGTKDeprecationWarning))
228             self.assertRegexpMatches(str(warn[0].message),
229                                      '.*flags.*destroy_with_parent.*')
230
231     def test_dialog_deprecation_stacklevels(self):
232         # Test warning levels are setup to give the correct filename for
233         # deprecations in different classes in the inheritance hierarchy.
234
235         # Base class
236         self.assertEqual(Gtk.Dialog, gi.overrides.Gtk.Dialog)
237         with warnings.catch_warnings(record=True) as warn:
238             warnings.simplefilter('always')
239             Gtk.Dialog(flags=Gtk.DialogFlags.MODAL)
240             self.assertEqual(len(warn), 1)
241             self.assertRegexpMatches(warn[0].filename, '.*test_overrides_gtk.*')
242
243         # Validate overridden base with overridden sub-class.
244         self.assertEqual(Gtk.MessageDialog, gi.overrides.Gtk.MessageDialog)
245         with warnings.catch_warnings(record=True) as warn:
246             warnings.simplefilter('always')
247             Gtk.MessageDialog(flags=Gtk.DialogFlags.MODAL)
248             self.assertEqual(len(warn), 1)
249             self.assertRegexpMatches(warn[0].filename, '.*test_overrides_gtk.*')
250
251         # Validate overridden base with non-overridden sub-class.
252         self.assertEqual(Gtk.AboutDialog, gi.repository.Gtk.AboutDialog)
253         with warnings.catch_warnings(record=True) as warn:
254             warnings.simplefilter('always')
255             Gtk.AboutDialog(flags=Gtk.DialogFlags.MODAL)
256             self.assertEqual(len(warn), 1)
257             self.assertRegexpMatches(warn[0].filename, '.*test_overrides_gtk.*')
258
259     def test_dialog_add_buttons(self):
260         # The overloaded "buttons" keyword gives a warning when attempting
261         # to use it for adding buttons as was available in PyGTK.
262         with warnings.catch_warnings(record=True) as warn:
263             warnings.simplefilter('always')
264             dialog = Gtk.Dialog(title='Foo', modal=True,
265                                 buttons=('test-button1', 1))
266             self.assertEqual(len(warn), 1)
267             self.assertTrue(issubclass(warn[0].category, PyGTKDeprecationWarning))
268             self.assertRegexpMatches(str(warn[0].message),
269                                      '.*ButtonsType.*add_buttons.*')
270
271         dialog.add_buttons('test-button2', 2, 'gtk-close', Gtk.ResponseType.CLOSE)
272         button = dialog.get_widget_for_response(1)
273         self.assertEqual('test-button1', button.get_label())
274         button = dialog.get_widget_for_response(2)
275         self.assertEqual('test-button2', button.get_label())
276         button = dialog.get_widget_for_response(Gtk.ResponseType.CLOSE)
277         self.assertEqual('gtk-close', button.get_label())
278
279     def test_about_dialog(self):
280         dialog = Gtk.AboutDialog()
281         self.assertTrue(isinstance(dialog, Gtk.Dialog))
282         self.assertTrue(isinstance(dialog, Gtk.Window))
283
284         # AboutDialog is not sub-classed in overrides, make sure
285         # the mro still injects the base class "add_buttons" override.
286         self.assertTrue(hasattr(dialog, 'add_buttons'))
287
288     def test_message_dialog(self):
289         dialog = Gtk.MessageDialog(title='message dialog test',
290                                    modal=True,
291                                    buttons=Gtk.ButtonsType.OK,
292                                    text='dude!')
293         self.assertTrue(isinstance(dialog, Gtk.Dialog))
294         self.assertTrue(isinstance(dialog, Gtk.Window))
295
296         self.assertEqual('message dialog test', dialog.get_title())
297         self.assertTrue(dialog.get_modal())
298         text = dialog.get_property('text')
299         self.assertEqual('dude!', text)
300
301         dialog.format_secondary_text('2nd text')
302         self.assertEqual(dialog.get_property('secondary-text'), '2nd text')
303         self.assertFalse(dialog.get_property('secondary-use-markup'))
304
305         dialog.format_secondary_markup('2nd markup')
306         self.assertEqual(dialog.get_property('secondary-text'), '2nd markup')
307         self.assertTrue(dialog.get_property('secondary-use-markup'))
308
309     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
310     def test_color_selection_dialog(self):
311         dialog = Gtk.ColorSelectionDialog(title="color selection dialog test")
312         self.assertTrue(isinstance(dialog, Gtk.Dialog))
313         self.assertTrue(isinstance(dialog, Gtk.Window))
314         self.assertEqual('color selection dialog test', dialog.get_title())
315
316     def test_file_chooser_dialog(self):
317         # might cause a GVFS warning, do not break on this
318         with capture_glib_warnings(allow_warnings=True):
319             dialog = Gtk.FileChooserDialog(title='file chooser dialog test',
320                                            action=Gtk.FileChooserAction.SAVE)
321
322         self.assertTrue(isinstance(dialog, Gtk.Dialog))
323         self.assertTrue(isinstance(dialog, Gtk.Window))
324         self.assertEqual('file chooser dialog test', dialog.get_title())
325
326         action = dialog.get_property('action')
327         self.assertEqual(Gtk.FileChooserAction.SAVE, action)
328
329     def test_file_chooser_dialog_default_action(self):
330         # might cause a GVFS warning, do not break on this
331         with capture_glib_warnings(allow_warnings=True):
332             dialog = Gtk.FileChooserDialog(title='file chooser dialog test')
333
334         action = dialog.get_property('action')
335         self.assertEqual(Gtk.FileChooserAction.OPEN, action)
336
337     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
338     def test_font_selection_dialog(self):
339         dialog = Gtk.FontSelectionDialog(title="font selection dialog test")
340         self.assertTrue(isinstance(dialog, Gtk.Dialog))
341         self.assertTrue(isinstance(dialog, Gtk.Window))
342         self.assertEqual('font selection dialog test', dialog.get_title())
343
344     def test_recent_chooser_dialog(self):
345         test_manager = Gtk.RecentManager()
346         dialog = Gtk.RecentChooserDialog(title='recent chooser dialog test',
347                                          recent_manager=test_manager)
348         self.assertTrue(isinstance(dialog, Gtk.Dialog))
349         self.assertTrue(isinstance(dialog, Gtk.Window))
350         self.assertEqual('recent chooser dialog test', dialog.get_title())
351
352     class TestClass(GObject.GObject):
353         __gtype_name__ = "GIOverrideTreeAPITest"
354
355         def __init__(self, tester, int_value, string_value):
356             super(TestGtk.TestClass, self).__init__()
357             self.tester = tester
358             self.int_value = int_value
359             self.string_value = string_value
360
361         def check(self, int_value, string_value):
362             self.tester.assertEqual(int_value, self.int_value)
363             self.tester.assertEqual(string_value, self.string_value)
364
365     def test_buttons(self):
366         self.assertEqual(Gtk.Button, gi.overrides.Gtk.Button)
367
368         # test Gtk.Button
369         button = Gtk.Button()
370         self.assertTrue(isinstance(button, Gtk.Button))
371         self.assertTrue(isinstance(button, Gtk.Container))
372         self.assertTrue(isinstance(button, Gtk.Widget))
373
374         if Gtk._version != "4.0":
375             # Using stock items causes hard warning in devel versions of GTK+.
376             with capture_glib_warnings(allow_warnings=True):
377                 button = Gtk.Button.new_from_stock(Gtk.STOCK_CLOSE)
378
379             self.assertEqual(Gtk.STOCK_CLOSE, button.get_label())
380             self.assertTrue(button.get_use_stock())
381             self.assertTrue(button.get_use_underline())
382
383             # test Gtk.Button use_stock
384             button = Gtk.Button(label=Gtk.STOCK_CLOSE, use_stock=True,
385                                 use_underline=True)
386             self.assertEqual(Gtk.STOCK_CLOSE, button.get_label())
387             self.assertTrue(button.get_use_stock())
388             self.assertTrue(button.get_use_underline())
389
390         # test Gtk.LinkButton
391         button = Gtk.LinkButton(uri='http://www.Gtk.org', label='Gtk')
392         self.assertTrue(isinstance(button, Gtk.Button))
393         self.assertTrue(isinstance(button, Gtk.Container))
394         self.assertTrue(isinstance(button, Gtk.Widget))
395         self.assertEqual('http://www.Gtk.org', button.get_uri())
396         self.assertEqual('Gtk', button.get_label())
397
398     def test_inheritance(self):
399         for name in gi.overrides.Gtk.__all__:
400             over = getattr(gi.overrides.Gtk, name)
401             for element in dir(Gtk):
402                 try:
403                     klass = getattr(Gtk, element)
404                     info = klass.__info__
405                 except (NotImplementedError, AttributeError):
406                     continue
407
408                 # Get all parent classes and interfaces klass inherits from
409                 if isinstance(info, gi.types.ObjectInfo):
410                     classes = list(info.get_interfaces())
411                     parent = info.get_parent()
412                     while parent.get_name() != "Object":
413                         classes.append(parent)
414                         parent = parent.get_parent()
415                     classes = [kl for kl in classes if kl.get_namespace() == "Gtk"]
416                 else:
417                     continue
418
419                 for kl in classes:
420                     if kl.get_name() == name:
421                         self.assertTrue(issubclass(klass, over,),
422                                         "%r does not inherit from override %r" % (klass, over,))
423
424     def test_editable(self):
425         self.assertEqual(Gtk.Editable, gi.overrides.Gtk.Editable)
426
427         # need to use Gtk.Entry because Editable is an interface
428         entry = Gtk.Entry()
429         pos = entry.insert_text('HeWorld', 0)
430         self.assertEqual(pos, 7)
431         pos = entry.insert_text('llo ', 2)
432         self.assertEqual(pos, 6)
433         text = entry.get_chars(0, 11)
434         self.assertEqual('Hello World', text)
435
436     def test_label(self):
437         label = Gtk.Label(label='Hello')
438         self.assertTrue(isinstance(label, Gtk.Widget))
439         self.assertEqual(label.get_text(), 'Hello')
440
441     def adjustment_check(self, adjustment, value=0.0, lower=0.0, upper=0.0,
442                          step_increment=0.0, page_increment=0.0, page_size=0.0):
443         self.assertEqual(adjustment.get_value(), value)
444         self.assertEqual(adjustment.get_lower(), lower)
445         self.assertEqual(adjustment.get_upper(), upper)
446         self.assertEqual(adjustment.get_step_increment(), step_increment)
447         self.assertEqual(adjustment.get_page_increment(), page_increment)
448         self.assertEqual(adjustment.get_page_size(), page_size)
449
450     def test_adjustment(self):
451         adjustment = Gtk.Adjustment(value=1, lower=0, upper=6, step_increment=4, page_increment=5, page_size=3)
452         self.adjustment_check(adjustment, value=1, lower=0, upper=6, step_increment=4, page_increment=5, page_size=3)
453
454         adjustment = Gtk.Adjustment(value=1, lower=0, upper=6, step_increment=4, page_increment=5)
455         self.adjustment_check(adjustment, value=1, lower=0, upper=6, step_increment=4, page_increment=5)
456
457         adjustment = Gtk.Adjustment(value=1, lower=0, upper=6, step_increment=4)
458         self.adjustment_check(adjustment, value=1, lower=0, upper=6, step_increment=4)
459
460         adjustment = Gtk.Adjustment(value=1, lower=0, upper=6)
461         self.adjustment_check(adjustment, value=1, lower=0, upper=6)
462
463         adjustment = Gtk.Adjustment()
464         self.adjustment_check(adjustment)
465
466     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
467     def test_table(self):
468         table = Gtk.Table()
469         self.assertTrue(isinstance(table, Gtk.Table))
470         self.assertTrue(isinstance(table, Gtk.Container))
471         self.assertTrue(isinstance(table, Gtk.Widget))
472         self.assertEqual(table.get_size(), (1, 1))
473         self.assertEqual(table.get_homogeneous(), False)
474
475         table = Gtk.Table(n_rows=2, n_columns=3)
476         self.assertEqual(table.get_size(), (2, 3))
477         self.assertEqual(table.get_homogeneous(), False)
478
479         table = Gtk.Table(n_rows=2, n_columns=3, homogeneous=True)
480         self.assertEqual(table.get_size(), (2, 3))
481         self.assertEqual(table.get_homogeneous(), True)
482
483         label = Gtk.Label(label='Hello')
484         self.assertTrue(isinstance(label, Gtk.Widget))
485         table.attach(label, 0, 1, 0, 1)
486         self.assertEqual(label, table.get_children()[0])
487
488     def test_scrolledwindow(self):
489         sw = Gtk.ScrolledWindow()
490         self.assertTrue(isinstance(sw, Gtk.ScrolledWindow))
491         self.assertTrue(isinstance(sw, Gtk.Container))
492         self.assertTrue(isinstance(sw, Gtk.Widget))
493         sb = sw.get_hscrollbar()
494         self.assertEqual(sw.get_hadjustment(), sb.get_adjustment())
495         sb = sw.get_vscrollbar()
496         self.assertEqual(sw.get_vadjustment(), sb.get_adjustment())
497
498     def test_widget_drag_methods(self):
499         widget = Gtk.Button()
500
501         # here we are not checking functionality, only that the methods exist
502         # and except the right number of arguments
503
504         widget.drag_check_threshold(0, 0, 0, 0)
505
506         # drag_dest_ methods
507         widget.drag_dest_set(Gtk.DestDefaults.DROP, None, Gdk.DragAction.COPY)
508         widget.drag_dest_add_image_targets()
509         widget.drag_dest_add_text_targets()
510         widget.drag_dest_add_uri_targets()
511         widget.drag_dest_get_track_motion()
512         widget.drag_dest_set_track_motion(True)
513         widget.drag_dest_get_target_list()
514         widget.drag_dest_set_target_list(None)
515         widget.drag_dest_set_target_list(Gtk.TargetList.new([Gtk.TargetEntry.new('test', 0, 0)]))
516         widget.drag_dest_unset()
517
518         widget.drag_highlight()
519         widget.drag_unhighlight()
520
521         # drag_source_ methods
522         widget.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, None, Gdk.DragAction.MOVE)
523         widget.drag_source_add_image_targets()
524         widget.drag_source_add_text_targets()
525         widget.drag_source_add_uri_targets()
526         widget.drag_source_set_icon_name("_About")
527         widget.drag_source_set_icon_pixbuf(GdkPixbuf.Pixbuf())
528         if Gtk._version != "4.0":
529             widget.drag_source_set_icon_stock(Gtk.STOCK_ABOUT)
530         widget.drag_source_get_target_list()
531         widget.drag_source_set_target_list(None)
532         widget.drag_source_set_target_list(Gtk.TargetList.new([Gtk.TargetEntry.new('test', 0, 0)]))
533         widget.drag_source_unset()
534
535         # these methods cannot be called because they require a valid drag on
536         # a real GdkWindow. So we only check that they exist and are callable.
537         if Gtk._version != "4.0":
538             self.assertTrue(hasattr(widget, 'drag_dest_set_proxy'))
539         self.assertTrue(hasattr(widget, 'drag_get_data'))
540
541     def test_drag_target_list(self):
542         mixed_target_list = [Gtk.TargetEntry.new('test0', 0, 0),
543                              ('test1', 1, 1),
544                              Gtk.TargetEntry.new('test2', 2, 2),
545                              ('test3', 3, 3)]
546
547         def _test_target_list(targets):
548             for i, target in enumerate(targets):
549                 self.assertTrue(isinstance(target, Gtk.TargetEntry))
550                 self.assertEqual(target.target, 'test' + str(i))
551                 self.assertEqual(target.flags, i)
552                 self.assertEqual(target.info, i)
553
554         _test_target_list(Gtk._construct_target_list(mixed_target_list))
555
556         widget = Gtk.Button()
557         widget.drag_dest_set(Gtk.DestDefaults.DROP, None, Gdk.DragAction.COPY)
558         widget.drag_dest_set_target_list(mixed_target_list)
559         widget.drag_dest_get_target_list()
560
561         widget.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, None, Gdk.DragAction.MOVE)
562         widget.drag_source_set_target_list(mixed_target_list)
563         widget.drag_source_get_target_list()
564
565         treeview = Gtk.TreeView()
566         treeview.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK,
567                                           mixed_target_list,
568                                           Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE)
569
570         treeview.enable_model_drag_dest(mixed_target_list,
571                                         Gdk.DragAction.DEFAULT | Gdk.DragAction.MOVE)
572
573     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
574     def test_scrollbar(self):
575         adjustment = Gtk.Adjustment()
576
577         hscrollbar = Gtk.HScrollbar()
578         vscrollbar = Gtk.VScrollbar()
579         self.assertNotEqual(hscrollbar.props.adjustment, adjustment)
580         self.assertNotEqual(vscrollbar.props.adjustment, adjustment)
581
582         hscrollbar = Gtk.HScrollbar(adjustment=adjustment)
583         vscrollbar = Gtk.VScrollbar(adjustment=adjustment)
584         self.assertEqual(hscrollbar.props.adjustment, adjustment)
585         self.assertEqual(vscrollbar.props.adjustment, adjustment)
586
587     def test_iconview(self):
588         # PyGTK compat
589         iconview = Gtk.IconView()
590         self.assertEqual(iconview.props.model, None)
591
592         model = Gtk.ListStore(str)
593         iconview = Gtk.IconView(model=model)
594         self.assertEqual(iconview.props.model, model)
595
596     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
597     def test_toolbutton(self):
598         # PyGTK compat
599
600         # Using stock items causes hard warning in devel versions of GTK+.
601         with capture_glib_warnings(allow_warnings=True):
602             button = Gtk.ToolButton()
603             self.assertEqual(button.props.stock_id, None)
604
605             button = Gtk.ToolButton(stock_id='gtk-new')
606             self.assertEqual(button.props.stock_id, 'gtk-new')
607
608         icon = Gtk.Image.new_from_stock(Gtk.STOCK_OPEN, Gtk.IconSize.SMALL_TOOLBAR)
609         button = Gtk.ToolButton(label='mylabel', icon_widget=icon)
610         self.assertEqual(button.props.label, 'mylabel')
611         self.assertEqual(button.props.icon_widget, icon)
612
613     def test_toolbutton_gtk4(self):
614         icon = Gtk.Image.new()
615         button = Gtk.ToolButton(label='mylabel', icon_widget=icon)
616         self.assertEqual(button.props.label, 'mylabel')
617         self.assertEqual(button.props.icon_widget, icon)
618
619     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
620     def test_iconset(self):
621         Gtk.IconSet()
622         pixbuf = GdkPixbuf.Pixbuf()
623         Gtk.IconSet.new_from_pixbuf(pixbuf)
624
625     def test_viewport(self):
626         vadjustment = Gtk.Adjustment()
627         hadjustment = Gtk.Adjustment()
628
629         viewport = Gtk.Viewport(hadjustment=hadjustment,
630                                 vadjustment=vadjustment)
631
632         self.assertEqual(viewport.props.vadjustment, vadjustment)
633         self.assertEqual(viewport.props.hadjustment, hadjustment)
634
635     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
636     def test_stock_lookup(self):
637         stock_item = Gtk.stock_lookup('gtk-ok')
638         self.assertEqual(type(stock_item), Gtk.StockItem)
639         self.assertEqual(stock_item.stock_id, 'gtk-ok')
640         self.assertEqual(Gtk.stock_lookup('nosuchthing'), None)
641
642     def test_gtk_main(self):
643         # with no arguments
644         GLib.idle_add(Gtk.main_quit)
645         Gtk.main()
646
647         # overridden function ignores its arguments
648         GLib.idle_add(Gtk.main_quit, 'hello')
649         Gtk.main()
650
651     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
652     def test_widget_render_icon(self):
653         button = Gtk.Button(label='OK')
654         pixbuf = button.render_icon(Gtk.STOCK_OK, Gtk.IconSize.BUTTON)
655         self.assertTrue(pixbuf is not None)
656
657
658 @unittest.skipUnless(Gtk, 'Gtk not available')
659 @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
660 class TestWidget(unittest.TestCase):
661     def test_style_get_property_gvalue(self):
662         button = Gtk.Button()
663         value = GObject.Value(int, -42)
664         button.style_get_property('focus-padding', value)
665         # Test only that the style property changed since we can't actuall
666         # set it.
667         self.assertNotEqual(value.get_int(), -42)
668
669     def test_style_get_property_return_with_explicit_gvalue(self):
670         button = Gtk.Button()
671         value = GObject.Value(int, -42)
672         result = button.style_get_property('focus-padding', value)
673         self.assertIsInstance(result, int)
674         self.assertNotEqual(result, -42)
675
676     def test_style_get_property_return_with_implicit_gvalue(self):
677         button = Gtk.Button()
678         result = button.style_get_property('focus-padding')
679         self.assertIsInstance(result, int)
680         self.assertNotEqual(result, -42)
681
682     def test_style_get_property_error(self):
683         button = Gtk.Button()
684         with self.assertRaises(ValueError):
685             button.style_get_property('not-a-valid-style-property')
686
687
688 @unittest.skipIf(sys.platform == "darwin", "hangs")
689 @unittest.skipUnless(Gtk, 'Gtk not available')
690 class TestSignals(unittest.TestCase):
691     def test_class_closure_override_with_aliased_type(self):
692         class WindowWithSizeAllocOverride(Gtk.ScrolledWindow):
693             __gsignals__ = {'size-allocate': 'override'}
694
695             def __init__(self):
696                 Gtk.ScrolledWindow.__init__(self)
697                 self._alloc_called = False
698                 self._alloc_value = None
699                 self._alloc_error = None
700
701             def do_size_allocate(self, alloc):
702                 self._alloc_called = True
703                 self._alloc_value = alloc
704
705                 try:
706                     Gtk.ScrolledWindow.do_size_allocate(self, alloc)
707                 except Exception as e:
708                     self._alloc_error = e
709
710         win = WindowWithSizeAllocOverride()
711         rect = Gdk.Rectangle()
712         rect.width = 100
713         rect.height = 100
714
715         with realized(win):
716             win.show()
717             win.get_preferred_size()
718             win.size_allocate(rect)
719             self.assertTrue(win._alloc_called)
720             self.assertIsInstance(win._alloc_value, Gdk.Rectangle)
721             self.assertTrue(win._alloc_error is None, win._alloc_error)
722
723     @unittest.expectedFailure  # https://bugzilla.gnome.org/show_bug.cgi?id=735693
724     def test_overlay_child_position(self):
725         def get_child_position(overlay, widget, rect, user_data=None):
726             rect.x = 1
727             rect.y = 2
728             rect.width = 3
729             rect.height = 4
730             return True
731
732         overlay = Gtk.Overlay()
733         overlay.connect('get-child-position', get_child_position)
734
735         rect = Gdk.Rectangle()
736         rect.x = -1
737         rect.y = -1
738         rect.width = -1
739         rect.height = -1
740
741         overlay.emit('get-child-position', None, rect)
742         self.assertEqual(rect.x, 1)
743         self.assertEqual(rect.y, 2)
744         self.assertEqual(rect.width, 3)
745         self.assertEqual(rect.height, 4)
746
747
748 @unittest.skipUnless(Gtk, 'Gtk not available')
749 class TestBuilder(unittest.TestCase):
750     class SignalTest(GObject.GObject):
751         __gtype_name__ = "GIOverrideSignalTest"
752         __gsignals__ = {
753             "test-signal": (GObject.SignalFlags.RUN_FIRST,
754                             None,
755                             []),
756         }
757
758     def test_extract_handler_and_args_object(self):
759         class Obj():
760             pass
761
762         obj = Obj()
763         obj.foo = lambda: None
764
765         handler, args = Gtk._extract_handler_and_args(obj, 'foo')
766         self.assertEqual(handler, obj.foo)
767         self.assertEqual(len(args), 0)
768
769     def test_extract_handler_and_args_dict(self):
770         obj = {'foo': lambda: None}
771
772         handler, args = Gtk._extract_handler_and_args(obj, 'foo')
773         self.assertEqual(handler, obj['foo'])
774         self.assertEqual(len(args), 0)
775
776     def test_extract_handler_and_args_with_seq(self):
777         obj = {'foo': (lambda: None, 1, 2)}
778
779         handler, args = Gtk._extract_handler_and_args(obj, 'foo')
780         self.assertEqual(handler, obj['foo'][0])
781         self.assertSequenceEqual(args, [1, 2])
782
783     def test_extract_handler_and_args_no_handler_error(self):
784         obj = dict(foo=lambda: None)
785         self.assertRaises(AttributeError,
786                           Gtk._extract_handler_and_args,
787                           obj, 'not_a_handler')
788
789     def test_builder_with_handler_and_args(self):
790         builder = Gtk.Builder()
791         builder.add_from_string("""
792             <interface>
793               <object class="GIOverrideSignalTest" id="object_sig_test">
794                   <signal name="test-signal" handler="on_signal1" />
795                   <signal name="test-signal" handler="on_signal2" after="yes" />
796               </object>
797             </interface>
798             """)
799
800         args_collector = []
801
802         def on_signal(*args):
803             args_collector.append(args)
804
805         builder.connect_signals({'on_signal1': (on_signal, 1, 2),
806                                  'on_signal2': on_signal})
807
808         objects = builder.get_objects()
809         self.assertEqual(len(objects), 1)
810         obj, = objects
811         obj.emit('test-signal')
812
813         self.assertEqual(len(args_collector), 2)
814         self.assertSequenceEqual(args_collector[0], (obj, 1, 2))
815         self.assertSequenceEqual(args_collector[1], (obj, ))
816
817     def test_builder(self):
818         self.assertEqual(Gtk.Builder, gi.overrides.Gtk.Builder)
819
820         class SignalCheck:
821             def __init__(self):
822                 self.sentinel = 0
823                 self.after_sentinel = 0
824
825             def on_signal_1(self, *args):
826                 self.sentinel += 1
827                 self.after_sentinel += 1
828
829             def on_signal_3(self, *args):
830                 self.sentinel += 3
831
832             def on_signal_after(self, *args):
833                 if self.after_sentinel == 1:
834                     self.after_sentinel += 1
835
836         signal_checker = SignalCheck()
837         builder = Gtk.Builder()
838
839         # add object1 to the builder
840         builder.add_from_string("""
841 <interface>
842   <object class="GIOverrideSignalTest" id="object1">
843       <signal name="test-signal" after="yes" handler="on_signal_after" />
844       <signal name="test-signal" handler="on_signal_1" />
845   </object>
846 </interface>
847 """)
848
849         # only add object3 to the builder
850         builder.add_objects_from_string("""
851 <interface>
852   <object class="GIOverrideSignalTest" id="object2">
853       <signal name="test-signal" handler="on_signal_2" />
854   </object>
855   <object class="GIOverrideSignalTest" id="object3">
856       <signal name="test-signal" handler="on_signal_3" />
857   </object>
858   <object class="GIOverrideSignalTest" id="object4">
859       <signal name="test-signal" handler="on_signal_4" />
860   </object>
861 </interface>
862 """, ['object3'])
863
864         # hook up signals
865         builder.connect_signals(signal_checker)
866
867         # call their notify signals and check sentinel
868         objects = builder.get_objects()
869         self.assertEqual(len(objects), 2)
870         for obj in objects:
871             obj.emit('test-signal')
872
873         self.assertEqual(signal_checker.sentinel, 4)
874         self.assertEqual(signal_checker.after_sentinel, 2)
875
876
877 @ignore_gi_deprecation_warnings
878 @unittest.skipUnless(Gtk, 'Gtk not available')
879 class TestTreeModel(unittest.TestCase):
880     def test_tree_model_sort(self):
881         self.assertEqual(Gtk.TreeModelSort, gi.overrides.Gtk.TreeModelSort)
882         model = Gtk.TreeStore(int, bool)
883         model_sort = Gtk.TreeModelSort(model=model)
884         self.assertEqual(model_sort.get_model(), model)
885
886     def test_tree_store(self):
887         self.assertEqual(Gtk.TreeStore, gi.overrides.Gtk.TreeStore)
888         self.assertEqual(Gtk.ListStore, gi.overrides.Gtk.ListStore)
889         self.assertEqual(Gtk.TreeModel, gi.overrides.Gtk.TreeModel)
890         self.assertEqual(Gtk.TreeViewColumn, gi.overrides.Gtk.TreeViewColumn)
891
892         class TestPyObject(object):
893             pass
894
895         test_pyobj = TestPyObject()
896         test_pydict = {1: 1, "2": 2, "3": "3"}
897         test_pylist = [1, "2", "3"]
898         tree_store = Gtk.TreeStore(int,
899                                    'gchararray',
900                                    TestGtk.TestClass,
901                                    GObject.TYPE_PYOBJECT,
902                                    object,
903                                    object,
904                                    object,
905                                    bool,
906                                    bool,
907                                    GObject.TYPE_UINT,
908                                    GObject.TYPE_ULONG,
909                                    GObject.TYPE_INT64,
910                                    GObject.TYPE_UINT64,
911                                    GObject.TYPE_UCHAR,
912                                    GObject.TYPE_CHAR)
913
914         parent = None
915         for i in range(97):
916             label = 'this is child #%d' % i
917             testobj = TestGtk.TestClass(self, i, label)
918             parent = tree_store.append(parent, (i,
919                                                 label,
920                                                 testobj,
921                                                 testobj,
922                                                 test_pyobj,
923                                                 test_pydict,
924                                                 test_pylist,
925                                                 i % 2,
926                                                 bool(i % 2),
927                                                 i,
928                                                 GLib.MAXULONG,
929                                                 GLib.MININT64,
930                                                 0xffffffffffffffff,
931                                                 254,
932                                                 b'a'
933                                                 ))
934         # test set
935         parent = tree_store.append(parent)
936         i = 97
937         label = 'this is child #%d' % i
938         testobj = TestGtk.TestClass(self, i, label)
939         tree_store.set(parent,
940                        0, i,
941                        2, testobj,
942                        1, label,
943                        3, testobj,
944                        4, test_pyobj,
945                        5, test_pydict,
946                        6, test_pylist,
947                        7, i % 2,
948                        8, bool(i % 2),
949                        9, i,
950                        10, GLib.MAXULONG,
951                        11, GLib.MININT64,
952                        12, 0xffffffffffffffff,
953                        13, 254,
954                        14, b'a')
955
956         parent = tree_store.append(parent)
957         i = 98
958         label = 'this is child #%d' % i
959         testobj = TestGtk.TestClass(self, i, label)
960         tree_store.set(parent, {0: i,
961                                 2: testobj,
962                                 1: label,
963                                 3: testobj,
964                                 4: test_pyobj,
965                                 5: test_pydict,
966                                 6: test_pylist,
967                                 7: i % 2,
968                                 8: bool(i % 2),
969                                 9: i,
970                                 10: GLib.MAXULONG,
971                                 11: GLib.MININT64,
972                                 12: 0xffffffffffffffff,
973                                 13: 254,
974                                 14: b'a'})
975
976         parent = tree_store.append(parent)
977         i = 99
978         label = 'this is child #%d' % i
979         testobj = TestGtk.TestClass(self, i, label)
980         tree_store.set(parent, (0, 2, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14),
981                                (i,
982                                 testobj,
983                                 label,
984                                 testobj,
985                                 test_pyobj,
986                                 test_pydict,
987                                 test_pylist,
988                                 i % 2,
989                                 bool(i % 2),
990                                 i,
991                                 GLib.MAXULONG,
992                                 GLib.MININT64,
993                                 0xffffffffffffffff,
994                                 254,
995                                 b'a'))
996
997         # len gets the number of children in the root node
998         # since we kept appending to the previous node
999         # there should only be one child of the root
1000         self.assertEqual(len(tree_store), 1)
1001
1002         # walk the tree to see if the values were stored correctly
1003         parent = None
1004         i = 0
1005
1006         treeiter = tree_store.iter_children(parent)
1007         while treeiter:
1008             i = tree_store.get_value(treeiter, 0)
1009             s = tree_store.get_value(treeiter, 1)
1010             obj = tree_store.get_value(treeiter, 2)
1011             obj.check(i, s)
1012             obj2 = tree_store.get_value(treeiter, 3)
1013             self.assertEqual(obj, obj2)
1014
1015             pyobj = tree_store.get_value(treeiter, 4)
1016             self.assertEqual(pyobj, test_pyobj)
1017             pydict = tree_store.get_value(treeiter, 5)
1018             self.assertEqual(pydict, test_pydict)
1019             pylist = tree_store.get_value(treeiter, 6)
1020             self.assertEqual(pylist, test_pylist)
1021
1022             bool_1 = tree_store.get_value(treeiter, 7)
1023             bool_2 = tree_store.get_value(treeiter, 8)
1024             self.assertEqual(bool_1, bool_2)
1025             self.assertTrue(isinstance(bool_1, bool))
1026             self.assertTrue(isinstance(bool_2, bool))
1027
1028             uint_ = tree_store.get_value(treeiter, 9)
1029             self.assertEqual(uint_, i)
1030             ulong_ = tree_store.get_value(treeiter, 10)
1031             self.assertEqual(ulong_, GLib.MAXULONG)
1032             int64_ = tree_store.get_value(treeiter, 11)
1033             self.assertEqual(int64_, GLib.MININT64)
1034             uint64_ = tree_store.get_value(treeiter, 12)
1035             self.assertEqual(uint64_, 0xffffffffffffffff)
1036             uchar_ = tree_store.get_value(treeiter, 13)
1037             self.assertEqual(ord(uchar_), 254)
1038             char_ = tree_store.get_value(treeiter, 14)
1039             self.assertEqual(char_, 'a')
1040
1041             parent = treeiter
1042             treeiter = tree_store.iter_children(parent)
1043
1044         self.assertEqual(i, 99)
1045
1046     def test_tree_store_signals(self):
1047         tree_store = Gtk.TreeStore(int, bool)
1048
1049         def on_row_inserted(tree_store, tree_path, tree_iter, signal_list):
1050             signal_list.append('row-inserted')
1051
1052         def on_row_changed(tree_store, tree_path, tree_iter, signal_list):
1053             signal_list.append('row-changed')
1054
1055         signals = []
1056         tree_store.connect('row-inserted', on_row_inserted, signals)
1057         tree_store.connect('row-changed', on_row_changed, signals)
1058
1059         # adding rows with and without data should only call one signal
1060         tree_store.append(None, (0, False))
1061         self.assertEqual(signals, ['row-inserted'])
1062
1063         signals.pop()
1064         tree_store.append(None)
1065         self.assertEqual(signals, ['row-inserted'])
1066
1067         signals.pop()
1068         tree_store.prepend(None, (0, False))
1069         self.assertEqual(signals, ['row-inserted'])
1070
1071         signals.pop()
1072         tree_store.prepend(None)
1073         self.assertEqual(signals, ['row-inserted'])
1074
1075         signals.pop()
1076         tree_store.insert(None, 1, (0, False))
1077         self.assertEqual(signals, ['row-inserted'])
1078
1079         signals.pop()
1080         tree_store.insert(None, 1)
1081         self.assertEqual(signals, ['row-inserted'])
1082
1083     def test_list_store(self):
1084         class TestPyObject(object):
1085             pass
1086
1087         test_pyobj = TestPyObject()
1088         test_pydict = {1: 1, "2": 2, "3": "3"}
1089         test_pylist = [1, "2", "3"]
1090
1091         list_store = Gtk.ListStore(int, str, 'GIOverrideTreeAPITest', object, object, object, bool, bool)
1092         for i in range(1, 93):
1093             label = 'this is row #%d' % i
1094             testobj = TestGtk.TestClass(self, i, label)
1095             list_store.append((i,
1096                                label,
1097                                testobj,
1098                                test_pyobj,
1099                                test_pydict,
1100                                test_pylist,
1101                                i % 2,
1102                                bool(i % 2)))
1103
1104         i = 93
1105         label = u'this is row #93'
1106         treeiter = list_store.append()
1107         list_store.set_value(treeiter, 0, i)
1108         list_store.set_value(treeiter, 1, label)
1109         list_store.set_value(treeiter, 2, TestGtk.TestClass(self, i, label))
1110         list_store.set_value(treeiter, 3, test_pyobj)
1111         list_store.set_value(treeiter, 4, test_pydict)
1112         list_store.set_value(treeiter, 5, test_pylist)
1113         list_store.set_value(treeiter, 6, 1)
1114         list_store.set_value(treeiter, 7, True)
1115
1116         # test prepend
1117         label = 'this is row #0'
1118         list_store.prepend((0,
1119                             label,
1120                             TestGtk.TestClass(self, 0, label),
1121                             test_pyobj,
1122                             test_pydict,
1123                             test_pylist,
1124                             0,
1125                             False))
1126
1127         # test automatic unicode->str conversion
1128         i = 94
1129         label = u'this is row #94'
1130         treeiter = list_store.append((i,
1131                                       label,
1132                                       TestGtk.TestClass(self, i, label),
1133                                       test_pyobj,
1134                                       test_pydict,
1135                                       test_pylist,
1136                                       0,
1137                                       False))
1138
1139         # add sorted items out of order to test insert* apis
1140         # also test sending in None to not set a column
1141         i = 97
1142         label = 'this is row #97'
1143         treeiter = list_store.append((None,
1144                                       None,
1145                                       None,
1146                                       test_pyobj,
1147                                       None,
1148                                       test_pylist,
1149                                       1,
1150                                       None))
1151
1152         list_store.set_value(treeiter, 0, i)
1153         list_store.set_value(treeiter, 1, label)
1154         list_store.set_value(treeiter, 2, TestGtk.TestClass(self, i, label))
1155         list_store.set_value(treeiter, 4, test_pydict)
1156         list_store.set_value(treeiter, 7, True)
1157
1158         # this should append
1159         i = 99
1160         label = 'this is row #99'
1161         list_store.insert(9999, (i,
1162                                  label,
1163                                  TestGtk.TestClass(self, i, label),
1164                                  test_pyobj,
1165                                  test_pydict,
1166                                  test_pylist,
1167                                  1,
1168                                  True))
1169
1170         i = 96
1171         label = 'this is row #96'
1172         list_store.insert_before(treeiter, (i,
1173                                             label,
1174                                             TestGtk.TestClass(self, i, label),
1175                                             test_pyobj,
1176                                             test_pydict,
1177                                             test_pylist,
1178                                             0,
1179                                             False))
1180
1181         i = 98
1182         label = 'this is row #98'
1183         list_store.insert_after(treeiter, (i,
1184                                            label,
1185                                            TestGtk.TestClass(self, i, label),
1186                                            test_pyobj,
1187                                            test_pydict,
1188                                            test_pylist,
1189                                            0,
1190                                            False))
1191
1192         i = 95
1193         label = 'this is row #95'
1194         list_store.insert(95, (i,
1195                                label,
1196                                TestGtk.TestClass(self, i, label),
1197                                test_pyobj,
1198                                test_pydict,
1199                                test_pylist,
1200                                1,
1201                                True))
1202
1203         i = 100
1204         label = 'this is row #100'
1205         treeiter = list_store.append()
1206         list_store.set(treeiter,
1207                        1, label,
1208                        0, i,
1209                        2, TestGtk.TestClass(self, i, label),
1210                        3, test_pyobj,
1211                        4, test_pydict,
1212                        5, test_pylist,
1213                        6, 0,
1214                        7, False)
1215         i = 101
1216         label = 'this is row #101'
1217         treeiter = list_store.append()
1218         list_store.set(treeiter, {1: label,
1219                                   0: i,
1220                                   2: TestGtk.TestClass(self, i, label),
1221                                   3: test_pyobj,
1222                                   4: test_pydict,
1223                                   5: test_pylist,
1224                                   6: 1,
1225                                   7: True})
1226         i = 102
1227         label = 'this is row #102'
1228         treeiter = list_store.append()
1229         list_store.set(treeiter, (1, 0, 2, 3, 4, 5, 6, 7),
1230                                  (label,
1231                                   i,
1232                                   TestGtk.TestClass(self, i, label),
1233                                   test_pyobj,
1234                                   test_pydict,
1235                                   test_pylist,
1236                                   0,
1237                                   False))
1238
1239         self.assertEqual(len(list_store), 103)
1240
1241         # walk the list to see if the values were stored correctly
1242         i = 0
1243         treeiter = list_store.get_iter_first()
1244
1245         counter = 0
1246         while treeiter:
1247             i = list_store.get_value(treeiter, 0)
1248             self.assertEqual(i, counter)
1249             s = list_store.get_value(treeiter, 1)
1250             obj = list_store.get_value(treeiter, 2)
1251             obj.check(i, s)
1252
1253             pyobj = list_store.get_value(treeiter, 3)
1254             self.assertEqual(pyobj, test_pyobj)
1255             pydict = list_store.get_value(treeiter, 4)
1256             self.assertEqual(pydict, test_pydict)
1257             pylist = list_store.get_value(treeiter, 5)
1258             self.assertEqual(pylist, test_pylist)
1259
1260             bool_1 = list_store.get_value(treeiter, 6)
1261             bool_2 = list_store.get_value(treeiter, 7)
1262             self.assertEqual(bool_1, bool_2)
1263             self.assertTrue(isinstance(bool_1, bool))
1264             self.assertTrue(isinstance(bool_2, bool))
1265
1266             treeiter = list_store.iter_next(treeiter)
1267
1268             counter += 1
1269
1270         self.assertEqual(i, 102)
1271
1272     def test_list_store_sort(self):
1273         def comp1(model, row1, row2, user_data):
1274             v1 = model[row1][1]
1275             v2 = model[row2][1]
1276
1277             # make "m" smaller than anything else
1278             if v1.startswith('m') and not v2.startswith('m'):
1279                 return -1
1280             if v2.startswith('m') and not v1.startswith('m'):
1281                 return 1
1282             return (v1 > v2) - (v1 < v2)
1283
1284         list_store = Gtk.ListStore(int, str)
1285         list_store.set_sort_func(2, comp1, None)
1286         list_store.append((1, 'apples'))
1287         list_store.append((3, 'oranges'))
1288         list_store.append((2, 'mango'))
1289
1290         # not sorted yet, should be original order
1291         self.assertEqual([list(i) for i in list_store],
1292                          [[1, 'apples'], [3, 'oranges'], [2, 'mango']])
1293
1294         # sort with our custom function
1295         list_store.set_sort_column_id(2, Gtk.SortType.ASCENDING)
1296         self.assertEqual([list(i) for i in list_store],
1297                          [[2, 'mango'], [1, 'apples'], [3, 'oranges']])
1298
1299         list_store.set_sort_column_id(2, Gtk.SortType.DESCENDING)
1300         self.assertEqual([list(i) for i in list_store],
1301                          [[3, 'oranges'], [1, 'apples'], [2, 'mango']])
1302
1303     def test_list_store_signals(self):
1304         list_store = Gtk.ListStore(int, bool)
1305
1306         def on_row_inserted(list_store, tree_path, tree_iter, signal_list):
1307             signal_list.append('row-inserted')
1308
1309         def on_row_changed(list_store, tree_path, tree_iter, signal_list):
1310             signal_list.append('row-changed')
1311
1312         signals = []
1313         list_store.connect('row-inserted', on_row_inserted, signals)
1314         list_store.connect('row-changed', on_row_changed, signals)
1315
1316         # adding rows with and without data should only call one signal
1317         list_store.append((0, False))
1318         self.assertEqual(signals, ['row-inserted'])
1319
1320         signals.pop()
1321         list_store.append()
1322         self.assertEqual(signals, ['row-inserted'])
1323
1324         signals.pop()
1325         list_store.prepend((0, False))
1326         self.assertEqual(signals, ['row-inserted'])
1327
1328         signals.pop()
1329         list_store.prepend()
1330         self.assertEqual(signals, ['row-inserted'])
1331
1332         signals.pop()
1333         list_store.insert(1, (0, False))
1334         self.assertEqual(signals, ['row-inserted'])
1335
1336         signals.pop()
1337         list_store.insert(1)
1338         self.assertEqual(signals, ['row-inserted'])
1339
1340     def test_tree_path(self):
1341         p1 = Gtk.TreePath()
1342         p2 = Gtk.TreePath.new_first()
1343         self.assertEqual(p1, p2)
1344         self.assertEqual(str(p1), '0')
1345         self.assertEqual(len(p1), 1)
1346         p1 = Gtk.TreePath(2)
1347         p2 = Gtk.TreePath.new_from_string('2')
1348         self.assertEqual(p1, p2)
1349         self.assertEqual(str(p1), '2')
1350         self.assertEqual(len(p1), 1)
1351         p1 = Gtk.TreePath('1:2:3')
1352         p2 = Gtk.TreePath.new_from_string('1:2:3')
1353         self.assertEqual(p1, p2)
1354         self.assertEqual(str(p1), '1:2:3')
1355         self.assertEqual(len(p1), 3)
1356         p1 = Gtk.TreePath((1, 2, 3))
1357         p2 = Gtk.TreePath.new_from_string('1:2:3')
1358         self.assertEqual(p1, p2)
1359         self.assertEqual(str(p1), '1:2:3')
1360         self.assertEqual(len(p1), 3)
1361         self.assertNotEqual(p1, None)
1362         self.assertTrue(p1 > None)
1363         self.assertTrue(p1 >= None)
1364         self.assertFalse(p1 < None)
1365         self.assertFalse(p1 <= None)
1366
1367         self.assertEqual(tuple(p1), (1, 2, 3))
1368         self.assertEqual(p1[0], 1)
1369         self.assertEqual(p1[1], 2)
1370         self.assertEqual(p1[2], 3)
1371         self.assertRaises(IndexError, p1.__getitem__, 3)
1372
1373     def test_tree_model(self):
1374         tree_store = Gtk.TreeStore(int, str)
1375
1376         self.assertTrue(tree_store)
1377         self.assertEqual(len(tree_store), 0)
1378         self.assertEqual(tree_store.get_iter_first(), None)
1379
1380         def get_by_index(row, col=None):
1381             if col:
1382                 return tree_store[row][col]
1383             else:
1384                 return tree_store[row]
1385
1386         self.assertRaises(TypeError, get_by_index, None)
1387         self.assertRaises(TypeError, get_by_index, "")
1388         self.assertRaises(TypeError, get_by_index, ())
1389
1390         self.assertRaises(IndexError, get_by_index, "0")
1391         self.assertRaises(IndexError, get_by_index, 0)
1392         self.assertRaises(IndexError, get_by_index, (0,))
1393
1394         self.assertRaises(ValueError, tree_store.get_iter, "0")
1395         self.assertRaises(ValueError, tree_store.get_iter, 0)
1396         self.assertRaises(ValueError, tree_store.get_iter, (0,))
1397
1398         self.assertRaises(ValueError, tree_store.get_iter_from_string, "0")
1399
1400         for row in tree_store:
1401             self.fail("Should not be reached")
1402
1403         class DerivedIntType(int):
1404             pass
1405
1406         class DerivedStrType(str):
1407             pass
1408
1409         for i in range(100):
1410             label = 'this is row #%d' % i
1411             parent = tree_store.append(None, (DerivedIntType(i), DerivedStrType(label),))
1412             self.assertNotEqual(parent, None)
1413             for j in range(20):
1414                 label = 'this is child #%d of node #%d' % (j, i)
1415                 child = tree_store.append(parent, (j, label,))
1416                 self.assertNotEqual(child, None)
1417
1418         self.assertTrue(tree_store)
1419         self.assertEqual(len(tree_store), 100)
1420
1421         self.assertEqual(tree_store.iter_previous(tree_store.get_iter(0)), None)
1422
1423         for i, row in enumerate(tree_store):
1424             self.assertEqual(row.model, tree_store)
1425             self.assertEqual(row.parent, None)
1426
1427             self.assertEqual(tree_store[i].path, row.path)
1428             self.assertEqual(tree_store[str(i)].path, row.path)
1429             self.assertEqual(tree_store[(i,)].path, row.path)
1430
1431             self.assertEqual(tree_store[i][0], i)
1432             self.assertEqual(tree_store[i][1], "this is row #%d" % i)
1433
1434             aiter = tree_store.get_iter(i)
1435             self.assertEqual(tree_store.get_path(aiter), row.path)
1436
1437             aiter = tree_store.get_iter(str(i))
1438             self.assertEqual(tree_store.get_path(aiter), row.path)
1439
1440             aiter = tree_store.get_iter((i,))
1441             self.assertEqual(tree_store.get_path(aiter), row.path)
1442
1443             self.assertEqual(tree_store.iter_parent(aiter), row.parent)
1444
1445             next = tree_store.iter_next(aiter)
1446             if i < len(tree_store) - 1:
1447                 self.assertEqual(tree_store.get_path(next), row.next.path)
1448                 self.assertEqual(tree_store.get_path(tree_store.iter_previous(next)),
1449                                  tree_store.get_path(aiter))
1450             else:
1451                 self.assertEqual(next, None)
1452
1453             self.assertEqual(tree_store.iter_n_children(row.iter), 20)
1454
1455             child = tree_store.iter_children(row.iter)
1456             for j, childrow in enumerate(row.iterchildren()):
1457                 child_path = tree_store.get_path(child)
1458                 self.assertEqual(childrow.path, child_path)
1459                 self.assertEqual(childrow.parent.path, row.path)
1460                 self.assertEqual(childrow.path, tree_store[child].path)
1461                 self.assertEqual(childrow.path, tree_store[child_path].path)
1462
1463                 self.assertEqual(childrow[0], tree_store[child][0])
1464                 self.assertEqual(childrow[0], j)
1465                 self.assertEqual(childrow[1], tree_store[child][1])
1466                 self.assertEqual(childrow[1], 'this is child #%d of node #%d' % (j, i))
1467
1468                 self.assertRaises(IndexError, get_by_index, child, 2)
1469
1470                 tree_store[child][1] = 'this was child #%d of node #%d' % (j, i)
1471                 self.assertEqual(childrow[1], 'this was child #%d of node #%d' % (j, i))
1472
1473                 nth_child = tree_store.iter_nth_child(row.iter, j)
1474                 self.assertEqual(childrow.path, tree_store.get_path(nth_child))
1475
1476                 childrow2 = tree_store["%d:%d" % (i, j)]
1477                 self.assertEqual(childrow.path, childrow2.path)
1478
1479                 childrow2 = tree_store[(i, j,)]
1480                 self.assertEqual(childrow.path, childrow2.path)
1481
1482                 child = tree_store.iter_next(child)
1483                 if j < 19:
1484                     self.assertEqual(childrow.next.path, tree_store.get_path(child))
1485                 else:
1486                     self.assertEqual(child, childrow.next)
1487                     self.assertEqual(child, None)
1488
1489             self.assertEqual(j, 19)
1490
1491         self.assertEqual(i, 99)
1492
1493         # negative indices
1494         for i in range(-1, -100, -1):
1495             i_real = i + 100
1496             self.assertEqual(tree_store[i][0], i_real)
1497
1498             row = tree_store[i]
1499             for j in range(-1, -20, -1):
1500                 j_real = j + 20
1501                 path = (i_real, j_real,)
1502
1503                 self.assertEqual(tree_store[path][-2], j_real)
1504
1505                 label = 'this was child #%d of node #%d' % (j_real, i_real)
1506                 self.assertEqual(tree_store[path][-1], label)
1507
1508                 new_label = 'this still is child #%d of node #%d' % (j_real, i_real)
1509                 tree_store[path][-1] = new_label
1510                 self.assertEqual(tree_store[path][-1], new_label)
1511
1512                 self.assertRaises(IndexError, get_by_index, path, -3)
1513
1514         self.assertRaises(IndexError, get_by_index, -101)
1515
1516         last_row = tree_store[99]
1517         self.assertNotEqual(last_row, None)
1518
1519         for i, childrow in enumerate(last_row.iterchildren()):
1520             if i < 19:
1521                 self.assertTrue(tree_store.remove(childrow.iter))
1522             else:
1523                 self.assertFalse(tree_store.remove(childrow.iter))
1524
1525         self.assertEqual(i, 19)
1526
1527         self.assertEqual(tree_store.iter_n_children(last_row.iter), 0)
1528         for childrow in last_row.iterchildren():
1529             self.fail("Should not be reached")
1530
1531         aiter = tree_store.get_iter(10)
1532         self.assertRaises(TypeError, tree_store.get, aiter, 1, 'a')
1533         self.assertRaises(ValueError, tree_store.get, aiter, 1, -1)
1534         self.assertRaises(ValueError, tree_store.get, aiter, 1, 100)
1535         self.assertEqual(tree_store.get(aiter, 0, 1), (10, 'this is row #10'))
1536
1537         # check __delitem__
1538         self.assertEqual(len(tree_store), 100)
1539         aiter = tree_store.get_iter(10)
1540         del tree_store[aiter]
1541         self.assertEqual(len(tree_store), 99)
1542         self.assertRaises(TypeError, tree_store.__delitem__, None)
1543         self.assertRaises(IndexError, tree_store.__delitem__, -101)
1544         self.assertRaises(IndexError, tree_store.__delitem__, 101)
1545
1546     def test_tree_model_get_iter_fail(self):
1547         # TreeModel class with a failing get_iter()
1548         class MyTreeModel(GObject.GObject, Gtk.TreeModel):
1549             def do_get_iter(self, iter):
1550                 return (False, None)
1551
1552         tm = MyTreeModel()
1553         self.assertEqual(tm.get_iter_first(), None)
1554
1555     def test_tree_model_edit(self):
1556         model = Gtk.ListStore(int, str, float)
1557         model.append([1, "one", -0.1])
1558         model.append([2, "two", -0.2])
1559
1560         def set_row(value):
1561             model[1] = value
1562
1563         self.assertRaises(TypeError, set_row, 3)
1564         self.assertRaises(TypeError, set_row, "three")
1565         self.assertRaises(ValueError, set_row, [])
1566         self.assertRaises(ValueError, set_row, [3, "three"])
1567
1568         model[0] = (3, "three", -0.3)
1569
1570     def test_tree_row_slice(self):
1571         model = Gtk.ListStore(int, str, float)
1572         model.append([1, "one", -0.1])
1573
1574         self.assertEqual([1, "one", -0.1], model[0][:])
1575         self.assertEqual([1, "one"], model[0][:2])
1576         self.assertEqual(["one", -0.1], model[0][1:])
1577         self.assertEqual(["one"], model[0][1:-1])
1578         self.assertEqual([1], model[0][:-2])
1579         self.assertEqual([], model[0][5:])
1580         self.assertEqual([1, -0.1], model[0][0:3:2])
1581
1582         model[0][:] = (2, "two", -0.2)
1583         self.assertEqual([2, "two", -0.2], model[0][:])
1584
1585         model[0][:2] = (3, "three")
1586         self.assertEqual([3, "three", -0.2], model[0][:])
1587
1588         model[0][1:] = ("four", -0.4)
1589         self.assertEqual([3, "four", -0.4], model[0][:])
1590
1591         model[0][1:-1] = ("five",)
1592         self.assertEqual([3, "five", -0.4], model[0][:])
1593
1594         model[0][0:3:2] = (6, -0.6)
1595         self.assertEqual([6, "five", -0.6], model[0][:])
1596
1597         def set_row1():
1598             model[0][5:] = ("doesn't", "matter",)
1599
1600         self.assertRaises(ValueError, set_row1)
1601
1602         def set_row2():
1603             model[0][:1] = (0, "zero", 0)
1604
1605         self.assertRaises(ValueError, set_row2)
1606
1607         def set_row3():
1608             model[0][:2] = ("0", 0)
1609
1610         self.assertRaises(TypeError, set_row3)
1611
1612     def test_tree_row_sequence(self):
1613         model = Gtk.ListStore(int, str, float)
1614         model.append([1, "one", -0.1])
1615
1616         self.assertEqual([1, "one", -0.1], model[0][0, 1, 2])
1617         self.assertEqual([1, "one"], model[0][0, 1])
1618         self.assertEqual(["one", -0.1], model[0][1, 2])
1619         self.assertEqual("one", model[0][1])
1620         self.assertEqual([1, -0.1], model[0][0, 2])
1621         self.assertEqual([-0.1, 1], model[0][2, 0])
1622
1623         model[0][0, 1, 2] = (2, "two", -0.2)
1624         self.assertEqual([2, "two", -0.2], model[0][0, 1, 2])
1625
1626         model[0][0, 1] = (3, "three")
1627         self.assertEqual([3, "three"], model[0][0, 1])
1628
1629         model[0][1, 2] = ("four", -0.4)
1630         self.assertEqual(["four", -0.4], model[0][1, 2])
1631
1632         model[0][0, 2] = (5, -0.5)
1633         self.assertEqual([5, -0.5], model[0][0, 2])
1634
1635         model[0][0, 1, 2] = (6, "six", -0.6)
1636         self.assertEqual([-0.6, 6, "six"], model[0][2, 0, 1])
1637
1638         def set_row1():
1639             model[0][4, 5] = ("shouldn't", "work",)
1640
1641         self.assertRaises(IndexError, set_row1)
1642
1643         def set_row2():
1644             model[0][0, 1] = (0, "zero", 0)
1645
1646         self.assertRaises(ValueError, set_row2)
1647
1648         def set_row3():
1649             model[0][0, 1] = ("shouldn't", 0)
1650
1651         self.assertRaises(TypeError, set_row3)
1652
1653         def set_row4():
1654             model[0][0, "two"] = (0, "zero")
1655
1656         self.assertRaises(TypeError, set_row4)
1657
1658     def test_tree_model_set_value_to_none(self):
1659         # Tests allowing the usage of None to set an empty value on a model.
1660         store = Gtk.ListStore(str)
1661         row = store.append(['test'])
1662         self.assertSequenceEqual(store[0][:], ['test'])
1663         store.set_value(row, 0, None)
1664         self.assertSequenceEqual(store[0][:], [None])
1665
1666     def test_signal_emission_tree_path_coerce(self):
1667         class Model(GObject.Object, Gtk.TreeModel):
1668             pass
1669
1670         model = Model()
1671         tree_paths = []
1672
1673         def on_any_signal(model, path, *args):
1674             tree_paths.append(path.to_string())
1675
1676         model.connect('row-changed', on_any_signal)
1677         model.connect('row-deleted', on_any_signal)
1678         model.connect('row-has-child-toggled', on_any_signal)
1679         model.connect('row-inserted', on_any_signal)
1680
1681         model.row_changed('0', Gtk.TreeIter())
1682         self.assertEqual(tree_paths[-1], '0')
1683
1684         model.row_deleted('1')
1685         self.assertEqual(tree_paths[-1], '1')
1686
1687         model.row_has_child_toggled('2', Gtk.TreeIter())
1688         self.assertEqual(tree_paths[-1], '2')
1689
1690         model.row_inserted('3', Gtk.TreeIter())
1691         self.assertEqual(tree_paths[-1], '3')
1692
1693     def test_tree_model_filter(self):
1694         model = Gtk.ListStore(int, str, float)
1695         model.append([1, "one", -0.1])
1696         model.append([2, "two", -0.2])
1697
1698         filtered = Gtk.TreeModelFilter(child_model=model)
1699
1700         self.assertEqual(filtered[0][1], 'one')
1701         filtered[0][1] = 'ONE'
1702         self.assertEqual(filtered[0][1], 'ONE')
1703
1704     def test_list_store_performance(self):
1705         model = Gtk.ListStore(int, str)
1706
1707         iterations = 2000
1708         start = time.clock()
1709         i = iterations
1710         while i > 0:
1711             model.append([1, 'hello'])
1712             i -= 1
1713         end = time.clock()
1714         sys.stderr.write('[%.0f Âµs/append] ' % ((end - start) * 1000000 / iterations))
1715
1716     def test_filter_new_default(self):
1717         # Test filter_new accepts implicit default of None
1718         model = Gtk.ListStore(int)
1719         filt = model.filter_new()
1720         self.assertTrue(filt is not None)
1721
1722
1723 @unittest.skipIf(sys.platform == "darwin", "hangs")
1724 @unittest.skipUnless(Gtk, 'Gtk not available')
1725 class TestTreeView(unittest.TestCase):
1726     def test_tree_view(self):
1727         store = Gtk.ListStore(int, str)
1728         store.append((0, "foo"))
1729         store.append((1, "bar"))
1730         view = Gtk.TreeView()
1731
1732         with realized(view):
1733             view.set_cursor(store[1].path)
1734             view.set_cursor(str(store[1].path))
1735
1736             view.get_cell_area(store[1].path)
1737             view.get_cell_area(str(store[1].path))
1738
1739     def test_tree_view_column(self):
1740         cell = Gtk.CellRendererText()
1741         col = Gtk.TreeViewColumn(title='This is just a test',
1742                                  cell_renderer=cell,
1743                                  text=0,
1744                                  style=2)
1745
1746         # Regression test for: https://bugzilla.gnome.org/show_bug.cgi?id=711173
1747         col.set_cell_data_func(cell, None, None)
1748
1749     def test_tree_view_add_column_with_attributes(self):
1750         model = Gtk.ListStore(str, str, str)
1751         # deliberately use out-of-order sorting here; we assign column 0 to
1752         # model index 2, etc.
1753         model.append(['cell13', 'cell11', 'cell12'])
1754         model.append(['cell23', 'cell21', 'cell22'])
1755
1756         tree = Gtk.TreeView(model=model)
1757         cell1 = Gtk.CellRendererText()
1758         cell2 = Gtk.CellRendererText()
1759         cell3 = Gtk.CellRendererText()
1760         cell4 = Gtk.CellRendererText()
1761
1762         tree.insert_column_with_attributes(0, 'Head2', cell2, text=2)
1763         tree.insert_column_with_attributes(0, 'Head1', cell1, text=1)
1764         tree.insert_column_with_attributes(-1, 'Head3', cell3, text=0)
1765         # unconnected
1766         tree.insert_column_with_attributes(-1, 'Head4', cell4)
1767
1768         with realized(tree):
1769             tree.set_cursor(model[0].path)
1770             while Gtk.events_pending():
1771                 Gtk.main_iteration()
1772
1773             self.assertEqual(tree.get_column(0).get_title(), 'Head1')
1774             self.assertEqual(tree.get_column(1).get_title(), 'Head2')
1775             self.assertEqual(tree.get_column(2).get_title(), 'Head3')
1776             self.assertEqual(tree.get_column(3).get_title(), 'Head4')
1777
1778             # cursor should be at the first row
1779             self.assertEqual(cell1.props.text, 'cell11')
1780             self.assertEqual(cell2.props.text, 'cell12')
1781             self.assertEqual(cell3.props.text, 'cell13')
1782             self.assertEqual(cell4.props.text, None)
1783
1784     def test_tree_view_column_set_attributes(self):
1785         store = Gtk.ListStore(int, str)
1786         directors = ['Fellini', 'Tarantino', 'Tarkovskiy']
1787         for i, director in enumerate(directors):
1788             store.append([i, director])
1789
1790         treeview = Gtk.TreeView()
1791         treeview.set_model(store)
1792
1793         column = Gtk.TreeViewColumn()
1794         treeview.append_column(column)
1795
1796         cell = Gtk.CellRendererText()
1797         column.pack_start(cell, expand=True)
1798         column.set_attributes(cell, text=1)
1799
1800         with realized(treeview):
1801             self.assertTrue(cell.props.text in directors)
1802
1803     def test_tree_selection(self):
1804         store = Gtk.ListStore(int, str)
1805         for i in range(10):
1806             store.append((i, "foo"))
1807         view = Gtk.TreeView()
1808         view.set_model(store)
1809         firstpath = store.get_path(store.get_iter_first())
1810         sel = view.get_selection()
1811
1812         sel.select_path(firstpath)
1813         (m, s) = sel.get_selected()
1814         self.assertEqual(m, store)
1815         self.assertEqual(store.get_path(s), firstpath)
1816
1817         sel.select_path(0)
1818         (m, s) = sel.get_selected()
1819         self.assertEqual(m, store)
1820         self.assertEqual(store.get_path(s), firstpath)
1821
1822         sel.select_path("0:0")
1823         (m, s) = sel.get_selected()
1824         self.assertEqual(m, store)
1825         self.assertEqual(store.get_path(s), firstpath)
1826
1827         sel.select_path((0, 0))
1828         (m, s) = sel.get_selected()
1829         self.assertEqual(m, store)
1830         self.assertEqual(store.get_path(s), firstpath)
1831
1832
1833 @unittest.skipUnless(Gtk, 'Gtk not available')
1834 class TestTextBuffer(unittest.TestCase):
1835     def test_text_buffer(self):
1836         self.assertEqual(Gtk.TextBuffer, gi.overrides.Gtk.TextBuffer)
1837         buffer = Gtk.TextBuffer()
1838         tag = buffer.create_tag('title', font='Sans 18')
1839
1840         self.assertEqual(tag.props.name, 'title')
1841         self.assertEqual(tag.props.font, 'Sans 18')
1842
1843         (start, end) = buffer.get_bounds()
1844
1845         mark = buffer.create_mark(None, start)
1846         self.assertFalse(mark.get_left_gravity())
1847
1848         buffer.set_text('Hello Jane Hello Bob')
1849         (start, end) = buffer.get_bounds()
1850         text = buffer.get_text(start, end, False)
1851         self.assertEqual(text, 'Hello Jane Hello Bob')
1852
1853         buffer.set_text('')
1854         (start, end) = buffer.get_bounds()
1855         text = buffer.get_text(start, end, False)
1856         self.assertEqual(text, '')
1857
1858         buffer.insert(end, 'HelloHello')
1859         buffer.insert(end, ' Bob')
1860
1861         cursor_iter = end.copy()
1862         cursor_iter.backward_chars(9)
1863         buffer.place_cursor(cursor_iter)
1864         buffer.insert_at_cursor(' Jane ')
1865
1866         (start, end) = buffer.get_bounds()
1867         text = buffer.get_text(start, end, False)
1868         self.assertEqual(text, 'Hello Jane Hello Bob')
1869
1870         sel = buffer.get_selection_bounds()
1871         self.assertEqual(sel, ())
1872         buffer.select_range(start, end)
1873         sel = buffer.get_selection_bounds()
1874         self.assertTrue(sel[0].equal(start))
1875         self.assertTrue(sel[1].equal(end))
1876
1877         buffer.set_text('')
1878         buffer.insert_with_tags(buffer.get_start_iter(), 'HelloHello')
1879         start, end = buffer.get_bounds()
1880         text = buffer.get_text(start, end, False)
1881         self.assertEqual(text, 'HelloHello')
1882
1883         buffer.set_text('')
1884         buffer.insert_with_tags_by_name(buffer.get_start_iter(), 'HelloHello')
1885         start, end = buffer.get_bounds()
1886         text = buffer.get_text(start, end, False)
1887         self.assertEqual(text, 'HelloHello')
1888
1889         try:
1890             starts_tag = Gtk.TextIter.starts_tag
1891         except AttributeError:
1892             starts_tag = Gtk.TextIter.begins_tag
1893
1894         buffer.set_text('')
1895         buffer.insert_with_tags(buffer.get_start_iter(), 'HelloHello', tag)
1896         (start, end) = buffer.get_bounds()
1897         self.assertTrue(starts_tag(start, tag))
1898         self.assertTrue(start.has_tag(tag))
1899
1900         buffer.set_text('')
1901         buffer.insert_with_tags_by_name(buffer.get_start_iter(), 'HelloHello', 'title')
1902         (start, end) = buffer.get_bounds()
1903         self.assertTrue(starts_tag(start, tag))
1904         self.assertTrue(start.has_tag(tag))
1905
1906         self.assertRaises(ValueError, buffer.insert_with_tags_by_name,
1907                           buffer.get_start_iter(), 'HelloHello', 'unknowntag')
1908
1909     def test_text_iter(self):
1910         try:
1911             starts_tag = Gtk.TextIter.starts_tag
1912         except AttributeError:
1913             starts_tag = Gtk.TextIter.begins_tag
1914
1915         self.assertEqual(Gtk.TextIter, gi.overrides.Gtk.TextIter)
1916         buffer = Gtk.TextBuffer()
1917         buffer.set_text('Hello Jane Hello Bob')
1918         tag = buffer.create_tag('title', font='Sans 18')
1919         (start, end) = buffer.get_bounds()
1920         start.forward_chars(10)
1921         buffer.apply_tag(tag, start, end)
1922         self.assertTrue(starts_tag(start))
1923         self.assertTrue(end.ends_tag())
1924         self.assertTrue(start.toggles_tag())
1925         self.assertTrue(end.toggles_tag())
1926         start.backward_chars(1)
1927         self.assertFalse(starts_tag(start))
1928         self.assertFalse(start.ends_tag())
1929         self.assertFalse(start.toggles_tag())
1930
1931     def test_text_buffer_search(self):
1932         buffer = Gtk.TextBuffer()
1933         buffer.set_text('Hello World Hello GNOME')
1934
1935         i = buffer.get_iter_at_offset(0)
1936         self.assertTrue(isinstance(i, Gtk.TextIter))
1937
1938         self.assertEqual(i.forward_search('world', 0, None), None)
1939
1940         (start, end) = i.forward_search('World', 0, None)
1941         self.assertEqual(start.get_offset(), 6)
1942         self.assertEqual(end.get_offset(), 11)
1943
1944         (start, end) = i.forward_search('world',
1945                                         Gtk.TextSearchFlags.CASE_INSENSITIVE,
1946                                         None)
1947         self.assertEqual(start.get_offset(), 6)
1948         self.assertEqual(end.get_offset(), 11)
1949
1950     def test_insert_text_signal_location_modification(self):
1951         # Regression test for: https://bugzilla.gnome.org/show_bug.cgi?id=736175
1952
1953         def callback(buffer, location, text, length):
1954             location.assign(buffer.get_end_iter())
1955
1956         buffer = Gtk.TextBuffer()
1957         buffer.set_text('first line\n')
1958         buffer.connect('insert-text', callback)
1959
1960         # attempt insertion at the beginning of the buffer, the callback will
1961         # modify the insert location to the end.
1962         buffer.place_cursor(buffer.get_start_iter())
1963         buffer.insert_at_cursor('second line\n')
1964
1965         self.assertEqual(buffer.get_property('text'),
1966                          'first line\nsecond line\n')
1967
1968     def test_backward_find_char(self):
1969         buffer = Gtk.TextBuffer()
1970         buffer.set_text('abc')
1971         end = buffer.get_iter_at_line(99)
1972
1973         values = []
1974
1975         def pred_func(ch, user_data):
1976             values.append(ch)
1977             return ch == u"a"
1978
1979         self.assertTrue(end.backward_find_char(pred_func))
1980         self.assertEqual(values, [u"c", u"b", u"a"])
1981
1982
1983 @unittest.skipUnless(Gtk, 'Gtk not available')
1984 class TestContainer(unittest.TestCase):
1985
1986     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
1987     def test_child_set_property(self):
1988         box = Gtk.Box()
1989         child = Gtk.Button()
1990         box.pack_start(child, expand=False, fill=True, padding=0)
1991
1992         box.child_set_property(child, 'padding', 42)
1993
1994         value = GObject.Value(int)
1995         box.child_get_property(child, 'padding', value)
1996         self.assertEqual(value.get_int(), 42)
1997
1998     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
1999     def test_child_get_property_gvalue(self):
2000         box = Gtk.Box()
2001         child = Gtk.Button()
2002         box.pack_start(child, expand=False, fill=True, padding=42)
2003
2004         value = GObject.Value(int)
2005         box.child_get_property(child, 'padding', value)
2006         self.assertEqual(value.get_int(), 42)
2007
2008     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
2009     def test_child_get_property_return_with_explicit_gvalue(self):
2010         box = Gtk.Box()
2011         child = Gtk.Button()
2012         box.pack_start(child, expand=False, fill=True, padding=42)
2013
2014         value = GObject.Value(int)
2015         result = box.child_get_property(child, 'padding', value)
2016         self.assertEqual(result, 42)
2017
2018     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
2019     def test_child_get_property_return_with_implicit_gvalue(self):
2020         box = Gtk.Box()
2021         child = Gtk.Button()
2022         box.pack_start(child, expand=False, fill=True, padding=42)
2023
2024         result = box.child_get_property(child, 'padding')
2025         self.assertEqual(result, 42)
2026
2027     def test_child_get_property_error(self):
2028         box = Gtk.Box()
2029         child = Gtk.Button()
2030         if Gtk._version == "4.0":
2031             box.pack_start(child, expand=False, fill=True)
2032         else:
2033             box.pack_start(child, expand=False, fill=True, padding=42)
2034         with self.assertRaises(ValueError):
2035             box.child_get_property(child, 'not-a-valid-child-property')
2036
2037     @unittest.skipIf(Gtk._version == "4.0", "not in gtk4")
2038     def test_child_get_and_set(self):
2039         box = Gtk.Box()
2040         child = Gtk.Button()
2041         box.pack_start(child, expand=True, fill=True, padding=42)
2042
2043         expand, fill, padding = box.child_get(child, 'expand', 'fill', 'padding')
2044         self.assertEqual(expand, True)
2045         self.assertEqual(fill, True)
2046         self.assertEqual(padding, 42)
2047
2048         box.child_set(child, expand=False, fill=False, padding=21, pack_type=1)
2049         expand, fill, padding, pack_type = box.child_get(child, 'expand', 'fill', 'padding', 'pack-type')
2050         self.assertEqual(expand, False)
2051         self.assertEqual(fill, False)
2052         self.assertEqual(padding, 21)
2053
2054     @unittest.skipIf(Gtk._version != "4.0", "only in gtk4")
2055     def test_child_get_and_set_gtk4(self):
2056         # padding got removed in gtk4
2057         box = Gtk.Box()
2058         child = Gtk.Button()
2059         box.pack_start(child, expand=True, fill=True)
2060
2061         expand, fill = box.child_get(child, 'expand', 'fill')
2062         self.assertEqual(expand, True)
2063         self.assertEqual(fill, True)
2064
2065         box.child_set(child, expand=False, fill=False, pack_type=1)
2066         expand, fill, pack_type = box.child_get(child, 'expand', 'fill', 'pack-type')
2067         self.assertEqual(expand, False)
2068         self.assertEqual(fill, False)