def restore(self): text = config.gettext("browsers", "query_text") entry = self.__search entry.set_text(text) # update_filter expects a parsable query if Query.is_parsable(text): self.__update_filter(entry, text, scroll_up=False, restore=True) keys = config.gettext("browsers", "albums").split("\n") # FIXME: If albums is "" then it could be either all albums or # no albums. If it's "" and some other stuff, assume no albums, # otherwise all albums. self.__inhibit() if keys == [""]: self.view.set_cursor((0,)) else: def select_fun(row): album = row[0].album if not album: # all return False return album.str_key in keys self.view.select_by_func(select_fun) self.__uninhibit()
def restore(self): try: text = config.gettext("browsers", "query_text") except config.Error: pass else: self._set_text(text) selected = config.gettext("browsers", "pane_selection") if not selected: return for pane, string in zip(self._panes, selected.split(u"\n")): pane.parse_restore_string(string)
def _apply_value(self, model, iter_, cell, stamp): if not stamp: cell.set_property('text', _("Never")) else: try: date = datetime.datetime.fromtimestamp(stamp).date() except (OverflowError, ValueError, OSError): text = u"" else: format_setting = config.gettext("settings", "datecolumn_timestamp_format") # use format configured in Advanced Preferences if format_setting: format_ = format_setting # use default behaviour-format else: today = datetime.datetime.now().date() days = (today - date).days if days == 0: format_ = "%X" elif days < 7: format_ = "%A" else: format_ = "%x" stamp = time.localtime(stamp) text = time.strftime(format_, stamp) cell.set_property('text', text)
def __init__(self, tag, value): super(SplitValues, self).__init__( label=_("Split into _Multiple Values"), use_underline=True) self.set_image(Gtk.Image.new_from_icon_name( Icons.EDIT_FIND_REPLACE, Gtk.IconSize.MENU)) spls = config.gettext("editing", "split_on").split() self.set_sensitive(len(split_value(value, spls)) > 1)
def __init__(self, tag, value): super(SplitTitle, self).__init__( label=_("Split _Version out of Title"), use_underline=True) self.set_image(Gtk.Image.new_from_icon_name( Icons.EDIT_FIND_REPLACE, Gtk.IconSize.MENU)) spls = config.gettext("editing", "split_on").split() self.set_sensitive(bool(split_title(value, spls)[1]))
def __init__(self, player): super(VolumeMenu, self).__init__() # ubuntu 12.04.. if hasattr(player, "bind_property"): # Translators: player state, no action item = Gtk.CheckMenuItem(label=_("_Mute"), use_underline=True) player.bind_property("mute", item, "active", GObject.BindingFlags.BIDIRECTIONAL) self.append(item) item.show() item = Gtk.MenuItem(label=_("_Replay Gain Mode"), use_underline=True) self.append(item) item.show() # Set replaygain mode as saved in configuration replaygain_mode = config.gettext("player", "replaygain_mode", "auto") self.__set_mode(player, replaygain_mode) rg = Gtk.Menu() rg.show() item.set_submenu(rg) item = None for mode, title, profile in self.__modes: item = RadioMenuItem(group=item, label=title, use_underline=True) rg.append(item) item.connect("toggled", self.__changed, player, mode) if replaygain_mode == mode: item.set_active(True) item.show()
def refresh_panes(self): self.multi_paned.destroy() # Fill in the pane list. The last pane reports back to us. self._panes = [self] for header in reversed(get_headers()): pane = Pane(self._library, header, self._panes[0]) pane.connect('row-activated', lambda *x: self.songs_activated()) self._panes.insert(0, pane) self._panes.pop() # remove self # Put the panes in scrollable windows sws = [] for pane in self._panes: sw = ScrolledWindow() sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) sw.set_shadow_type(Gtk.ShadowType.IN) sw.add(pane) sws.append(sw) self.multi_paned.set_widgets(sws) self.multi_paned.show_all() self.main_box.pack1(self.multi_paned.get_paned(), True, False) self.__star = {} for p in self._panes: tags = [t for t in p.tags if not t.startswith("~#")] self.__star.update(dict.fromkeys(tags)) self.set_column_mode(config.gettext("browsers", "pane_mode"))
def _init_gettext(no_translations=False): """Call before using gettext helpers""" if no_translations: language = u"C" else: language = config.gettext("settings", "language") if not language: language = None i18n.init(language) # Use the locale dir in ../build/share/locale if there is one base_dir = get_base_dir() localedir = os.path.dirname(base_dir) localedir = os.path.join(localedir, "build", "share", "locale") if not os.path.isdir(localedir) and os.name == "nt": # py2exe case localedir = os.path.join( base_dir, "..", "..", "share", "locale") i18n.register_translation("quodlibet", localedir) debug_text = environ.get("QUODLIBET_TEST_TRANS") if debug_text is not None: i18n.set_debug_text(fsn2text(debug_text))
def background_filter(): bg = config.gettext("browsers", "background") if not bg: return try: return Query(bg, SongList.star).search except Query.error: pass
def __update_title(self, player): song = player.info title = "Quod Libet" if song: tag = config.gettext("settings", "window_title_pattern") if tag: title = song.comma(tag) + " - " + title self.set_title(title)
def PluginPreferences(self, *args): current = config.gettext("settings", "language") if not current: current = None combo = Gtk.ComboBox() model = ObjectStore() combo.set_model(model) for lang_id in ([None] + sorted(get_available_languages("quodlibet"))): iter_ = model.append(row=[lang_id]) if lang_id == current: combo.set_active_iter(iter_) def cell_func(combo, render, model, iter_, *args): value = model.get_value(iter_) if value is None: text = escape(_("System Default")) else: if value == u"C": value = u"en" text = "%s <span weight='light'>(%s)</span>" % ( escape(value), escape(iso639.translate(value.split("_", 1)[0]))) render.set_property("markup", text) render = Gtk.CellRendererText() render.props.ellipsize = Pango.EllipsizeMode.END combo.pack_start(render, True) combo.set_cell_data_func(render, cell_func) def on_combo_changed(combo): new_language = model.get_value(combo.get_active_iter()) if new_language is None: new_language = u"" config.settext("settings", "language", new_language) combo.connect("changed", on_combo_changed) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12) box.pack_start(combo, False, False, 0) box.pack_start( Gtk.Label( label=_( "A restart is required for any changes to take effect"), wrap=True, xalign=0), False, False, 0) return box
def background_filter(): """Returns a filter function for AudioFile or None if nothing should be filtered. The filter is meant to be used globally to hide songs from the main library. Returns: function or None """ bg = config.gettext("browsers", "background") if not bg: return query = Query(bg, SongList.star) if query.is_parsable: return query.search
def __init__(self, browser): super(ColumnModes, self).__init__(spacing=6) self.browser = browser self.buttons = [] group = None for mode in [COLUMN_MODE_SMALL, COLUMN_MODE_WIDE, COLUMN_MODE_COLUMNAR]: group = Gtk.RadioButton(group=group, label=_(mode)) if mode == config.gettext("browsers", "pane_mode"): group.set_active(True) self.pack_start(group, False, True, 0) self.buttons.append(group) # Connect to signal after the correct radio button has been # selected for button in self.buttons: button.connect('toggled', self.toggled)
def _config(section, option, label, tooltip, getter): def on_changed(entry, *args): config.settext(section, option, gdecode(entry.get_text())) entry = UndoEntry() entry.set_tooltip_text(tooltip) entry.set_text(config.gettext(section, option)) entry.connect("changed", on_changed) def on_reverted(*args): config.reset(section, option) entry.set_text(config.gettext(section, option)) revert = Gtk.Button() revert.add(Gtk.Image.new_from_icon_name( Icons.DOCUMENT_REVERT, Gtk.IconSize.BUTTON)) revert.connect("clicked", on_reverted) return (Gtk.Label(label=label), entry, revert)
def restore(self): text = config.gettext("browsers", "query_text") self.__searchbar.set_text(text) if Query.is_parsable(text): self.__filter_changed(self.__searchbar, text, restore=True) keys = config.get("browsers", "radio").splitlines() def select_func(row): return row[self.TYPE] != self.TYPE_SEP and row[self.KEY] in keys self.__inhibit() view = self.view if not view.select_by_func(select_func): for row in view.get_model(): if row[self.TYPE] == self.TYPE_FAV: view.set_cursor(row.path) break self.__uninhibit()
def activated(self, tag, value): spls = config.gettext("editing", "split_on").split() artist, others = split_people(value, spls) return [(tag, artist)] + [(self.needs[0], o) for o in others]
def activated(self, tag, value): spls = config.gettext("editing", "split_on").split() title, versions = split_title(value, spls) return [(tag, title)] + [("version", v) for v in versions]
def __call__(self, key, default: Any = u"", connector=" - ", joiner=', '): """Return the value(s) for a key, synthesizing if necessary. Multiple values for a key are delimited by newlines. A default value may be given (like `dict.get`); the default default is an empty unicode string (even if the tag is numeric). If a tied tag ('a~b') is requested, the `connector` keyword argument may be used to specify what it is tied with. In case the tied tag contains numeric and file path tags, the result will still be a unicode string. The `joiner` keyword specifies how multiple *values* will be joined within that tied tag output, e.g. ~people~title = "Kanye West, Jay Z - New Day" For details on tied tags, see the documentation for `util.tagsplit`. """ if key[:1] == "~": key = key[1:] if "~" in key: real_key = "~" + key values = [] sub_tags = util.tagsplit(real_key) # If it's genuinely a tied tag (not ~~people etc), we want # to delimit the multi-values separately from the tying j = joiner if len(sub_tags) > 1 else "\n" for t in sub_tags: vs = [decode_value(real_key, v) for v in (self.list(t))] v = j.join(vs) if v: values.append(v) return connector.join(values) or default elif key == "#track": try: return int(self["tracknumber"].split("/")[0]) except (ValueError, TypeError, KeyError): return default elif key == "#disc": try: return int(self["discnumber"].split("/")[0]) except (ValueError, TypeError, KeyError): return default elif key == "length": length = self.get("~#length") if length is None: return default else: return util.format_time_display(length) elif key == "#rating": return dict.get(self, "~" + key, config.RATINGS.default) elif key == "rating": return util.format_rating(self("~#rating")) elif key == "people": return "\n".join(self.list_unique(PEOPLE)) or default elif key == "people:real": # Issue 1034: Allow removal of V.A. if others exist. unique = self.list_unique(PEOPLE) # Order is important, for (unlikely case): multiple removals for val in VARIOUS_ARTISTS_VALUES: if len(unique) > 1 and val in unique: unique.remove(val) return "\n".join(unique) or default elif key == "people:roles": return (self._role_call("performer", PEOPLE) or default) elif key == "peoplesort": return ("\n".join(self.list_unique(PEOPLE_SORT)) or self("~people", default, connector)) elif key == "peoplesort:roles": # Ignores non-sort tags if there are any sort tags (e.g. just # returns "B" for {artist=A, performersort=B}). # TODO: figure out the "correct" behavior for mixed sort tags return (self._role_call("performersort", PEOPLE_SORT) or self("~peoplesort", default, connector)) elif key in ("performers", "performer"): return self._prefixvalue("performer") or default elif key in ("performerssort", "performersort"): return (self._prefixvalue("performersort") or self("~" + key[-4:], default, connector)) elif key in ("performers:roles", "performer:roles"): return (self._role_call("performer") or default) elif key in ("performerssort:roles", "performersort:roles"): return (self._role_call("performersort") or self( "~" + key.replace("sort", ""), default, connector)) elif key == "basename": return os.path.basename(self["~filename"]) or self["~filename"] elif key == "dirname": return os.path.dirname(self["~filename"]) or self["~filename"] elif key == "uri": try: return self["~uri"] except KeyError: return fsn2uri(self["~filename"]) elif key == "format": return self.get("~format", str(self.format)) elif key == "codec": codec = self.get("~codec") if codec is None: return self("~format") return codec elif key == "encoding": encoding = "\n".join( part for part in [self.get("~encoding"), self.get("encodedby")] if part) return encoding or default elif key == "language": codes = self.list("language") if not codes: return default return u"\n".join(iso639.translate(c) or c for c in codes) elif key == "bitrate": return util.format_bitrate(self("~#bitrate")) elif key == "#date": date = self.get("date") if date is None: return default return util.date_key(date) elif key == "year": return self.get("date", default)[:4] elif key == "#year": try: return int(self.get("date", default)[:4]) except (ValueError, TypeError, KeyError): return default elif key == "originalyear": return self.get("originaldate", default)[:4] elif key == "#originalyear": try: return int(self.get("originaldate", default)[:4]) except (ValueError, TypeError, KeyError): return default elif key == "#tracks": try: return int(self["tracknumber"].split("/")[1]) except (ValueError, IndexError, TypeError, KeyError): return default elif key == "#discs": try: return int(self["discnumber"].split("/")[1]) except (ValueError, IndexError, TypeError, KeyError): return default elif key == "lyrics": # First, try the embedded lyrics. try: return self["lyrics"] except KeyError: pass try: return self["unsyncedlyrics"] except KeyError: pass # If there are no embedded lyrics, try to read them from # the external file. lyric_filename = self.lyric_filename if not lyric_filename: return default try: with open(lyric_filename, "rb") as fileobj: print_d(f"Reading lyrics from {lyric_filename!r}") text = fileobj.read().decode("utf-8", "replace") # try to skip binary files if "\0" in text: return default return text except (EnvironmentError, UnicodeDecodeError): return default elif key == "filesize": return util.format_size(self("~#filesize", 0)) elif key == "playlists": # TODO: avoid static dependency here... somehow from quodlibet import app lib = app.library if not lib: return "" playlists = lib.playlists.playlists_featuring(self) return "\n".join(s.name for s in playlists) or default elif key.startswith("#replaygain_"): try: val = self.get(key[1:], default) return round(float(val.split(" ")[0]), 2) except (ValueError, TypeError, AttributeError): return default elif key[:1] == "#": key = "~" + key if key in self: return self[key] elif key in NUMERIC_ZERO_DEFAULT: return 0 else: try: val = self[key[2:]] except KeyError: return default try: return int(val) except ValueError: try: return float(val) except ValueError: return default else: return dict.get(self, "~" + key, default) elif key == "title": title = dict.get(self, "title") if title is None: # build a title with missing_title_template option unknown_track_template = _( config.gettext("browsers", "missing_title_template")) from quodlibet.pattern import Pattern try: pattern = Pattern(unknown_track_template) except ValueError: title = decode_value("~basename", self("~basename")) else: title = pattern % self return title elif key in SORT_TO_TAG: try: return self[key] except KeyError: key = SORT_TO_TAG[key] return dict.get(self, key, default)
def activated(self, tag, value): spls = config.gettext("editing", "split_on").split() return [(tag, v) for v in split_value(value, spls)]
def format(song: AudioFile) -> List[Tuple[Text, Text]]: fmt = config.gettext("settings", "datecolumn_timestamp_format") date_str = format_date(song(cat), fmt) return [(date_str, date_str)]
def restore(self): text = config.gettext("browsers", "query_text") self.__searchbar.set_text(text) self.__query_changed(None, text, restore=True)
def filter(self, tag, value): spls = config.gettext("editing", "split_on") spls = spls.split() return "\n".join(split_value(value, spls))
def restore(self): text = config.gettext("browsers", "query_text") self._set_text(text)
def on_reverted(*args): config.reset(section, option) entry.set_text(config.gettext(section, option))
def activated(self, tag, value): tag_spls = config.gettext("editing", "split_on").split() sub_spls = config.gettext("editing", "sub_split_on").split() artist, others = split_people(value, tag_spls, sub_spls) return [(tag, artist)] + [(self.needs[0], o) for o in others]
def activated(self, tag, value): tag_spls = config.gettext("editing", "split_on").split() sub_spls = config.gettext("editing", "sub_split_on").split() title, versions = split_title(value, tag_spls, sub_spls) return [(tag, title)] + [("version", v) for v in versions]