tests: don't test for specific device labels
[pygobject.git] / pygtkcompat / pygtkcompat.py
blob85eb35b28f47ba47e63325b2a5a0a7267e29b781
1 # -*- Mode: Python; py-indent-offset: 4 -*-
2 # vim: tabstop=4 shiftwidth=4 expandtab
4 # Copyright (C) 2011-2012 Johan Dahlin <johan@gnome.org>
6 # This library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2.1 of the License, or (at your option) any later version.
11 # This library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this library; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 # USA
21 """
22 PyGTK compatibility layer.
24 This modules goes a little bit longer to maintain PyGTK compatibility than
25 the normal overrides system.
27 It is recommended to not depend on this layer, but only use it as an
28 intermediate step when porting your application to PyGI.
30 Compatibility might never be 100%, but the aim is to make it possible to run
31 a well behaved PyGTK application mostly unmodified on top of PyGI.
33 """
35 import sys
36 import warnings
38 import gi
39 from gi.repository import GObject
40 from gi import _compat
43 _patches = []
44 _module_patches = []
45 _unset = object()
46 _enabled_registry = {}
49 def _patch(obj, name, new_value):
50 old_value = getattr(obj, name, _unset)
51 setattr(obj, name, new_value)
52 _patches.append((obj, name, old_value))
55 def _patch_module(name, new_value):
56 old_value = sys.modules.get(name, _unset)
57 sys.modules[name] = new_value
58 _module_patches.append((name, old_value))
61 def _install_enums(module, dest=None, strip=''):
62 if dest is None:
63 dest = module
64 modname = dest.__name__.rsplit('.', 1)[1].upper()
65 for attr in dir(module):
66 try:
67 obj = getattr(module, attr, None)
68 except:
69 continue
70 try:
71 if issubclass(obj, GObject.GEnum):
72 for value, enum in obj.__enum_values__.items():
73 name = enum.value_name
74 name = name.replace(modname + '_', '')
75 if strip and name.startswith(strip):
76 name = name[len(strip):]
77 _patch(dest, name, enum)
78 except TypeError:
79 continue
80 try:
81 if issubclass(obj, GObject.GFlags):
82 for value, flag in obj.__flags_values__.items():
83 try:
84 name = flag.value_names[-1].replace(modname + '_', '')
85 except IndexError:
86 # FIXME: this happens for some large flags which do not
87 # fit into a long on 32 bit systems
88 continue
89 _patch(dest, name, flag)
90 except TypeError:
91 continue
94 def _check_enabled(name, version=None):
95 """Returns True in case it is already enabled"""
97 if name in _enabled_registry:
98 enabled_version = _enabled_registry[name]
99 if enabled_version != version:
100 raise ValueError(
101 "%r already enabled with different version (%r)" % (
102 name, enabled_version))
103 return True
104 else:
105 _enabled_registry[name] = version
106 return False
109 def enable():
110 if _check_enabled(""):
111 return
113 # gobject
114 from gi.repository import GLib
115 _patch_module('glib', GLib)
117 # gobject
118 from gi.repository import GObject
119 _patch_module('gobject', GObject)
120 from gi import _propertyhelper
121 _patch_module('gobject.propertyhelper', _propertyhelper)
123 # gio
124 from gi.repository import Gio
125 _patch_module('gio', Gio)
128 def _disable_all():
129 """Reverse all effects of the enable_xxx() calls except for
130 require_version() calls and imports.
133 _enabled_registry.clear()
135 for obj, name, old_value in reversed(_patches):
136 if old_value is _unset:
137 delattr(obj, name)
138 else:
139 # try if deleting is enough (for override proxies)
140 delattr(obj, name)
141 if getattr(obj, name, _unset) is not old_value:
142 setattr(obj, name, old_value)
143 del _patches[:]
145 for name, old_value in reversed(_module_patches):
146 if old_value is _unset:
147 del sys.modules[name]
148 else:
149 sys.modules[name] = old_value
150 del _module_patches[:]
152 _compat.reload(sys)
153 if _compat.PY2:
154 sys.setdefaultencoding('ascii')
157 def enable_gtk(version='3.0'):
158 if _check_enabled("gtk", version):
159 return
161 if version == "4.0":
162 raise ValueError("version 4.0 not supported")
164 # set the default encoding like PyGTK
165 _compat.reload(sys)
166 if _compat.PY2:
167 sys.setdefaultencoding('utf-8')
169 # atk
170 gi.require_version('Atk', '1.0')
171 from gi.repository import Atk
172 _patch_module('atk', Atk)
173 _install_enums(Atk)
175 # pango
176 gi.require_version('Pango', '1.0')
177 from gi.repository import Pango
178 _patch_module('pango', Pango)
179 _install_enums(Pango)
181 # pangocairo
182 gi.require_version('PangoCairo', '1.0')
183 from gi.repository import PangoCairo
184 _patch_module('pangocairo', PangoCairo)
186 # gdk
187 gi.require_version('Gdk', version)
188 gi.require_version('GdkPixbuf', '2.0')
189 from gi.repository import Gdk
190 from gi.repository import GdkPixbuf
191 _patch_module('gtk.gdk', Gdk)
192 _install_enums(Gdk)
193 _install_enums(GdkPixbuf, dest=Gdk)
194 _patch(Gdk, "_2BUTTON_PRESS", 5)
195 _patch(Gdk, "BUTTON_PRESS", 4)
197 _patch(Gdk, "screen_get_default", Gdk.Screen.get_default)
198 _patch(Gdk, "Pixbuf", GdkPixbuf.Pixbuf)
199 _patch(Gdk, "PixbufLoader", GdkPixbuf.PixbufLoader.new_with_type)
200 _patch(Gdk, "pixbuf_new_from_data", GdkPixbuf.Pixbuf.new_from_data)
201 _patch(Gdk, "pixbuf_new_from_file", GdkPixbuf.Pixbuf.new_from_file)
202 try:
203 _patch(Gdk, "pixbuf_new_from_file_at_scale", GdkPixbuf.Pixbuf.new_from_file_at_scale)
204 except AttributeError:
205 pass
206 _patch(Gdk, "pixbuf_new_from_file_at_size", GdkPixbuf.Pixbuf.new_from_file_at_size)
207 _patch(Gdk, "pixbuf_new_from_inline", GdkPixbuf.Pixbuf.new_from_inline)
208 _patch(Gdk, "pixbuf_new_from_stream", GdkPixbuf.Pixbuf.new_from_stream)
209 _patch(Gdk, "pixbuf_new_from_stream_at_scale", GdkPixbuf.Pixbuf.new_from_stream_at_scale)
210 _patch(Gdk, "pixbuf_new_from_xpm_data", GdkPixbuf.Pixbuf.new_from_xpm_data)
211 _patch(Gdk, "pixbuf_get_file_info", GdkPixbuf.Pixbuf.get_file_info)
213 orig_get_formats = GdkPixbuf.Pixbuf.get_formats
215 def get_formats():
216 formats = orig_get_formats()
217 result = []
219 def make_dict(format_):
220 result = {}
221 result['description'] = format_.get_description()
222 result['name'] = format_.get_name()
223 result['mime_types'] = format_.get_mime_types()
224 result['extensions'] = format_.get_extensions()
225 return result
227 for format_ in formats:
228 result.append(make_dict(format_))
229 return result
231 _patch(Gdk, "pixbuf_get_formats", get_formats)
233 orig_get_frame_extents = Gdk.Window.get_frame_extents
235 def get_frame_extents(window):
236 try:
237 try:
238 rect = Gdk.Rectangle(0, 0, 0, 0)
239 except TypeError:
240 rect = Gdk.Rectangle()
241 orig_get_frame_extents(window, rect)
242 except TypeError:
243 rect = orig_get_frame_extents(window)
244 return rect
245 _patch(Gdk.Window, "get_frame_extents", get_frame_extents)
247 orig_get_origin = Gdk.Window.get_origin
249 def get_origin(self):
250 return orig_get_origin(self)[1:]
251 _patch(Gdk.Window, "get_origin", get_origin)
253 _patch(Gdk, "screen_width", Gdk.Screen.width)
254 _patch(Gdk, "screen_height", Gdk.Screen.height)
256 orig_gdk_window_get_geometry = Gdk.Window.get_geometry
258 def gdk_window_get_geometry(window):
259 return orig_gdk_window_get_geometry(window) + (window.get_visual().get_best_depth(),)
260 _patch(Gdk.Window, "get_geometry", gdk_window_get_geometry)
262 # gtk
263 gi.require_version('Gtk', version)
264 from gi.repository import Gtk
265 _patch_module('gtk', Gtk)
266 _patch(Gtk, "gdk", Gdk)
268 _patch(Gtk, "pygtk_version", (2, 99, 0))
270 _patch(Gtk, "gtk_version", (Gtk.MAJOR_VERSION,
271 Gtk.MINOR_VERSION,
272 Gtk.MICRO_VERSION))
273 _install_enums(Gtk)
275 # Action
277 def set_tool_item_type(menuaction, gtype):
278 warnings.warn('set_tool_item_type() is not supported',
279 gi.PyGIDeprecationWarning, stacklevel=2)
280 _patch(Gtk.Action, "set_tool_item_type", classmethod(set_tool_item_type))
282 # Alignment
284 orig_Alignment = Gtk.Alignment
286 class Alignment(orig_Alignment):
287 def __init__(self, xalign=0.0, yalign=0.0, xscale=0.0, yscale=0.0):
288 orig_Alignment.__init__(self)
289 self.props.xalign = xalign
290 self.props.yalign = yalign
291 self.props.xscale = xscale
292 self.props.yscale = yscale
294 _patch(Gtk, "Alignment", Alignment)
296 # Box
298 orig_pack_end = Gtk.Box.pack_end
300 def pack_end(self, child, expand=True, fill=True, padding=0):
301 orig_pack_end(self, child, expand, fill, padding)
302 _patch(Gtk.Box, "pack_end", pack_end)
304 orig_pack_start = Gtk.Box.pack_start
306 def pack_start(self, child, expand=True, fill=True, padding=0):
307 orig_pack_start(self, child, expand, fill, padding)
308 _patch(Gtk.Box, "pack_start", pack_start)
310 # TreeViewColumn
312 orig_tree_view_column_pack_end = Gtk.TreeViewColumn.pack_end
314 def tree_view_column_pack_end(self, cell, expand=True):
315 orig_tree_view_column_pack_end(self, cell, expand)
316 _patch(Gtk.TreeViewColumn, "pack_end", tree_view_column_pack_end)
318 orig_tree_view_column_pack_start = Gtk.TreeViewColumn.pack_start
320 def tree_view_column_pack_start(self, cell, expand=True):
321 orig_tree_view_column_pack_start(self, cell, expand)
322 _patch(Gtk.TreeViewColumn, "pack_start", tree_view_column_pack_start)
324 # CellLayout
326 orig_cell_pack_end = Gtk.CellLayout.pack_end
328 def cell_pack_end(self, cell, expand=True):
329 orig_cell_pack_end(self, cell, expand)
330 _patch(Gtk.CellLayout, "pack_end", cell_pack_end)
332 orig_cell_pack_start = Gtk.CellLayout.pack_start
334 def cell_pack_start(self, cell, expand=True):
335 orig_cell_pack_start(self, cell, expand)
336 _patch(Gtk.CellLayout, "pack_start", cell_pack_start)
338 orig_set_cell_data_func = Gtk.CellLayout.set_cell_data_func
340 def set_cell_data_func(self, cell, func, user_data=_unset):
341 def callback(*args):
342 if args[-1] == _unset:
343 args = args[:-1]
344 return func(*args)
345 orig_set_cell_data_func(self, cell, callback, user_data)
346 _patch(Gtk.CellLayout, "set_cell_data_func", set_cell_data_func)
348 # CellRenderer
350 class GenericCellRenderer(Gtk.CellRenderer):
351 pass
352 _patch(Gtk, "GenericCellRenderer", GenericCellRenderer)
354 # ComboBox
356 orig_combo_row_separator_func = Gtk.ComboBox.set_row_separator_func
358 def combo_row_separator_func(self, func, user_data=_unset):
359 def callback(*args):
360 if args[-1] == _unset:
361 args = args[:-1]
362 return func(*args)
363 orig_combo_row_separator_func(self, callback, user_data)
364 _patch(Gtk.ComboBox, "set_row_separator_func", combo_row_separator_func)
366 # ComboBoxEntry
368 class ComboBoxEntry(Gtk.ComboBox):
369 def __init__(self, **kwds):
370 Gtk.ComboBox.__init__(self, has_entry=True, **kwds)
372 def set_text_column(self, text_column):
373 self.set_entry_text_column(text_column)
375 def get_text_column(self):
376 return self.get_entry_text_column()
377 _patch(Gtk, "ComboBoxEntry", ComboBoxEntry)
379 def combo_box_entry_new():
380 return Gtk.ComboBoxEntry()
381 _patch(Gtk, "combo_box_entry_new", combo_box_entry_new)
383 def combo_box_entry_new_with_model(model):
384 return Gtk.ComboBoxEntry(model=model)
385 _patch(Gtk, "combo_box_entry_new_with_model", combo_box_entry_new_with_model)
387 # Container
389 def install_child_property(container, flag, pspec):
390 warnings.warn('install_child_property() is not supported',
391 gi.PyGIDeprecationWarning, stacklevel=2)
392 _patch(Gtk.Container, "install_child_property", classmethod(install_child_property))
394 def new_text():
395 combo = Gtk.ComboBox()
396 model = Gtk.ListStore(str)
397 combo.set_model(model)
398 combo.set_entry_text_column(0)
399 return combo
400 _patch(Gtk, "combo_box_new_text", new_text)
402 def append_text(self, text):
403 model = self.get_model()
404 model.append([text])
405 _patch(Gtk.ComboBox, "append_text", append_text)
406 _patch(Gtk, "expander_new_with_mnemonic", Gtk.Expander.new_with_mnemonic)
407 _patch(Gtk, "icon_theme_get_default", Gtk.IconTheme.get_default)
408 _patch(Gtk, "image_new_from_pixbuf", Gtk.Image.new_from_pixbuf)
409 _patch(Gtk, "image_new_from_stock", Gtk.Image.new_from_stock)
410 _patch(Gtk, "image_new_from_animation", Gtk.Image.new_from_animation)
411 _patch(Gtk, "image_new_from_icon_set", Gtk.Image.new_from_icon_set)
412 _patch(Gtk, "image_new_from_file", Gtk.Image.new_from_file)
413 _patch(Gtk, "settings_get_default", Gtk.Settings.get_default)
414 _patch(Gtk, "window_set_default_icon", Gtk.Window.set_default_icon)
415 try:
416 _patch(Gtk, "clipboard_get", Gtk.Clipboard.get)
417 except AttributeError:
418 pass
420 # AccelGroup
421 _patch(Gtk.AccelGroup, "connect_group", Gtk.AccelGroup.connect)
423 # StatusIcon
424 _patch(Gtk, "status_icon_position_menu", Gtk.StatusIcon.position_menu)
425 _patch(Gtk.StatusIcon, "set_tooltip", Gtk.StatusIcon.set_tooltip_text)
427 # Scale
429 orig_HScale = Gtk.HScale
430 orig_VScale = Gtk.VScale
432 class HScale(orig_HScale):
433 def __init__(self, adjustment=None):
434 orig_HScale.__init__(self, adjustment=adjustment)
435 _patch(Gtk, "HScale", HScale)
437 class VScale(orig_VScale):
438 def __init__(self, adjustment=None):
439 orig_VScale.__init__(self, adjustment=adjustment)
440 _patch(Gtk, "VScale", VScale)
442 _patch(Gtk, "stock_add", lambda items: None)
444 # Widget
446 _patch(Gtk.Widget, "window", property(fget=Gtk.Widget.get_window))
448 _patch(Gtk, "widget_get_default_direction", Gtk.Widget.get_default_direction)
449 orig_size_request = Gtk.Widget.size_request
451 def size_request(widget):
452 class SizeRequest(_compat.UserList):
453 def __init__(self, req):
454 self.height = req.height
455 self.width = req.width
456 _compat.UserList.__init__(self, [self.width, self.height])
457 return SizeRequest(orig_size_request(widget))
458 _patch(Gtk.Widget, "size_request", size_request)
459 _patch(Gtk.Widget, "hide_all", Gtk.Widget.hide)
461 class BaseGetter(object):
462 def __init__(self, context):
463 self.context = context
465 def __getitem__(self, state):
466 color = self.context.get_background_color(state)
467 return Gdk.Color(red=int(color.red * 65535),
468 green=int(color.green * 65535),
469 blue=int(color.blue * 65535))
471 class Styles(object):
472 def __init__(self, widget):
473 context = widget.get_style_context()
474 self.base = BaseGetter(context)
475 self.black = Gdk.Color(red=0, green=0, blue=0)
477 class StyleDescriptor(object):
478 def __get__(self, instance, class_):
479 return Styles(instance)
480 _patch(Gtk.Widget, "style", StyleDescriptor())
482 # TextView
484 orig_text_view_scroll_to_mark = Gtk.TextView.scroll_to_mark
486 def text_view_scroll_to_mark(self, mark, within_margin,
487 use_align=False, xalign=0.5, yalign=0.5):
488 return orig_text_view_scroll_to_mark(self, mark, within_margin,
489 use_align, xalign, yalign)
490 _patch(Gtk.TextView, "scroll_to_mark", text_view_scroll_to_mark)
492 # Window
494 orig_set_geometry_hints = Gtk.Window.set_geometry_hints
496 def set_geometry_hints(self, geometry_widget=None,
497 min_width=-1, min_height=-1, max_width=-1, max_height=-1,
498 base_width=-1, base_height=-1, width_inc=-1, height_inc=-1,
499 min_aspect=-1.0, max_aspect=-1.0):
501 geometry = Gdk.Geometry()
502 geom_mask = Gdk.WindowHints(0)
504 if min_width >= 0 or min_height >= 0:
505 geometry.min_width = max(min_width, 0)
506 geometry.min_height = max(min_height, 0)
507 geom_mask |= Gdk.WindowHints.MIN_SIZE
509 if max_width >= 0 or max_height >= 0:
510 geometry.max_width = max(max_width, 0)
511 geometry.max_height = max(max_height, 0)
512 geom_mask |= Gdk.WindowHints.MAX_SIZE
514 if base_width >= 0 or base_height >= 0:
515 geometry.base_width = max(base_width, 0)
516 geometry.base_height = max(base_height, 0)
517 geom_mask |= Gdk.WindowHints.BASE_SIZE
519 if width_inc >= 0 or height_inc >= 0:
520 geometry.width_inc = max(width_inc, 0)
521 geometry.height_inc = max(height_inc, 0)
522 geom_mask |= Gdk.WindowHints.RESIZE_INC
524 if min_aspect >= 0.0 or max_aspect >= 0.0:
525 if min_aspect <= 0.0 or max_aspect <= 0.0:
526 raise TypeError("aspect ratios must be positive")
528 geometry.min_aspect = min_aspect
529 geometry.max_aspect = max_aspect
530 geom_mask |= Gdk.WindowHints.ASPECT
532 return orig_set_geometry_hints(self, geometry_widget, geometry, geom_mask)
534 _patch(Gtk.Window, "set_geometry_hints", set_geometry_hints)
535 _patch(Gtk, "window_list_toplevels", Gtk.Window.list_toplevels)
536 _patch(Gtk, "window_set_default_icon_name", Gtk.Window.set_default_icon_name)
538 # gtk.unixprint
540 class UnixPrint(object):
541 pass
542 unixprint = UnixPrint()
543 _patch_module('gtkunixprint', unixprint)
545 # gtk.keysyms
547 with warnings.catch_warnings():
548 warnings.simplefilter('ignore', category=RuntimeWarning)
549 from gi.overrides import keysyms
551 _patch_module('gtk.keysyms', keysyms)
552 _patch(Gtk, "keysyms", keysyms)
554 from . import generictreemodel
555 _patch(Gtk, "GenericTreeModel", generictreemodel.GenericTreeModel)
558 def enable_vte():
559 if _check_enabled("vte"):
560 return
562 gi.require_version('Vte', '0.0')
563 from gi.repository import Vte
564 _patch_module('vte', Vte)
567 def enable_poppler():
568 if _check_enabled("poppler"):
569 return
571 gi.require_version('Poppler', '0.18')
572 from gi.repository import Poppler
573 _patch_module('poppler', Poppler)
575 _patch(Poppler, "pypoppler_version", (1, 0, 0))
578 def enable_webkit(version='1.0'):
579 if _check_enabled("webkit", version):
580 return
582 gi.require_version('WebKit', version)
583 from gi.repository import WebKit
584 _patch_module('webkit', WebKit)
586 _patch(WebKit.WebView, "get_web_inspector", WebKit.WebView.get_inspector)
589 def enable_gudev():
590 if _check_enabled("gudev"):
591 return
593 gi.require_version('GUdev', '1.0')
594 from gi.repository import GUdev
595 _patch_module('gudev', GUdev)
598 def enable_gst():
599 if _check_enabled("gst"):
600 return
602 gi.require_version('Gst', '0.10')
603 from gi.repository import Gst
604 _patch_module('gst', Gst)
605 _install_enums(Gst)
607 _patch(Gst, "registry_get_default", Gst.Registry.get_default)
608 _patch(Gst, "element_register", Gst.Element.register)
609 _patch(Gst, "element_factory_make", Gst.ElementFactory.make)
610 _patch(Gst, "caps_new_any", Gst.Caps.new_any)
611 _patch(Gst, "get_pygst_version", lambda: (0, 10, 19))
612 _patch(Gst, "get_gst_version", lambda: (0, 10, 40))
614 from gi.repository import GstInterfaces
615 _patch_module('gst.interfaces', GstInterfaces)
616 _install_enums(GstInterfaces)
618 from gi.repository import GstAudio
619 _patch_module('gst.audio', GstAudio)
620 _install_enums(GstAudio)
622 from gi.repository import GstVideo
623 _patch_module('gst.video', GstVideo)
624 _install_enums(GstVideo)
626 from gi.repository import GstBase
627 _patch_module('gst.base', GstBase)
628 _install_enums(GstBase)
630 _patch(Gst, "BaseTransform", GstBase.BaseTransform)
631 _patch(Gst, "BaseSink", GstBase.BaseSink)
633 from gi.repository import GstController
634 _patch_module('gst.controller', GstController)
635 _install_enums(GstController, dest=Gst)
637 from gi.repository import GstPbutils
638 _patch_module('gst.pbutils', GstPbutils)
639 _install_enums(GstPbutils)
642 def enable_goocanvas():
643 if _check_enabled("goocanvas"):
644 return
646 gi.require_version('GooCanvas', '2.0')
647 from gi.repository import GooCanvas
648 _patch_module('goocanvas', GooCanvas)
649 _install_enums(GooCanvas, strip='GOO_CANVAS_')
651 _patch(GooCanvas, "ItemSimple", GooCanvas.CanvasItemSimple)
652 _patch(GooCanvas, "Item", GooCanvas.CanvasItem)
653 _patch(GooCanvas, "Image", GooCanvas.CanvasImage)
654 _patch(GooCanvas, "Group", GooCanvas.CanvasGroup)
655 _patch(GooCanvas, "Rect", GooCanvas.CanvasRect)