def test_constrains(self): if is_py2exe(): self.assertEqual(is_py2exe_console(), not is_py2exe_window()) self.assertTrue(is_windows()) if is_windows(): self.assertFalse(is_osx()) if is_osx(): self.assertFalse(is_windows())
def test_constrains(self): if is_py2exe(): self.assertEqual(is_py2exe_console(), not is_py2exe_window()) self.assertTrue(is_windows()) if is_windows(): self.assertFalse(is_osx()) if is_osx(): self.assertFalse(is_windows())
def test_all(self): self.assertTrue(isinstance(is_unity(), bool)) self.assertTrue(isinstance(is_windows(), bool)) self.assertTrue(isinstance(is_osx(), bool)) self.assertTrue(isinstance(is_py2exe(), bool)) self.assertTrue(isinstance(is_py2exe_console(), bool)) self.assertTrue(isinstance(is_py2exe_window(), bool))
def test_all(self): self.assertTrue(isinstance(is_unity(), bool)) self.assertTrue(isinstance(is_windows(), bool)) self.assertTrue(isinstance(is_osx(), bool)) self.assertTrue(isinstance(is_py2exe(), bool)) self.assertTrue(isinstance(is_py2exe_console(), bool)) self.assertTrue(isinstance(is_py2exe_window(), bool))
def get_user_dir(): """Place where QL saves its state, database, config etc.""" if os.name == "nt": USERDIR = os.path.join(windows.get_appdata_dir(), "Quod Libet") elif is_osx(): USERDIR = os.path.join(os.path.expanduser("~"), ".quodlibet") else: USERDIR = os.path.join(xdg_get_config_home(), "quodlibet") if not os.path.exists(USERDIR): tmp = os.path.join(os.path.expanduser("~"), ".quodlibet") if os.path.exists(tmp): USERDIR = tmp if 'QUODLIBET_USERDIR' in environ: USERDIR = environ['QUODLIBET_USERDIR'] if build.BUILD_TYPE == u"windows-portable": USERDIR = os.path.normpath(os.path.join( os.path.dirname(path2fsn(sys.executable)), "..", "..", "config")) # XXX: users shouldn't assume the dir is there, but we currently do in # some places mkdir(USERDIR, 0o750) return USERDIR
def _init_python(): if PY2 or is_release(): MinVersions.PYTHON2.check(sys.version_info) else: # for non release builds we allow Python3 MinVersions.PYTHON3.check(sys.version_info) if is_osx(): # We build our own openssl on OSX and need to make sure that # our own ca file is used in all cases as the non-system openssl # doesn't use the system certs install_urllib2_ca_file() if is_windows(): # Not really needed on Windows as pygi-aio seems to work fine, but # wine doesn't have certs which we use for testing. install_urllib2_ca_file() if is_windows() and os.sep != "\\": # In the MSYS2 console MSYSTEM is set, which breaks os.sep/os.path.sep # If you hit this do a "setup.py clean -all" to get rid of the # bytecode cache then start things with "MSYSTEM= ..." raise AssertionError("MSYSTEM is set (%r)" % environ.get("MSYSTEM")) if is_windows(): # gdbm is broken under msys2, this makes shelve use another backend sys.modules["gdbm"] = None sys.modules["_gdbm"] = None logging.getLogger().addHandler(PrintHandler())
def _init_python(): if PY2 or is_release(): MinVersions.PYTHON2.check(sys.version_info) else: # for non release builds we allow Python3 MinVersions.PYTHON3.check(sys.version_info) if is_osx(): # We build our own openssl on OSX and need to make sure that # our own ca file is used in all cases as the non-system openssl # doesn't use the system certs install_urllib2_ca_file() if is_windows(): # Not really needed on Windows as pygi-aio seems to work fine, but # wine doesn't have certs which we use for testing. install_urllib2_ca_file() if is_windows() and os.sep != "\\": # In the MSYS2 console MSYSTEM is set, which breaks os.sep/os.path.sep # If you hit this do a "setup.py clean -all" to get rid of the # bytecode cache then start things with "MSYSTEM= ..." raise AssertionError("MSYSTEM is set (%r)" % environ.get("MSYSTEM")) if is_windows(): # gdbm is broken under msys2, this makes shelve use another backend sys.modules["gdbm"] = None sys.modules["_gdbm"] = None logging.getLogger().addHandler(PrintHandler())
def get_user_dir(): """Place where QL saves its state, database, config etc.""" if os.name == "nt": USERDIR = os.path.join(windows.get_appdata_dir(), "Quod Libet") elif is_osx(): USERDIR = os.path.join(os.path.expanduser("~"), ".quodlibet") else: USERDIR = os.path.join(xdg_get_config_home(), "quodlibet") if not os.path.exists(USERDIR): tmp = os.path.join(os.path.expanduser("~"), ".quodlibet") if os.path.exists(tmp): USERDIR = tmp if 'QUODLIBET_USERDIR' in environ: USERDIR = environ['QUODLIBET_USERDIR'] if build.BUILD_TYPE == u"windows-portable": USERDIR = os.path.normpath( os.path.join(os.path.dirname(path2fsn(sys.executable)), "..", "..", "config")) # XXX: users shouldn't assume the dir is there, but we currently do in # some places mkdir(USERDIR, 0o750) return USERDIR
def enabled(self): impl = get_indicator_impl() self._tray = impl() self._tray.set_song(app.player.song) self._tray.set_info_song(app.player.info) self._tray.set_paused(app.player.paused) if not is_osx() and not pconfig.getboolean("window_visible"): Window.prevent_inital_show(True)
def enabled(self): impl = get_indicator_impl() self._tray = impl() self._tray.set_song(app.player.song) self._tray.set_info_song(app.player.info) self._tray.set_paused(app.player.paused) if not is_osx() and not pconfig.getboolean("window_visible"): Window.prevent_inital_show(True)
def _show_files_finder(dirname, *args): if not is_osx(): raise BrowseError("OS X only") try: if subprocess.call(["open", "-R", dirname]) != 0: raise EnvironmentError("open error return status") except EnvironmentError as e: raise BrowseError(e)
def _show_files_finder(dirname, *args): if not is_osx(): raise BrowseError("OS X only") try: if subprocess.call(["open", "-R", dirname]) != 0: raise EnvironmentError("open error return status") except EnvironmentError as e: raise BrowseError(e)
def has_locale(loc): if is_osx(): return False try: with set_locale(loc): pass except locale.Error: return False else: return True
def test_soup(self): if is_osx(): return for url in self.GOOD: session = Soup.Session.new() request = session.request_http("get", url) request.send(None).close() for url in self.BAD: with self.assertRaises(GLib.GError): session = Soup.Session.new() request = session.request_http("get", url) request.send(None).close()
def test_gio(self): if is_osx(): return for url in self.GOOD: client = Gio.SocketClient.new() client.set_tls(True) client.connect_to_uri(url, 443, None).close() for url in self.BAD: with self.assertRaises(GLib.GError): client = Gio.SocketClient.new() client.set_tls(True) client.connect_to_uri(url, 443, None).close()
def test_gio(self): if is_osx(): return for url in self.GOOD: client = Gio.SocketClient.new() client.set_tls(True) client.connect_to_uri(url, 443, None).close() for url in self.BAD: with self.assertRaises(GLib.GError): client = Gio.SocketClient.new() client.set_tls(True) client.connect_to_uri(url, 443, None).close()
def test_soup(self): if is_osx(): return for url in self.GOOD: session = Soup.Session.new() request = session.request_http("get", url) request.send(None).close() for url in self.BAD: with self.assertRaises(GLib.GError): session = Soup.Session.new() request = session.request_http("get", url) request.send(None).close()
def init_devices(): global devices load_pyc = util.is_windows() or util.is_osx() modules = load_dir_modules(dirname(__file__), package=__package__, load_compiled=load_pyc) for mod in modules: try: devices.extend(mod.devices) except AttributeError: print_w("%r doesn't contain any devices." % mod.__name__) devices.sort()
def init_devices(): global devices load_pyc = util.is_windows() or util.is_osx() modules = load_dir_modules(dirname(__file__), package=__package__, load_compiled=load_pyc) for mod in modules: try: devices.extend(mod.devices) except AttributeError: print_w("%r doesn't contain any devices." % mod.__name__) devices.sort(key=lambda d: repr(d))
def init(): """Import all browsers from this package and from the user directory. After this is called the global `browers` list will contain all classes sorted by priority. Can be called multiple times. """ global browsers, default # ignore double init (for the test suite) if browsers: return this_dir = os.path.dirname(__file__) load_pyc = util.is_windows() or util.is_osx() modules = load_dir_modules(this_dir, package=__package__, load_compiled=load_pyc) user_dir = os.path.join(quodlibet.get_user_dir(), "browsers") if os.path.isdir(user_dir): modules += load_dir_modules(user_dir, package="quodlibet.fake.browsers", load_compiled=load_pyc) for browser in modules: try: browsers.extend(browser.browsers) except AttributeError: print_w("%r doesn't contain any browsers." % browser.__name__) def is_browser(Kind): return isinstance(Kind, type) and issubclass(Kind, Browser) browsers = list(filter(is_browser, browsers)) if not browsers: raise SystemExit("No browsers found!") browsers.sort(key=lambda Kind: Kind.priority) try: default = get("SearchBar") except ValueError: raise SystemExit("Default browser not found!")
def _init_python(): if PY2 or is_release(): MinVersions.PYTHON2.check(sys.version_info) else: # for non release builds we allow Python3 MinVersions.PYTHON3.check(sys.version_info) if is_osx(): # We build our own openssl on OSX and need to make sure that # our own ca file is used in all cases as the non-system openssl # doesn't use the system certs util.install_urllib2_ca_file() if is_windows(): # Not really needed on Windows as pygi-aio seems to work fine, but # wine doesn't have certs which we use for testing. util.install_urllib2_ca_file()
def init(): """Load/Import all formats. Before this is called loading a file and unpickling will not work. """ global mimes, loaders, types MinVersions.MUTAGEN.check(mutagen.version) base = util.get_module_dir() load_pyc = util.is_windows() or util.is_osx() formats = load_dir_modules(base, package=__package__, load_compiled=load_pyc) module_names = [] for format in formats: name = format.__name__ for ext in format.extensions: loaders[ext] = format.loader types.update(format.types) if format.extensions: for type_ in format.types: mimes.update(type_.mimes) module_names.append(name.split(".")[-1]) # Migrate pre-0.16 library, which was using an undocumented "feature". sys.modules[name.replace(".", "/")] = format # Migrate old layout if name.startswith("quodlibet."): sys.modules[name.split(".", 1)[1]] = format # This can be used for the quodlibet.desktop file desktop_mime_types = "MimeType=" + \ ";".join(sorted({m.split(";")[0] for m in mimes})) + ";" print_d(desktop_mime_types) s = ", ".join(sorted(module_names)) print_d("Supported formats: %s" % s) if not loaders: raise SystemExit("No formats found!")
def _init_python(): if PY2 or is_release(): MinVersions.PYTHON2.check(sys.version_info) else: # for non release builds we allow Python3 MinVersions.PYTHON3.check(sys.version_info) if is_osx(): # We build our own openssl on OSX and need to make sure that # our own ca file is used in all cases as the non-system openssl # doesn't use the system certs util.install_urllib2_ca_file() if is_windows(): # Not really needed on Windows as pygi-aio seems to work fine, but # wine doesn't have certs which we use for testing. util.install_urllib2_ca_file()
def init(): """Load/Import all formats. Before this is called loading a file and unpickling will not work. """ global mimes, loaders, types MinVersions.MUTAGEN.check(mutagen.version) base = util.get_module_dir() load_pyc = util.is_windows() or util.is_osx() formats = load_dir_modules(base, package=__package__, load_compiled=load_pyc) module_names = [] for format in formats: name = format.__name__ for ext in format.extensions: loaders[ext] = format.loader types.update(format.types) if format.extensions: for type_ in format.types: mimes.update(type_.mimes) module_names.append(name.split(".")[-1]) # Migrate pre-0.16 library, which was using an undocumented "feature". sys.modules[name.replace(".", "/")] = format # Migrate old layout if name.startswith("quodlibet."): sys.modules[name.split(".", 1)[1]] = format # This can be used for the quodlibet.desktop file desktop_mime_types = "MimeType=" + \ ";".join(sorted({m.split(";")[0] for m in mimes})) + ";" print_d(desktop_mime_types) s = ", ".join(sorted(module_names)) print_d("Supported formats: %s" % s) if not loaders: raise SystemExit("No formats found!")
def init(): """Import all browsers from this package and from the user directory. After this is called the global `browers` list will contain all classes sorted by priority. Can be called multiple times. """ global browsers, default # ignore double init (for the test suite) if browsers: return this_dir = os.path.dirname(__file__) load_pyc = util.is_windows() or util.is_osx() modules = load_dir_modules(this_dir, package=__package__, load_compiled=load_pyc) user_dir = os.path.join(quodlibet.get_user_dir(), "browsers") if os.path.isdir(user_dir): modules += load_dir_modules(user_dir, package="quodlibet.fake.browsers", load_compiled=load_pyc) for browser in modules: try: browsers.extend(browser.browsers) except AttributeError: print_w("%r doesn't contain any browsers." % browser.__name__) def is_browser(Kind): return isinstance(Kind, type) and issubclass(Kind, Browser) browsers = filter(is_browser, browsers) if not browsers: raise SystemExit("No browsers found!") browsers.sort(key=lambda Kind: Kind.priority) try: default = get("SearchBar") except ValueError: raise SystemExit("Default browser not found!")
def init(): global mimes, _infos, modules, names, _extensions import mutagen MinVersions.MUTAGEN.check(mutagen.version) base = os.path.dirname(__file__) load_pyc = util.is_windows() or util.is_osx() formats = load_dir_modules(base, package=__package__, load_compiled=load_pyc) for format in formats: name = format.__name__ for ext in format.extensions: _infos[ext] = format.info types.extend(format.types) if format.extensions: for type_ in format.types: mimes.update(type_.mimes) names.append(type_.format) modules.append(name.split(".")[-1]) # Migrate pre-0.16 library, which was using an undocumented "feature". sys.modules[name.replace(".", "/")] = format # Migrate old layout if name.startswith("quodlibet."): sys.modules[name.split(".", 1)[1]] = format modules.sort() names.sort() # This can be used for the quodlibet.desktop file desktop_mime_types = "MimeType=" + \ ";".join(sorted({m.split(";")[0] for m in mimes})) + ";" print_d(desktop_mime_types) if not _infos: raise SystemExit("No formats found!") _extensions = tuple(_infos.keys())
def init(): global mimes, _infos, modules, names import mutagen if mutagen.version < MinVersions.MUTAGEN: raise ImportError( "Mutagen %s required. %s found." % (MinVersions.MUTAGEN, mutagen.version_string)) base = os.path.dirname(__file__) load_pyc = util.is_windows() or util.is_osx() formats = load_dir_modules(base, package=__package__, load_compiled=load_pyc) for format in formats: name = format.__name__ for ext in format.extensions: _infos[ext] = format.info types.extend(format.types) if format.extensions: for type_ in format.types: mimes.update(type_.mimes) names.append(type_.format) modules.append(name.split(".")[-1]) # Migrate pre-0.16 library, which was using an undocumented "feature". sys.modules[name.replace(".", "/")] = format # Migrate old layout if name.startswith("quodlibet."): sys.modules[name.split(".", 1)[1]] = format modules.sort() names.sort() # This can be used for the quodlibet.desktop file desktop_mime_types = "MimeType=" + \ ";".join(sorted({m.split(";")[0] for m in mimes})) + ";" print_d(desktop_mime_types) if not _infos: raise SystemExit("No formats found!")
def _init_dbus(): """Setup dbus mainloop integration. Call before using dbus""" # To make GDBus fail early and we don't have to wait for a timeout if is_osx() or is_windows(): os.environ["DBUS_SYSTEM_BUS_ADDRESS"] = "something-invalid" os.environ["DBUS_SESSION_BUS_ADDRESS"] = "something-invalid" try: from dbus.mainloop.glib import DBusGMainLoop, threads_init except ImportError: try: import dbus.glib dbus.glib except ImportError: return else: threads_init() DBusGMainLoop(set_as_default=True)
def show_uri(label, uri): """Shows a uri. The uri can be anything handled by GIO or a quodlibet specific one. Currently handled quodlibet uris: - quodlibet:///prefs/plugins/<plugin id> Args: label (str) uri (str) the uri to show Returns: True on success, False on error """ parsed = urlparse(uri) if parsed.scheme == "quodlibet": if parsed.netloc != "": print_w("Unknown QuodLibet URL format (%s)" % uri) return False else: return __show_quodlibet_uri(parsed) elif parsed.scheme == "file" and (is_windows() or is_osx()): # Gio on non-Linux can't handle file URIs for some reason, # fall back to our own implementation for now from quodlibet.qltk.showfiles import show_files try: filepath = uri2fsn(uri) except ValueError: return False else: return show_files(filepath, []) else: # Gtk.show_uri_on_window exists since 3.22 try: if hasattr(Gtk, "show_uri_on_window"): from quodlibet.qltk import get_top_parent return Gtk.show_uri_on_window(get_top_parent(label), uri, 0) else: return Gtk.show_uri(None, uri, 0) except GLib.Error: return False
def show_files(dirname, entries=[]): """Shows the directory in the default file browser and if passed a list of directory entries will highlight those. Depending on the system/platform this might highlight all files passed, or only one of them, or none at all. Args: dirname (fsnative): Path to the directory entries (List[fsnative]): List of (relative) filenames in the directory entries (List[fsnative]): List of (relative) filenames in the directory Returns: bool: if the action was successful or not """ assert isinstance(dirname, fsnative) assert all(isinstance(e, fsnative) and os.path.basename(e) == e for e in entries) dirname = os.path.abspath(dirname) if is_windows(): implementations = [_show_files_win32] elif is_osx(): implementations = [_show_files_finder] else: implementations = [ _show_files_fdo, _show_files_thunar, _show_files_xdg_open, _show_files_gnome_open, ] for impl in implementations: try: impl(dirname, entries) except BrowseError as e: print_d("Couldn't show files with %s (%s), ignoring." % (impl, e)) continue else: return True return False
def show_uri(label, uri): """Shows a uri. The uri can be anything handled by GIO or a quodlibet specific one. Currently handled quodlibet uris: - quodlibet:///prefs/plugins/<plugin id> Args: label (str) uri (str) the uri to show Returns: True on success, False on error """ parsed = urlparse(uri) if parsed.scheme == "quodlibet": if parsed.netloc != "": print_w("Unknown QuodLibet URL format (%s)" % uri) return False else: return __show_quodlibet_uri(parsed) elif parsed.scheme == "file" and (is_windows() or is_osx()): # Gio on non-Linux can't handle file URIs for some reason, # fall back to our own implementation for now from quodlibet.qltk.showfiles import show_files try: filepath = uri2fsn(uri) except ValueError: return False else: return show_files(filepath, []) else: # Gtk.show_uri_on_window exists since 3.22 try: if hasattr(Gtk, "show_uri_on_window"): from quodlibet.qltk import get_top_parent return Gtk.show_uri_on_window(get_top_parent(label), uri, 0) else: return Gtk.show_uri(None, uri, 0) except GLib.Error: return False
def show_files(dirname, entries=[]): """Shows the directory in the default file browser and if passed a list of directory entries will highlight those. Depending on the system/platform this might highlight all files passed, or only one of them, or none at all. Args: dirname (fsnative): Path to the directory entries (List[fsnative]): List of (relative) filenames in the directory entries (List[fsnative]): List of (relative) filenames in the directory Returns: bool: if the action was successful or not """ assert isinstance(dirname, fsnative) assert all(isinstance(e, fsnative) and os.path.basename(e) == e for e in entries) dirname = os.path.abspath(dirname) if is_windows(): implementations = [_show_files_win32] elif is_osx(): implementations = [_show_files_finder] else: implementations = [ _show_files_fdo, _show_files_thunar, _show_files_xdg_open, _show_files_gnome_open, ] for impl in implementations: try: impl(dirname, entries) except BrowseError: continue else: return True return False
def test_is_accel_primary(self): e = Gdk.Event.new(Gdk.EventType.KEY_PRESS) e.keyval = Gdk.KEY_Return e.state = Gdk.ModifierType.CONTROL_MASK if not util.is_osx(): self.assertTrue(qltk.is_accel(e, "<Primary>Return"))
import threading from xml.dom import minidom from urllib.parse import quote import queue from quodlibet import _, print_d from quodlibet.plugins.gui import UserInterfacePlugin from quodlibet.util import gi_require_versions, is_windows, is_osx from quodlibet.plugins.events import EventPlugin from quodlibet.plugins import (PluginImportException, PluginConfig, ConfProp, BoolConfProp, FloatConfProp, PluginNotSupportedError) try: gi_require_versions("WebKit2", ["4.0", "3.0"]) except ValueError as e: if is_windows() or is_osx(): raise PluginNotSupportedError raise PluginImportException("GObject Introspection: " + str(e)) from gi.repository import WebKit2, Gtk, GLib from quodlibet import qltk from quodlibet.util import escape, cached_property, connect_obj from quodlibet.qltk import Icons, Align from quodlibet.qltk.entry import UndoEntry from quodlibet.pattern import URLFromPattern from quodlibet.util.urllib import urlopen # for the mobile version USER_AGENT = ("Mozilla/5.0 (Linux; Android 5.1.1; Nexus 5 Build/LMY48B; wv) "
def _init_gtk(): """Call before using Gtk/Gdk""" import gi # pygiaio 3.14rev16 switched to fontconfig for PangoCairo. As this results # in 100% CPU under win7 revert it. Maybe we need to update the # cache in the windows installer for it to work... but for now revert. if is_windows(): environ['PANGOCAIRO_BACKEND'] = 'win32' environ["GTK_CSD"] = "0" # disable for consistency and trigger events seem a bit flaky here if is_osx(): environ["GTK_OVERLAY_SCROLLING"] = "0" try: # not sure if this is available under Windows gi.require_version("GdkX11", "3.0") from gi.repository import GdkX11 GdkX11 except (ValueError, ImportError): pass gi.require_version("Gtk", "3.0") gi.require_version("Gdk", "3.0") gi.require_version("Pango", "1.0") gi.require_version('Soup', '2.4') from gi.repository import Gtk from quodlibet.qltk import ThemeOverrider, gtk_version # PyGObject doesn't fail anymore when init fails, so do it ourself initialized, argv[:] = Gtk.init_check(argv) if not initialized: raise SystemExit("Gtk.init failed") # include our own icon theme directory theme = Gtk.IconTheme.get_default() theme_search_path = get_image_dir() assert os.path.exists(theme_search_path) theme.append_search_path(theme_search_path) # Force menu/button image related settings. We might show too many atm # but this makes sure we don't miss cases where we forgot to force them # per widget. # https://bugzilla.gnome.org/show_bug.cgi?id=708676 warnings.filterwarnings('ignore', '.*g_value_get_int.*', Warning) # some day... but not now warnings.filterwarnings('ignore', '.*Stock items are deprecated.*', Warning) warnings.filterwarnings('ignore', '.*:use-stock.*', Warning) warnings.filterwarnings( 'ignore', '.*The property GtkAlignment:[^\s]+ is deprecated.*', Warning) settings = Gtk.Settings.get_default() with warnings.catch_warnings(): warnings.simplefilter("ignore") settings.set_property("gtk-button-images", True) settings.set_property("gtk-menu-images", True) if hasattr(settings.props, "gtk_primary_button_warps_slider"): # https://bugzilla.gnome.org/show_bug.cgi?id=737843 settings.set_property("gtk-primary-button-warps-slider", True) # Make sure PyGObject includes support for foreign cairo structs try: gi.require_foreign("cairo") except ImportError: print_e("PyGObject is missing cairo support") exit(1) css_override = ThemeOverrider() if sys.platform == "darwin": # fix duplicated shadows for popups with Gtk+3.14 style_provider = Gtk.CssProvider() style_provider.load_from_data(b""" GtkWindow { box-shadow: none; } .tooltip { border-radius: 0; padding: 0; } .tooltip.background { background-clip: border-box; } """) css_override.register_provider("", style_provider) if gtk_version[:2] >= (3, 20): # https://bugzilla.gnome.org/show_bug.cgi?id=761435 style_provider = Gtk.CssProvider() style_provider.load_from_data(b""" spinbutton, button { min-height: 1.8rem; } .view button { min-height: 2.0rem; } entry { min-height: 2.4rem; } entry.cell { min-height: 0; } """) css_override.register_provider("Adwaita", style_provider) css_override.register_provider("HighContrast", style_provider) # https://github.com/quodlibet/quodlibet/issues/2541 style_provider = Gtk.CssProvider() style_provider.load_from_data(b""" treeview.view.separator { min-height: 2px; color: @borders; } """) css_override.register_provider("Ambiance", style_provider) css_override.register_provider("Radiance", style_provider) if gtk_version[:2] >= (3, 18): # Hack to get some grab handle like thing for panes style_provider = Gtk.CssProvider() style_provider.load_from_data(b""" GtkPaned.vertical, paned.vertical >separator { -gtk-icon-source: -gtk-icontheme("view-more-symbolic"); -gtk-icon-transform: rotate(90deg) scaleX(0.1) scaleY(3); } GtkPaned.horizontal, paned.horizontal >separator { -gtk-icon-source: -gtk-icontheme("view-more-symbolic"); -gtk-icon-transform: rotate(0deg) scaleX(0.1) scaleY(3); } """) css_override.register_provider("", style_provider) # https://bugzilla.gnome.org/show_bug.cgi?id=708676 warnings.filterwarnings('ignore', '.*g_value_get_int.*', Warning) # blacklist some modules, simply loading can cause segfaults sys.modules["gtk"] = None sys.modules["gpod"] = None sys.modules["gnome"] = None from quodlibet.qltk import pygobject_version, gtk_version MinVersions.GTK.check(gtk_version) MinVersions.PYGOBJECT.check(pygobject_version)
# the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. from gi.repository import Gtk from senf import fsnative, getcwd from quodlibet.qltk.chooser import choose_files, get_current_dir, \ set_current_dir, choose_folders, create_chooser_filter, \ choose_target_file, choose_target_folder, with_response from quodlibet.qltk import gtk_version from quodlibet.util import is_osx from . import TestCase, skipIf @skipIf(gtk_version < (3, 16, 0) or is_osx(), "crashy on older gtk+ and macOS") class Tchooser(TestCase): def test_choose_files(self): w = Gtk.Window() with with_response(Gtk.ResponseType.CANCEL): assert choose_files(w, u"title", u"action") == [] def test_choose_folders(self): w = Gtk.Window() with with_response(Gtk.ResponseType.CANCEL): assert choose_folders(w, u"title", u"action") == [] def test_choose_filter(self): cf = create_chooser_filter(u"filter", ["*.txt"]) assert isinstance(cf, Gtk.FileFilter)
# osx implementation might be buggy so let users disable it "disable_mmkeys": "false", # the UI language to use, empty means system default "language": "", # the pattern for the main window title "window_title_pattern": "~title~version~~people", # the format of the timestamps in DateColumn "datecolumn_timestamp_format": "", # scrollbar does not fade out when inactive "scrollbar_always_visible": "true" if (is_osx() or is_windows()) else "false", }, "rename": { "spaces": "false", "windows": "true", "ascii": "false", "move_art": "false", "move_art_overwrite": "false", "remove_empty_dirs": "false", }, "tagsfrompath": { "underscores": "false", "titlecase": "false", "split": "false",
class TWindows(TestCase): def test_on_first_map(self): w = Window() calls = [] def foo(*args): calls.append(args) on_first_map(w, foo, 1) w.show() self.assertEqual(calls, [(1, )]) on_first_map(w, foo, 2) self.assertEqual(calls, [(1, ), (2, )]) w.destroy() def test_ctr(self): Window().destroy() def test_instance_tracking(self): class SomeWindow(Window, InstanceTracker): def __init__(self): super().__init__() self._register_instance() self.assertFalse(SomeWindow.windows) other = Window() a = SomeWindow() self.assertTrue(a in SomeWindow.windows) self.assertTrue(a in SomeWindow.instances()) a.destroy() self.assertFalse(SomeWindow.instances()) self.assertTrue(SomeWindow.windows) other.destroy() self.assertFalse(SomeWindow.windows) def test_show_maybe(self): Window.prevent_inital_show(True) w = Window() w.show_maybe() self.assertFalse(w.get_visible()) Window.prevent_inital_show(False) w.show_maybe() self.assertTrue(w.get_visible()) w.destroy() def test_use_header_bar(self): w = Window(title="foo") w.use_header_bar() self.assertEqual(w.get_title(), "foo") w.destroy() w = Window() w.use_header_bar() self.assertEqual(w.get_title(), None) w.destroy() @skipIf(is_osx(), "crashes on 10.13") def test_toggle_fullscreen(self): w = Window(title="foo") w.toggle_fullscreen() with realized(w): w.toggle_fullscreen() w.toggle_fullscreen() w.destroy()
from xml.dom import minidom from urllib.parse import quote import queue from quodlibet import _, print_d from quodlibet.plugins.gui import UserInterfacePlugin from quodlibet.util import gi_require_versions, is_windows, is_osx from quodlibet.plugins.events import EventPlugin from quodlibet.plugins import (PluginImportException, PluginConfig, ConfProp, BoolConfProp, FloatConfProp, PluginNotSupportedError) try: gi_require_versions("WebKit2", ["4.0", "3.0"]) except ValueError as e: if is_windows() or is_osx(): raise PluginNotSupportedError raise PluginImportException("GObject Introspection: " + str(e)) from gi.repository import WebKit2, Gtk, GLib from quodlibet import qltk from quodlibet.util import escape, cached_property, connect_obj from quodlibet.qltk import Icons, Align from quodlibet.qltk.entry import UndoEntry from quodlibet.pattern import URLFromPattern from quodlibet.util.urllib import urlopen # for the mobile version USER_AGENT = ("Mozilla/5.0 (Linux; Android 5.1.1; Nexus 5 Build/LMY48B; wv) " "AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0"
# published by the Free Software Foundation from quodlibet import _ from quodlibet import app from quodlibet.plugins.events import EventPlugin from quodlibet.qltk import is_wayland, Icons from quodlibet.qltk.window import Window from quodlibet.util import (is_unity, is_osx, is_plasma, is_enlightenment, print_exc, print_w, print_d) from .prefs import Preferences from .util import pconfig from .systemtray import SystemTray if is_osx(): # Works, but not without problems: # https://github.com/quodlibet/quodlibet/issues/1870 # The dock menu is more useful so disable. from quodlibet.plugins import PluginNotSupportedError raise PluginNotSupportedError def get_indicator_impl(): """Returns a BaseIndicator implementation depending on the environ""" use_app_indicator = (is_unity() or is_wayland() or is_plasma() or is_enlightenment()) print_d("use app indicator: %s" % use_app_indicator) if not use_app_indicator:
# the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. from gi.repository import Gtk from senf import fsnative, getcwd from quodlibet.qltk.chooser import choose_files, get_current_dir, \ set_current_dir, choose_folders, create_chooser_filter, \ choose_target_file, choose_target_folder, with_response from quodlibet.qltk import gtk_version from quodlibet.util import is_osx from . import TestCase, skipIf @skipIf(gtk_version < (3, 16, 0) or is_osx(), "crashy on older gtk+ and macOS") class Tchooser(TestCase): def test_choose_files(self): w = Gtk.Window() with with_response(Gtk.ResponseType.CANCEL): assert choose_files(w, u"title", u"action") == [] def test_choose_folders(self): w = Gtk.Window() with with_response(Gtk.ResponseType.CANCEL): assert choose_folders(w, u"title", u"action") == [] def test_choose_filter(self): cf = create_chooser_filter(u"filter", ["*.txt"]) assert isinstance(cf, Gtk.FileFilter)
def test_constrains(self): if is_windows(): self.assertFalse(is_osx()) if is_osx(): self.assertFalse(is_windows())
import configparser as ConfigParser from os.path import dirname, basename from quodlibet.util.dprint import print_d, print_w from gi.repository import GObject from quodlibet.util.path import xdg_get_system_data_dirs import quodlibet from quodlibet import util from quodlibet.devices import _udev as udev from quodlibet.util.importhelper import load_dir_modules try: import dbus except ImportError: if not util.is_osx() and not util.is_windows(): print_w(_("Could not import %s, which is needed for " "device support.") % "dbus-python") dbus = None devices = [] def init_devices(): global devices load_pyc = util.is_windows() or util.is_osx() modules = load_dir_modules(dirname(__file__), package=__package__, load_compiled=load_pyc)
def _init_gtk(): """Call before using Gtk/Gdk""" import gi # pygiaio 3.14rev16 switched to fontconfig for PangoCairo. As this results # in 100% CPU under win7 revert it. Maybe we need to update the # cache in the windows installer for it to work... but for now revert. if is_windows(): os.environ['PANGOCAIRO_BACKEND'] = 'win32' # disable for consistency and trigger events seem a bit flaky here if is_osx(): os.environ["GTK_OVERLAY_SCROLLING"] = "0" # make sure GdkX11 doesn't get used under Windows if os.name == "nt": sys.modules["gi.repository.GdkX11"] = None try: # not sure if this is available under Windows gi.require_version("GdkX11", "3.0") from gi.repository import GdkX11 GdkX11 except (ValueError, ImportError): pass gi.require_version("Gtk", "3.0") gi.require_version("Gdk", "3.0") gi.require_version("Pango", "1.0") gi.require_version('Soup', '2.4') from gi.repository import Gtk, Gdk # PyGObject doesn't fail anymore when init fails, so do it ourself initialized, argv = Gtk.init_check(sys.argv) if not initialized: raise SystemExit("Gtk.init failed") sys.argv = list(argv) # add Gtk.TreePath.__getitem__/__len__ for PyGObject 3.2 try: Gtk.TreePath()[0] except TypeError: Gtk.TreePath.__getitem__ = lambda self, index: list(self)[index] Gtk.TreePath.__len__ = lambda self: self.get_depth() # GTK+ 3.4+ constants if not hasattr(Gdk, "BUTTON_PRIMARY"): Gdk.BUTTON_PRIMARY = 1 Gdk.BUTTON_MIDDLE = 2 Gdk.BUTTON_SECONDARY = 3 if not hasattr(Gdk, "EVENT_PROPAGATE"): Gdk.EVENT_PROPAGATE = 0 Gdk.EVENT_STOP = 1 # include our own icon theme directory theme = Gtk.IconTheme.get_default() theme_search_path = get_image_dir() assert os.path.exists(theme_search_path) theme.append_search_path(theme_search_path) # Force menu/button image related settings. We might show too many atm # but this makes sure we don't miss cases where we forgot to force them # per widget. # https://bugzilla.gnome.org/show_bug.cgi?id=708676 warnings.filterwarnings('ignore', '.*g_value_get_int.*', Warning) # some day... but not now warnings.filterwarnings( 'ignore', '.*Stock items are deprecated.*', Warning) warnings.filterwarnings( 'ignore', '.*:use-stock.*', Warning) warnings.filterwarnings( 'ignore', '.*The property GtkAlignment:[^\s]+ is deprecated.*', Warning) settings = Gtk.Settings.get_default() with warnings.catch_warnings(): warnings.simplefilter("ignore") settings.set_property("gtk-button-images", True) settings.set_property("gtk-menu-images", True) if hasattr(settings.props, "gtk_primary_button_warps_slider"): # https://bugzilla.gnome.org/show_bug.cgi?id=737843 settings.set_property("gtk-primary-button-warps-slider", True) # Make sure PyGObject includes support for foreign cairo structs try: gi.require_foreign("cairo") except AttributeError: # older pygobject pass except ImportError: print_e("PyGObject is missing cairo support") exit(1) # CSS overrides if os.name == "nt": # somehow borders are missing under Windows & Gtk+3.14 style_provider = Gtk.CssProvider() style_provider.load_from_data(""" .menu { border: 1px solid @borders; } """) Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ) if sys.platform == "darwin": # fix duplicated shadows for popups with Gtk+3.14 style_provider = Gtk.CssProvider() style_provider.load_from_data(""" GtkWindow { box-shadow: none; } .tooltip { border-radius: 0; padding: 0; } .tooltip.background { background-clip: border-box; } """) Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ) # https://bugzilla.gnome.org/show_bug.cgi?id=708676 warnings.filterwarnings('ignore', '.*g_value_get_int.*', Warning) # blacklist some modules, simply loading can cause segfaults sys.modules["gtk"] = None sys.modules["gpod"] = None sys.modules["gnome"] = None from quodlibet.qltk import pygobject_version, gtk_version MinVersions.GTK.check(gtk_version) MinVersions.PYGOBJECT.check(pygobject_version)
def run(window, before_quit=None): print_d("Entering quodlibet.main") from gi.repository import Gtk, Gdk, GLib from quodlibet._init import is_init assert is_init() def quit_gtk(window): if before_quit is not None: before_quit() # disable plugins import quodlibet.plugins quodlibet.plugins.quit() # for debug: this will list active copools from quodlibet.util import copool copool.pause_all() # See which browser windows are open and save their names # so we can restore them on start from quodlibet.qltk.browser import LibraryBrowser LibraryBrowser.save() # destroy all open windows so they hide immediately on close: # destroying all top level windows doesn't work (weird errors), # so we hide them all and only destroy our tracked instances # (browser windows, tag editors, pref window etc.) from quodlibet.qltk import Window for toplevel in Gtk.Window.list_toplevels(): toplevel.hide() for window in Window.windows: window.destroy() Gtk.main_quit() print_d("Quit GTK: done.") window.connect('destroy', quit_gtk) if sys.platform == "darwin": _main_setup_osx(window) if not window.show_maybe(): # if we don't show a window, startup isn't completed, so call manually Gdk.notify_startup_complete() from quodlibet.errorreport import faulthandling # gtk+ on osx is just too crashy if not is_osx(): try: faulthandling.enable(os.path.join(get_user_dir(), "faultdump")) except IOError: util.print_exc() else: GLib.idle_add(faulthandling.raise_and_clear_error) # set QUODLIBET_START_PERF to measure startup time until the # windows is first shown. if "QUODLIBET_START_PERF" in environ: window.connect("draw", Gtk.main_quit) Gtk.main() sys.exit() else: Gtk.main() print_d("Gtk.main() done.")
def _gtk_init(): """Call before using Gtk/Gdk""" import gi # disable for consistency and trigger events seem a bit flaky here if is_osx(): os.environ["GTK_OVERLAY_SCROLLING"] = "0" # make sure GdkX11 doesn't get used under Windows if os.name == "nt": sys.modules["gi.repository.GdkX11"] = None try: # not sure if this is available under Windows gi.require_version("GdkX11", "3.0") from gi.repository import GdkX11 GdkX11 except (ValueError, ImportError): pass gi.require_version("GLib", "2.0") gi.require_version("Gtk", "3.0") gi.require_version("Gdk", "3.0") gi.require_version("GObject", "2.0") gi.require_version("Pango", "1.0") gi.require_version("GdkPixbuf", "2.0") gi.require_version("Gio", "2.0") from gi.repository import Gtk, GObject, Gdk, GdkPixbuf # add Gtk.TreePath.__getitem__/__len__ for PyGObject 3.2 try: Gtk.TreePath()[0] except TypeError: Gtk.TreePath.__getitem__ = lambda self, index: list(self)[index] Gtk.TreePath.__len__ = lambda self: self.get_depth() # GTK+ 3.4+ constants if not hasattr(Gdk, "BUTTON_PRIMARY"): Gdk.BUTTON_PRIMARY = 1 Gdk.BUTTON_MIDDLE = 2 Gdk.BUTTON_SECONDARY = 3 if not hasattr(Gdk, "EVENT_PROPAGATE"): Gdk.EVENT_PROPAGATE = 0 Gdk.EVENT_STOP = 1 # On windows the default variants only do ANSI paths, so replace them. # In some typelibs they are replaced by default, in some don't.. if os.name == "nt": for name in ["new_from_file_at_scale", "new_from_file_at_size", "new_from_file"]: cls = GdkPixbuf.Pixbuf setattr(cls, name, getattr(cls, name + "_utf8", name)) # https://bugzilla.gnome.org/show_bug.cgi?id=670372 if not hasattr(GdkPixbuf.Pixbuf, "savev"): GdkPixbuf.Pixbuf.savev = GdkPixbuf.Pixbuf.save # Force menu/button image related settings. We might show too many atm # but this makes sure we don't miss cases where we forgot to force them # per widget. # https://bugzilla.gnome.org/show_bug.cgi?id=708676 warnings.filterwarnings('ignore', '.*g_value_get_int.*', Warning) # some day... but not now warnings.filterwarnings( 'ignore', '.*Stock items are deprecated.*', Warning) warnings.filterwarnings( 'ignore', '.*:use-stock.*', Warning) warnings.filterwarnings( 'ignore', '.*The property GtkAlignment:[^\s]+ is deprecated.*', Warning) # Newer glib is noisy regarding deprecated signals/properties # even with stable releases. if is_release(): warnings.filterwarnings( 'ignore', '.* It will be removed in a future version.', Warning) settings = Gtk.Settings.get_default() with warnings.catch_warnings(): warnings.simplefilter("ignore") settings.set_property("gtk-button-images", True) settings.set_property("gtk-menu-images", True) if hasattr(settings.props, "gtk_primary_button_warps_slider"): settings.set_property("gtk-primary-button-warps-slider", True) # Make sure PyGObject includes support for foreign cairo structs try: gi.require_foreign("cairo") except AttributeError: # older pygobject pass except ImportError: print_e("PyGObject is missing cairo support") exit(1) # CSS overrides if os.name == "nt": # somehow borders are missing under Windows & Gtk+3.14 style_provider = Gtk.CssProvider() style_provider.load_from_data(""" .menu { border: 1px solid @borders; } """) Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ) if sys.platform == "darwin": # fix duplicated shadows for popups with Gtk+3.14 style_provider = Gtk.CssProvider() style_provider.load_from_data(""" GtkWindow { box-shadow: none; } .tooltip { border-radius: 0; padding: 0; } .tooltip.background { background-clip: border-box; } """) Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ) # https://bugzilla.gnome.org/show_bug.cgi?id=708676 warnings.filterwarnings('ignore', '.*g_value_get_int.*', Warning) # blacklist some modules, simply loading can cause segfaults sys.modules["gtk"] = None sys.modules["gpod"] = None sys.modules["glib"] = None sys.modules["gobject"] = None sys.modules["gnome"] = None from quodlibet.qltk import pygobject_version if pygobject_version < (3, 9): GObject.threads_init()
# -*- coding: utf-8 -*- # Copyright 2016 Christoph Reiter # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. from tests import TestCase, skipUnless from gi.repository import Gio, Soup from quodlibet.util import is_osx, is_windows from quodlibet.compat import urlopen @skipUnless(is_osx() or is_windows(), "not on linux") class Thttps(TestCase): """For Windows/OSX to check if we can create a TLS connection using both openssl and whatever backend soup/gio uses. """ URI = "https://www.google.com" def test_urllib(self): if is_windows(): # FXIME return urlopen(self.URI).close() def test_gio(self): if is_osx():
def test_gio(self): if is_osx(): return client = Gio.SocketClient.new() client.set_tls(True) client.connect_to_uri(self.URI, 443, None).close()
def test_soup(self): if is_osx(): return session = Soup.Session.new() request = session.request_http("get", self.URI) request.send(None).close()
# it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. from quodlibet import _ from quodlibet import app from quodlibet.plugins.events import EventPlugin from quodlibet.qltk import Icons from quodlibet.util import (is_linux, is_osx, print_w, print_d) from quodlibet.util.environment import dbus_name_owned from .prefs import Preferences from .systemtray import SystemTray if is_osx(): # Works, but not without problems: # https://github.com/quodlibet/quodlibet/issues/1870 # The dock menu is more useful so disable. from quodlibet.plugins import PluginNotSupportedError raise PluginNotSupportedError def get_indicator_impl(): """Returns a BaseIndicator implementation depending on the environ""" use_app_indicator = ( is_linux() and dbus_name_owned("org.kde.StatusNotifierWatcher")) print_d("use app indicator: %s" % use_app_indicator) if not use_app_indicator:
# osx implementation might be buggy so let users disable it "disable_mmkeys": "false", # the UI language to use, empty means system default "language": "", # the pattern for the main window title "window_title_pattern": "~title~version~~people", # the format of the timestamps in DateColumn "datecolumn_timestamp_format": "", # scrollbar does not fade out when inactive "scrollbar_always_visible": "true" if (is_osx() or is_windows()) else "false", # Force fontconfig as PangoCairo backend "pangocairo_force_fontconfig": "false", # Whether the plugin window appears on top of others "plugins_window_on_top": "false", # search bar font style (#3647) "monospace_query": "false", # size to apply to query box, in any Pango CSS units (e.g. '100%', '1rem') "query_font_size": "100%", # Amount of colour to apply to validating text entries # (0.0 = no colour, 1.0 = full colour)
# This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. import os from tests import TestCase, skipUnless from gi.repository import Gio, Soup from quodlibet.util import is_osx, is_windows from quodlibet.compat import urlopen # this is fixed in 3.6, but we currently still use 3.5 bundles on travis if is_osx(): glib_net_fixed = "GTLS_SYSTEM_CA_FILE" in os.environ else: glib_net_fixed = True @skipUnless(is_osx() or is_windows()) class Thttps(TestCase): """For Windows/OSX to check if we can create a TLS connection using both openssl and whatever backend soup/gio uses. """ URI = "https://www.google.com" def test_urllib(self): if is_windows():
def test_is_accel_primary(self): e = Gdk.Event.new(Gdk.EventType.KEY_PRESS) e.keyval = Gdk.KEY_Return e.state = Gdk.ModifierType.CONTROL_MASK if not util.is_osx(): self.assertTrue(qltk.is_accel(e, "<Primary>Return"))
def _init_gtk(): """Call before using Gtk/Gdk""" import gi # pygiaio 3.14rev16 switched to fontconfig for PangoCairo. As this results # in 100% CPU under win7 revert it. Maybe we need to update the # cache in the windows installer for it to work... but for now revert. if is_windows(): environ['PANGOCAIRO_BACKEND'] = 'win32' environ["GTK_CSD"] = "0" # disable for consistency and trigger events seem a bit flaky here if is_osx(): environ["GTK_OVERLAY_SCROLLING"] = "0" # make sure GdkX11 doesn't get used under Windows if os.name == "nt": sys.modules["gi.repository.GdkX11"] = None try: # not sure if this is available under Windows gi.require_version("GdkX11", "3.0") from gi.repository import GdkX11 GdkX11 except (ValueError, ImportError): pass gi.require_version("Gtk", "3.0") gi.require_version("Gdk", "3.0") gi.require_version("Pango", "1.0") gi.require_version('Soup', '2.4') from gi.repository import Gtk, Soup from quodlibet.qltk import ThemeOverrider, gtk_version # Work around missing annotation in older libsoup (Ubuntu 14.04 at least) message = Soup.Message() try: message.set_request(None, Soup.MemoryUse.COPY, b"") except TypeError: orig = Soup.Message.set_request def new_set_request(self, content_type, req_use, req_body): return orig(self, content_type, req_use, req_body, len(req_body)) Soup.Message.set_request = new_set_request # PyGObject doesn't fail anymore when init fails, so do it ourself initialized, argv[:] = Gtk.init_check(argv) if not initialized: raise SystemExit("Gtk.init failed") # include our own icon theme directory theme = Gtk.IconTheme.get_default() theme_search_path = get_image_dir() assert os.path.exists(theme_search_path) theme.append_search_path(theme_search_path) # Force menu/button image related settings. We might show too many atm # but this makes sure we don't miss cases where we forgot to force them # per widget. # https://bugzilla.gnome.org/show_bug.cgi?id=708676 warnings.filterwarnings('ignore', '.*g_value_get_int.*', Warning) # some day... but not now warnings.filterwarnings( 'ignore', '.*Stock items are deprecated.*', Warning) warnings.filterwarnings( 'ignore', '.*:use-stock.*', Warning) warnings.filterwarnings( 'ignore', '.*The property GtkAlignment:[^\s]+ is deprecated.*', Warning) settings = Gtk.Settings.get_default() with warnings.catch_warnings(): warnings.simplefilter("ignore") settings.set_property("gtk-button-images", True) settings.set_property("gtk-menu-images", True) if hasattr(settings.props, "gtk_primary_button_warps_slider"): # https://bugzilla.gnome.org/show_bug.cgi?id=737843 settings.set_property("gtk-primary-button-warps-slider", True) # Make sure PyGObject includes support for foreign cairo structs try: gi.require_foreign("cairo") except AttributeError: # older pygobject pass except ImportError: print_e("PyGObject is missing cairo support") exit(1) css_override = ThemeOverrider() # CSS overrides if os.name == "nt": # somehow borders are missing under Windows & Gtk+3.14 style_provider = Gtk.CssProvider() style_provider.load_from_data(b""" .menu { border: 1px solid @borders; } """) css_override.register_provider("", style_provider) if sys.platform == "darwin": # fix duplicated shadows for popups with Gtk+3.14 style_provider = Gtk.CssProvider() style_provider.load_from_data(b""" GtkWindow { box-shadow: none; } .tooltip { border-radius: 0; padding: 0; } .tooltip.background { background-clip: border-box; } """) css_override.register_provider("", style_provider) if gtk_version[:2] >= (3, 20): # https://bugzilla.gnome.org/show_bug.cgi?id=761435 style_provider = Gtk.CssProvider() style_provider.load_from_data(b""" spinbutton, button { min-height: 1.8rem; } .view button { min-height: 2.0rem; } entry { min-height: 2.4rem; } entry.cell { min-height: 0; } """) css_override.register_provider("Adwaita", style_provider) css_override.register_provider("HighContrast", style_provider) # https://bugzilla.gnome.org/show_bug.cgi?id=708676 warnings.filterwarnings('ignore', '.*g_value_get_int.*', Warning) # blacklist some modules, simply loading can cause segfaults sys.modules["gtk"] = None sys.modules["gpod"] = None sys.modules["gnome"] = None from quodlibet.qltk import pygobject_version, gtk_version MinVersions.GTK.check(gtk_version) MinVersions.PYGOBJECT.check(pygobject_version)
def _init_gtk(): """Call before using Gtk/Gdk""" import gi # pygiaio 3.14rev16 switched to fontconfig for PangoCairo. As this results # in 100% CPU under win7 revert it. Maybe we need to update the # cache in the windows installer for it to work... but for now revert. if is_windows(): os.environ['PANGOCAIRO_BACKEND'] = 'win32' os.environ["GTK_CSD"] = "0" # disable for consistency and trigger events seem a bit flaky here if is_osx(): os.environ["GTK_OVERLAY_SCROLLING"] = "0" # make sure GdkX11 doesn't get used under Windows if os.name == "nt": sys.modules["gi.repository.GdkX11"] = None try: # not sure if this is available under Windows gi.require_version("GdkX11", "3.0") from gi.repository import GdkX11 GdkX11 except (ValueError, ImportError): pass gi.require_version("Gtk", "3.0") gi.require_version("Gdk", "3.0") gi.require_version("Pango", "1.0") gi.require_version('Soup', '2.4') from gi.repository import Gtk, Soup from quodlibet.qltk import ThemeOverrider, gtk_version # Work around missing annotation in older libsoup (Ubuntu 14.04 at least) message = Soup.Message() try: message.set_request(None, Soup.MemoryUse.COPY, b"") except TypeError: orig = Soup.Message.set_request def new_set_request(self, content_type, req_use, req_body): return orig(self, content_type, req_use, req_body, len(req_body)) Soup.Message.set_request = new_set_request # PyGObject doesn't fail anymore when init fails, so do it ourself initialized, argv = Gtk.init_check(sys.argv) if not initialized: raise SystemExit("Gtk.init failed") sys.argv = list(argv) # include our own icon theme directory theme = Gtk.IconTheme.get_default() theme_search_path = get_image_dir() assert os.path.exists(theme_search_path) theme.append_search_path(theme_search_path) # Force menu/button image related settings. We might show too many atm # but this makes sure we don't miss cases where we forgot to force them # per widget. # https://bugzilla.gnome.org/show_bug.cgi?id=708676 warnings.filterwarnings('ignore', '.*g_value_get_int.*', Warning) # some day... but not now warnings.filterwarnings( 'ignore', '.*Stock items are deprecated.*', Warning) warnings.filterwarnings( 'ignore', '.*:use-stock.*', Warning) warnings.filterwarnings( 'ignore', '.*The property GtkAlignment:[^\s]+ is deprecated.*', Warning) settings = Gtk.Settings.get_default() with warnings.catch_warnings(): warnings.simplefilter("ignore") settings.set_property("gtk-button-images", True) settings.set_property("gtk-menu-images", True) if hasattr(settings.props, "gtk_primary_button_warps_slider"): # https://bugzilla.gnome.org/show_bug.cgi?id=737843 settings.set_property("gtk-primary-button-warps-slider", True) # Make sure PyGObject includes support for foreign cairo structs try: gi.require_foreign("cairo") except AttributeError: # older pygobject pass except ImportError: print_e("PyGObject is missing cairo support") exit(1) css_override = ThemeOverrider() # CSS overrides if os.name == "nt": # somehow borders are missing under Windows & Gtk+3.14 style_provider = Gtk.CssProvider() style_provider.load_from_data(b""" .menu { border: 1px solid @borders; } """) css_override.register_provider("", style_provider) if sys.platform == "darwin": # fix duplicated shadows for popups with Gtk+3.14 style_provider = Gtk.CssProvider() style_provider.load_from_data(b""" GtkWindow { box-shadow: none; } .tooltip { border-radius: 0; padding: 0; } .tooltip.background { background-clip: border-box; } """) css_override.register_provider("", style_provider) if gtk_version[:2] >= (3, 20): # https://bugzilla.gnome.org/show_bug.cgi?id=761435 style_provider = Gtk.CssProvider() style_provider.load_from_data(b""" spinbutton, button { min-height: 1.8rem; } .view button { min-height: 2.0rem; } entry { min-height: 2.4rem; } """) css_override.register_provider("Adwaita", style_provider) css_override.register_provider("HighContrast", style_provider) # https://bugzilla.gnome.org/show_bug.cgi?id=708676 warnings.filterwarnings('ignore', '.*g_value_get_int.*', Warning) # blacklist some modules, simply loading can cause segfaults sys.modules["gtk"] = None sys.modules["gpod"] = None sys.modules["gnome"] = None from quodlibet.qltk import pygobject_version, gtk_version MinVersions.GTK.check(gtk_version) MinVersions.PYGOBJECT.check(pygobject_version)
def run(window, before_quit=None): print_d("Entering quodlibet.main") from gi.repository import Gtk, Gdk, GLib from quodlibet._init import is_init assert is_init() def quit_gtk(window): if before_quit is not None: before_quit() # disable plugins import quodlibet.plugins quodlibet.plugins.quit() # for debug: this will list active copools from quodlibet.util import copool copool.pause_all() # See which browser windows are open and save their names # so we can restore them on start from quodlibet.qltk.browser import LibraryBrowser LibraryBrowser.save() # destroy all open windows so they hide immediately on close: # destroying all top level windows doesn't work (weird errors), # so we hide them all and only destroy our tracked instances # (browser windows, tag editors, pref window etc.) from quodlibet.qltk import Window for toplevel in Gtk.Window.list_toplevels(): toplevel.hide() for window in Window.windows: window.destroy() Gtk.main_quit() print_d("Quit GTK: done.") window.connect('destroy', quit_gtk) if sys.platform == "darwin": _main_setup_osx(window) if not window.show_maybe(): # if we don't show a window, startup isn't completed, so call manually Gdk.notify_startup_complete() from quodlibet.errorreport import faulthandling # gtk+ on osx is just too crashy if not is_osx(): try: faulthandling.enable(os.path.join(get_user_dir(), "faultdump")) except IOError: util.print_exc() else: GLib.idle_add(faulthandling.raise_and_clear_error) # set QUODLIBET_START_PERF to measure startup time until the # windows is first shown. if "QUODLIBET_START_PERF" in environ: window.connect("draw", Gtk.main_quit) Gtk.main() sys.exit() else: Gtk.main() print_d("Gtk.main() done.")
class TIRFile(TestCase): def setUp(self): self.s = IRFile("http://foo.bar") def test_website(self): self.s["website"] = "abc" self.assertEqual(self.s.get("artist"), "abc") self.assertEqual(self.s("artist"), "abc") self.assertEqual(self.s.list("artist"), ["abc"]) def test_organisation(self): self.s["organization"] = "foo" self.assertEqual(self.s("title"), "foo") self.assertEqual(self.s.get("title"), "foo") def test_title_split_stream(self): self.assertFalse(self.s("artist")) self.s["title"] = "artist - title" self.s.multisong = False self.assertEqual(self.s("title"), "title") self.assertEqual(self.s.get("title"), "title") self.assertEqual(self.s("artist"), "artist") self.assertEqual(self.s.get("artist"), "artist") def test_title_split(self): self.assertTrue(self.s.multisong) self.s["title"] = "artist - title" self.assertEqual(self.s("title"), self.s["title"]) def test_format(self): self.assertEqual(self.s("~format"), self.s.format) self.s["audio-codec"] = "SomeCodec" self.assertTrue("SomeCodec" in self.s("~format")) self.assertTrue(self.s.format in self.s("~format")) def test_people(self): self.s["title"] = "artist - title" self.s.multisong = False self.assertEqual(self.s("~people"), "artist") self.assertEqual(self.s("~~people~foo"), "artist") def testcan_write(self): self.failUnless(self.s.can_change("title")) self.s.streamsong = True self.failIf(self.s.can_change("title")) def test_dump_to_file(self): self.s["title"] = "artist - title" self.s.multisong = False dump = self.s.to_dump() new = AudioFile() new.from_dump(dump) self.assertEqual(new["title"], "title") self.assertEqual(new["artist"], "artist") del self.s["title"] dump = self.s.to_dump() new = AudioFile() new.from_dump(dump) self.assertTrue("title" not in new) self.assertTrue("artist" not in new) @skipIf(is_windows() or is_osx(), "Don't need to test all the time") def test_download_tags(self): self.received = [] # TODO: parameterise this, spin up a local HTTP server, inject this. url = STATION_LIST_URL def cb(data): assert data self.received += data ret = list(download_taglist(cb, None)) run_loop() assert all(ret) assert self.received, "No stations received from %s" % url assert len(self.received) > 100