def _album(self, song, box): if "album" not in song: return text = ["<span size='x-large'><i>%s</i></span>" % util.escape(song.comma("album"))] secondary = [] if "discnumber" in song: secondary.append(_("Disc %s") % song["discnumber"]) if "discsubtitle" in song: secondary.append("<i>%s</i>" % util.escape(song.comma("discsubtitle"))) if "tracknumber" in song: secondary.append(_("Track %s") % song["tracknumber"]) if secondary: text.append(" - ".join(secondary)) if "date" in song: text.append(util.escape(song.comma("date"))) if "organization" in song or "labelid" in song: t = util.escape(song.comma("~organization~labelid")) text.append(t) if "producer" in song: text.append(_("Produced by %s") % ( util.escape(song.comma("producer")))) w = Label(markup="\n".join(text), ellipsize=True) hb = Gtk.HBox(spacing=12) hb.pack_start(w, True, True, 0) box.pack_start(Frame(tag("album"), hb), False, False, 0) cover = ReactiveCoverImage(song=song) hb.pack_start(cover, False, True, 0)
def _title(self, songs, box): song = songs[0] self.title = text = song["album"] markup = "<i>%s</i>" % util.escape(text) if "date" in song: markup += " <small>(%s)</small>" % util.escape(song("~year")) box.pack_start(TitleLabel(markup, is_markup=True), False, False, 0)
def __save_files(self, parent, model, library): win = WritingWindow(parent, len(model)) was_changed = [] for row in model: song = row[0] track = row[2] if song.get("tracknumber") == track: win.step() continue if not song.valid() and not qltk.ConfirmAction( win, _("Tag may not be accurate"), _("<b>%s</b> changed while the program was running. " "Saving without refreshing your library may " "overwrite other changes to the song.\n\n" "Save this song anyway?") %( util.escape(util.fsdecode(song("~basename")))) ).run(): break song["tracknumber"] = track try: song.write() except: util.print_exc() qltk.ErrorMessage( win, _("Unable to save song"), _("Saving <b>%s</b> failed. The file may be " "read-only, corrupted, or you do not have " "permission to edit it.")%( util.escape(util.fsdecode(song('~basename'))))).run() library.reload(song, changed=was_changed) break was_changed.append(song) if win.step(): break library.changed(was_changed) win.destroy()
def _description(self, songs, box): text = [] cur_disc = songs[0]("~#disc", 1) - 1 cur_part = None cur_track = songs[0]("~#track", 1) - 1 for song in songs: track = song("~#track", 0) disc = song("~#disc", 0) part = song.get("part") if disc != cur_disc: if cur_disc: text.append("") cur_track = song("~#track", 1) - 1 cur_part = None cur_disc = disc if disc: text.append("%s" % (_("Disc %s") % disc)) if part != cur_part: ts = " " * bool(disc) cur_part = part if part: text.append("%s%s" % (ts, util.escape(part))) cur_track += 1 ts = " " * (bool(disc) + bool(part)) while cur_track < track: text.append("{ts}{cur: >2}. {text}".format( ts=ts, cur=cur_track, text=_("Track unavailable"))) cur_track += 1 markup = util.escape(song.comma("~title~version")) text.append("{ts}{cur: >2}. <i>{text}</i>".format( ts=ts, cur=track, text=markup)) l = Label(markup="\n".join(text), ellipsize=True) box.pack_start(Frame(_("Track List"), l), False, False, 0)
def _additional(self, song, box): if "website" not in song and "comment" not in song: return data = [] if "comment" in song: comments = song.list("comment") markups = ["<i>%s</i>" % c for c in comments] data.append(("comment", markups)) if "website" in song: markups = ["<a href=\"%(url)s\">%(text)s</a>" % {"text": util.escape(website), "url": util.escape(website)} for website in song.list("website")] data.append(("website", markups)) table = Table(1) for i, (key, markups) in enumerate(data): title = readable(key, plural=len(markups) > 1) lab = Label(markup=util.capitalize(util.escape(title) + ":")) table.attach(lab, 0, 1, i, i + 1, xoptions=Gtk.AttachOptions.FILL) lab = Label(markup="\n".join(markups), ellipsize=True) table.attach(lab, 1, 2, i, i + 1) box.pack_start(Frame(_("Additional"), table), False, False, 0)
def _description(self, songs, box): text = [] cur_disc = songs[0]("~#disc", 1) - 1 cur_part = None cur_track = songs[0]("~#track", 1) - 1 for song in songs: track = song("~#track", 0) disc = song("~#disc", 0) part = song.get("part") if disc != cur_disc: if cur_disc: text.append("") cur_track = song("~#track", 1) - 1 cur_part = None cur_disc = disc if disc: text.append("<b>%s</b>" % (_("Disc %s") % disc)) if part != cur_part: ts = " " * bool(disc) cur_part = part if part: text.append("%s<b>%s</b>" % (ts, util.escape(part))) cur_track += 1 ts = " " * (bool(disc) + bool(part)) while cur_track < track: text.append("%s<b>%d.</b> <i>%s</i>" % ( ts, cur_track, _("Track unavailable"))) cur_track += 1 text.append("%s<b>%d.</b> %s" % ( ts, track, util.escape(song.comma("~title~version")))) l = Label() l.set_markup("\n".join(text)) l.set_ellipsize(Pango.EllipsizeMode.END) box.pack_start(Frame(_("Track List"), l), False, False, 0)
def cell_data(col, render, model, iter): device = model[iter][0] if device.is_connected(): render.markup = "<b>%s</b>" % util.escape(device['name']) else: render.markup = util.escape(device['name']) render.set_property('markup', render.markup)
def _album(self, song, box): if "album" not in song: return w = Label("") text = [] text.append("<i>%s</i>" % util.escape(song.comma("album"))) if "date" in song: text[-1] += " (%s)" % util.escape(song.comma("date")) secondary = [] if "discnumber" in song: secondary.append(_("Disc %s") % song["discnumber"]) if "discsubtitle" in song: secondary.append("<i>%s</i>" % util.escape(song.comma("discsubtitle"))) if "tracknumber" in song: secondary.append(_("Track %s") % song["tracknumber"]) if secondary: text.append(" - ".join(secondary)) if "organization" in song or "labelid" in song: t = util.escape(song.comma("~organization~labelid")) text.append(t) if "producer" in song: text.append("Produced by %s" %( util.escape(song.comma("producer")))) w.set_markup("\n".join(text)) w.set_ellipsize(pango.ELLIPSIZE_END) hb = gtk.HBox(spacing=12) cover = CoverImage(song=song) if cover: hb.pack_start(cover, expand=False) else: cover.destroy() hb.pack_start(w) box.pack_start(Frame(tag("album"), hb), expand=False, fill=False)
def website(self): """Look for a URL in the audio metadata, or a Google search if no URL can be found.""" if "website" in self: return self.list("website")[0] for cont in self.list("contact") + self.list("comment"): c = cont.lower() if (c.startswith("http://") or c.startswith("https://") or c.startswith("www.")): return cont elif c.startswith("//www."): return "http:" + cont else: text = "http://www.google.com/search?q=" esc = lambda c: ord(c) > 127 and '%%%x' % ord(c) or c if "labelid" in self: text += ''.join(map(esc, self["labelid"])) else: artist = util.escape("+".join(self("artist").split())) album = util.escape("+".join(self("album").split())) artist = encode(artist) album = encode(album) artist = "%22" + ''.join(map(esc, artist)) + "%22" album = "%22" + ''.join(map(esc, album)) + "%22" text += artist + "+" + album text += "&ie=UTF8" return text
def __init__(self, parent, device): super(DeviceProperties, self).__init__( title=_("Device Properties"), transient_for=qltk.get_top_parent(parent)) self.add_icon_button(_("_Close"), Icons.WINDOW_CLOSE, Gtk.ResponseType.CLOSE) self.set_default_size(400, -1) self.connect('response', self.__close) table = Gtk.Table() table.set_border_width(8) table.set_row_spacings(8) table.set_col_spacings(8) self.vbox.pack_start(table, False, True, 0) props = [] props.append((_("Device:"), device.block_device, None)) mountpoint = util.escape( device.mountpoint or ("<i>%s</i>" % _("Not mounted"))) props.append((_("Mount point:"), mountpoint, None)) props.append((None, None, None)) entry = Gtk.Entry() entry.set_text(device['name']) props.append((_("_Name:"), entry, 'name')) y = 0 for title, value, key in props + device.Properties(): if title is None: table.attach(Gtk.HSeparator(), 0, 2, y, y + 1) else: if key and isinstance(value, Gtk.CheckButton): value.set_label(title) value.set_use_underline(True) value.connect('toggled', self.__changed, key, device) table.attach(value, 0, 2, y, y + 1, xoptions=Gtk.AttachOptions.FILL) else: label = Gtk.Label() label.set_markup("<b>%s</b>" % util.escape(title)) label.set_alignment(0.0, 0.5) table.attach(label, 0, 1, y, y + 1, xoptions=Gtk.AttachOptions.FILL) if key and isinstance(value, Gtk.Widget): widget = value label.set_mnemonic_widget(widget) label.set_use_underline(True) widget.connect('changed', self.__changed, key, device) else: widget = Gtk.Label(label=value) widget.set_use_markup(True) widget.set_selectable(True) widget.set_alignment(0.0, 0.5) table.attach(widget, 1, 2, y, y + 1) y += 1 self.get_child().show_all()
def _album(self, songs, box): text = [] discs = {} for song in songs: try: discs[song("~#disc")] = int( song["tracknumber"].split("/")[1]) except (AttributeError, ValueError, IndexError, KeyError): discs[song("~#disc")] = max([ song("~#track", discs.get(song("~#disc"), 0))]) tracks = sum(discs.values()) discs = len(discs) length = sum([song.get("~#length", 0) for song in songs]) if tracks == 0 or tracks < len(songs): tracks = len(songs) parts = [] if discs > 1: parts.append( ngettext("%d disc", "%d discs", discs) % discs) parts.append( ngettext("%d track", "%d tracks", tracks) % tracks) if tracks != len(songs): parts.append(ngettext("%d selected", "%d selected", len(songs)) % len(songs)) text.append(", ".join(parts)) text.append(util.format_time_preferred(length)) if "location" in song: text.append(util.escape(song["location"])) if "organization" in song or "labelid" in song: t = util.escape(song.comma("~organization~labelid")) text.append(t) if "producer" in song: text.append(_("Produced by %s") % ( util.escape(song.comma("producer")))) w = Label("") w.set_ellipsize(Pango.EllipsizeMode.END) w.set_markup("\n".join(text)) hb = Gtk.HBox(spacing=12) cover = CoverImage() cover.set_property('no-show-all', True) hb.pack_start(cover, False, True, 0) def show_cover(cover, success): if success: cover.show() cover.disconnect(signal_id) signal_id = cover.connect('cover-visible', show_cover) cover.set_song(song) hb.pack_start(w, True, True, 0) box.pack_start(hb, False, False, 0)
def cdf(column, cell, model, iter, data): row = model[iter] filename = fsn2text(unexpand(row[0])) function = row[1] line = row[2] cell.set_property( "markup", "<b>%s</b> line %d\n\t%s" % ( util.escape(function), line, util.escape(filename)))
def __init__(self, parent, player_error): add_full_stop = lambda s: s and (s.rstrip(".") + ".") description = add_full_stop(util.escape(player_error.short_desc)) details = add_full_stop(util.escape(player_error.long_desc or "")) if details: description += " " + details super(PlaybackErrorDialog, self).__init__(parent, _("Playback Error"), description)
def __cdf(self, column, cell, model, iter, data): row = model[iter] obj = row[0] obj_name = util.escape(obj.name) obj_description = util.escape(str(obj)) markup = '<b>%s</b>\n%s' % (obj_name, obj_description) cell.markup = markup cell.set_property('markup', markup)
def PluginPreferences(self, parent): outer_vb = Gtk.VBox(spacing=12) vb = Gtk.VBox(spacing=12) # Playing hb = Gtk.HBox(spacing=6) entry = UndoEntry() entry.set_text(self.config_get(self.CFG_PAT_PLAYING, self.DEFAULT_PAT)) entry.connect('changed', self.config_entry_changed, self.CFG_PAT_PLAYING) lbl = Gtk.Label(label=_("Playing:")) entry.set_tooltip_markup(_("Status text when a song is started. " "Accepts QL Patterns e.g. %s") % util.monospace( util.escape("<~artist~title>"))) lbl.set_mnemonic_widget(entry) hb.pack_start(lbl, False, True, 0) hb.pack_start(entry, True, True, 0) vb.pack_start(hb, True, True, 0) # Paused hb = Gtk.HBox(spacing=6) entry = UndoEntry() entry.set_text(self.config_get(self.CFG_PAT_PAUSED, self.DEFAULT_PAT_PAUSED)) entry.connect('changed', self.config_entry_changed, self.CFG_PAT_PAUSED) lbl = Gtk.Label(label=_("Paused:")) entry.set_tooltip_markup(_("Status text when a song is paused. " "Accepts QL Patterns e.g. %s") % util.monospace( util.escape("<~artist~title>"))) lbl.set_mnemonic_widget(entry) hb.pack_start(lbl, False, True, 0) hb.pack_start(entry, True, True, 0) vb.pack_start(hb, True, True, 0) # No Song hb = Gtk.HBox(spacing=6) entry = UndoEntry() entry.set_text(self.config_get(self.CFG_STATUS_SONGLESS, "")) entry.connect('changed', self.config_entry_changed, self.CFG_STATUS_SONGLESS) entry.set_tooltip_text( _("Plain text for status when there is no current song")) lbl = Gtk.Label(label=_("No song:")) lbl.set_mnemonic_widget(entry) hb.pack_start(lbl, False, True, 0) hb.pack_start(entry, True, True, 0) vb.pack_start(hb, True, True, 0) # Frame frame = qltk.Frame(_("Status Patterns"), child=vb) outer_vb.pack_start(frame, False, True, 0) return outer_vb
def _title(self, song, box): l = Label() text = "<big><b>%s</b></big>" % util.escape(song.comma("title")) if "version" in song: text += "\n" + util.escape(song.comma("version")) l.set_markup(text) l.set_ellipsize(Pango.EllipsizeMode.END) box.pack_start(l, False, False, 0) self.title = song.comma("title")
def _title(self, song, box): l = Label() text = "<big><b>%s</b></big>" % util.escape(song.comma("title")) if "version" in song: text += "\n" + util.escape(song.comma("version")) l.set_markup(text) l.set_ellipsize(pango.ELLIPSIZE_END) box.pack_start(l, expand=False, fill=False) self.title = song.comma("title")
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)
def get_markup(self): """Returns pango markup for displaying""" if self.shared and self.complete: return util.escape(self.text) elif self.shared: return "\n".join( ["%s<i> (%s)</i>" % (util.escape(s), util.escape(self._paren())) for s in self.text.split("\n")] ) else: return "<i>(%s)</i>" % util.escape(self._paren())
def __init__(self, parent, device): super(DeviceProperties, self).__init__( _("Device Properties"), qltk.get_top_parent(parent), buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)) self.set_default_size(400, -1) self.connect('response', self.__close) table = gtk.Table() table.set_border_width(8) table.set_row_spacings(8) table.set_col_spacings(8) self.vbox.pack_start(table, expand=False) props = [] props.append((_("Device:"), device.dev, None)) mountpoint = util.escape( device.mountpoint or ("<i>%s</i>" % _("Not mounted"))) props.append((_("Mount Point:"), mountpoint, None)) props.append((None, None, None)) entry = gtk.Entry() entry.set_text(device['name']) props.append((_("_Name:"), entry, 'name')) y = 0 for title, value, key in props + device.Properties(): if title == None: table.attach(gtk.HSeparator(), 0, 2, y, y + 1) else: if key and isinstance(value, gtk.CheckButton): value.set_label(title) value.set_use_underline(True) value.connect('toggled', self.__changed, key, device) table.attach(value, 0, 2, y, y + 1, xoptions=gtk.FILL) else: label = gtk.Label() label.set_markup("<b>%s</b>" % util.escape(title)) label.set_alignment(0.0, 0.5) table.attach(label, 0, 1, y, y + 1, xoptions=gtk.FILL) if key and isinstance(value, gtk.Widget): widget = value label.set_mnemonic_widget(widget) label.set_use_underline(True) widget.connect('changed', self.__changed, key, device) else: widget = gtk.Label(value) widget.set_use_markup(True) widget.set_selectable(True) widget.set_alignment(0.0, 0.5) table.attach(widget, 1, 2, y, y + 1) y += 1 self.show_all()
def __copy_songs(self, songlist, songs): model, iter = self.__view.get_selection().get_selected() if not iter: return False device = model[iter][0] if not self.__check_device(device, _("Unable to copy songs")): return False self.__busy = True wlb = self.__statusbar wlb.setup(len(songs), _("Copying <b>%(song)s</b>"), { 'song': '' }) wlb.show() model = songlist.get_model() for song in songs: label = util.escape(song('~artist~title')) if wlb.step(song=label): wlb.hide() break if len(model) > 0: songlist.scroll_to_cell(model[-1].path) while gtk.events_pending(): gtk.main_iteration() space, free = device.get_space() if free < os.path.getsize(song['~filename']): wlb.hide() qltk.WarningMessage( self, _("Unable to copy song"), _("There is not enough free space for this song.") ).run() break status = device.copy(songlist, song) if isinstance(status, AudioFile): model.append([status]) try: self.__cache[device.bid].append(song) except KeyError: pass self.__refresh_space(device) else: msg = _("<b>%s</b> could not be copied.") % label if type(status) == unicode: msg += "\n\n" + util.escape(status) qltk.WarningMessage(self, _("Unable to copy song"), msg).run() if device.cleanup and not device.cleanup(wlb, 'copy'): pass else: wlb.hide() self.__busy = False return True
def __copy_songs(self, songs): model, iter = self.__view.get_selection().get_selected() if not iter: return False device = model[iter][0] if not self.__check_device(device, _("Unable to copy songs")): return False self.__busy = True wlb = self.__statusbar wlb.setup( len(songs), _("Copying %(song)s") % {'song': '<b>%(song)s</b>'}, {'song': ''}) wlb.show() for song in songs: label = util.escape(song('~artist~title')) if wlb.step(song=label): wlb.hide() break space, free = device.get_space() if free < os.path.getsize(song['~filename']): wlb.hide() qltk.WarningMessage( self, _("Unable to copy song"), _("There is not enough free space for this song.") ).run() break status = device.copy(self, song) if isinstance(status, AudioFile): try: self.__cache[device.bid].append(song) except KeyError: pass self.__refresh_space(device) else: msg = _("%s could not be copied.") % util.bold(label) if type(status) == text_type: msg += "\n\n" + util.escape(status) qltk.WarningMessage(self, _("Unable to copy song"), msg).run() if device.cleanup and not device.cleanup(wlb, 'copy'): pass else: wlb.hide() self.__busy = False return True
def send_handshake(self, show_dialog=False): # construct url stamp = int(time.time()) auth = md5(self.password.encode("utf-8") + str(stamp).encode("utf-8")).hexdigest() url = "%s/?hs=true&p=%s&c=%s&v=%s&u=%s&a=%s&t=%d" % ( self.base_url, self.PROTOCOL_VERSION, self.CLIENT, self.CLIENT_VERSION, self.username, auth, stamp) print_d("Sending handshake to service.") try: resp = urlopen(url) except UrllibError: if show_dialog: self.quick_dialog( _("Could not contact service '%s'.") % util.escape(self.base_url), Gtk.MessageType.ERROR) else: print_d("Could not contact service. Queueing submissions.") return False except ValueError: self.quick_dialog(_("Authentication failed: invalid URL."), Gtk.MessageType.ERROR) self.broken = True return False # check response lines = resp.read().decode("utf-8", "ignore").rstrip().split("\n") status = lines.pop(0) print_d("Handshake status: %s" % status) if status == "OK": self.session_id, self.nowplaying_url, self.submit_url = lines self.handshake_sent = True print_d("Session ID: %s, NP URL: %s, Submit URL: %s" % ( self.session_id, self.nowplaying_url, self.submit_url)) return True elif status == "BADAUTH": self.quick_dialog(_("Authentication failed: Invalid username '%s' " "or bad password.") % util.escape(self.username), Gtk.MessageType.ERROR) self.broken = True elif status == "BANNED": self.quick_dialog(_("Client is banned. Contact the author."), Gtk.MessageType.ERROR) self.broken = True elif status == "BADTIME": self.quick_dialog(_("Wrong system time. Submissions may fail " "until it is corrected."), Gtk.MessageType.ERROR) else: # "FAILED" self.quick_dialog(util.escape(status), Gtk.MessageType.ERROR) self.changed() return False
def __save(self, addreplace, library): pattern_text = self.combo.child.get_text().decode('utf-8') pattern = TagsFromPattern(pattern_text) model = self.view.get_model() add = bool(addreplace.get_active()) win = WritingWindow(self, len(model)) was_changed = [] for row in (model or []): song = row[0] changed = False if not song.valid() and not qltk.ConfirmAction( self, _("Tag may not be accurate"), _("<b>%s</b> changed while the program was running. " "Saving without refreshing your library may " "overwrite other changes to the song.\n\n" "Save this song anyway?") %( util.escape(util.fsdecode(song("~basename")))) ).run(): break for i, h in enumerate(pattern.headers): if row[i + 2]: text = row[i + 2].decode("utf-8") if not add or h not in song or not song.multiple_values: song[h] = text changed = True else: for val in text.split("\n"): if val not in song.list(h): song.add(h, val) changed = True if changed: try: song.write() except: qltk.ErrorMessage( self, _("Unable to edit song"), _("Saving <b>%s</b> failed. The file " "may be read-only, corrupted, or you " "do not have permission to edit it.")%( util.escape(util.fsdecode(song('~basename')))) ).run() library.reload(song, changed=was_changed) break was_changed.append(song) if win.step(): break win.destroy() library.changed(was_changed) self.save.set_sensitive(False)
def __rename(self, library): model = self.view.get_model() win = WritingWindow(self, len(model)) win.show() was_changed = set() skip_all = False self.view.freeze_child_notify() for entry in model.itervalues(): song = entry.song new_name = entry.new_name old_name = entry.name if new_name is None: continue try: library.rename(song, fsnative(new_name), changed=was_changed) except Exception: util.print_exc() if skip_all: continue RESPONSE_SKIP_ALL = 1 msg = qltk.Message( Gtk.MessageType.ERROR, win, _("Unable to rename file"), _("Renaming <b>%(old-name)s</b> to <b>%(new-name)s</b> " "failed. Possibly the target file already exists, " "or you do not have permission to make the " "new file or remove the old one.") % { "old-name": util.escape(old_name), "new-name": util.escape(new_name), }, buttons=Gtk.ButtonsType.NONE) msg.add_button(_("Ignore _All Errors"), RESPONSE_SKIP_ALL) msg.add_icon_button(_("_Stop"), Icons.PROCESS_STOP, Gtk.ResponseType.CANCEL) msg.add_button(_("_Continue"), Gtk.ResponseType.OK) msg.set_default_response(Gtk.ResponseType.OK) resp = msg.run() skip_all |= (resp == RESPONSE_SKIP_ALL) # Preserve old behavior: shift-click is Ignore All mods = Gdk.Display.get_default().get_pointer()[3] skip_all |= mods & Gdk.ModifierType.SHIFT_MASK library.reload(song, changed=was_changed) if resp != Gtk.ResponseType.OK and resp != RESPONSE_SKIP_ALL: break if win.step(): break self.view.thaw_child_notify() win.destroy() library.changed(was_changed) self.save.set_sensitive(False)
def _album(self, songs, box): text = [] discs = {} for song in songs: try: discs[song("~#disc")] = int( song["tracknumber"].split("/")[1]) except (AttributeError, ValueError, IndexError, KeyError): discs[song("~#disc")] = max([ song("~#track", discs.get(song("~#disc"), 0))]) tracks = sum(discs.values()) discs = len(discs) length = sum([song.get("~#length", 0) for song in songs]) if tracks == 0 or tracks < len(songs): tracks = len(songs) parts = [] if discs > 1: parts.append( ngettext("%d disc", "%d discs", discs) % discs) parts.append( ngettext("%d track", "%d tracks", tracks) % tracks) if tracks != len(songs): parts.append(ngettext("%d selected", "%d selected", len(songs)) % len(songs)) text.append(", ".join(parts)) text.append(util.format_time_long(length)) if "location" in song: text.append(util.escape(song["location"])) if "organization" in song or "labelid" in song: t = util.escape(song.comma("~organization~labelid")) text.append(t) if "producer" in song: text.append(_("Produced by %s") %( util.escape(song.comma("producer")))) w = Label("") w.set_ellipsize(pango.ELLIPSIZE_END) w.set_markup("\n".join(text)) hb = gtk.HBox(spacing=12) cover = CoverImage(song=song) if cover: hb.pack_start(cover, expand=False) else: cover.destroy() hb.pack_start(w) box.pack_start(hb, expand=False, fill=False)
def celldata(layout, cell, model, iter_, data): release = model.get_value(iter_) extra_info = ", ".join( filter(None, [util.escape(release.date), util.escape(release.country), util.escape(release.medium_format), util.escape(release.labelid)])) artist_names = [a.name for a in release.artists] disc_count = release.disc_count track_count = release.track_count discs_format = ngettext( "%d disc", "%d discs", disc_count) % disc_count tracks_format = ngettext( "%d track", "%d tracks", track_count) % track_count markup = "<b>%s</b>\n%s - %s, %s (%s)" % ( util.escape(release.title), util.escape(", ".join(artist_names)), util.escape(discs_format), util.escape(tracks_format), extra_info) cell.set_property('markup', markup)
def celldata(layout, cell, model, iter, data): release = model[iter][0] if not release: return date = release.getEarliestReleaseDate() if date: date = '%s, ' % date else: date = '' markup = "<b>%s</b>\n%s - %s%s tracks" % ( util.escape(release.title), util.escape(release.artist.name), date, release.tracksCount) cell.set_property('markup', markup)
def plugin_songs(self, songs): # Check this is a launch, not a configure if self.com_index: com = self.get_data(self.com_index) print_d("Running %s" % com) try: com.run(songs) except Exception, err: print_e("Couldn't run command %s: %s %s at" % (com.name, type(err), err)) ErrorMessage( self.plugin_window, _("Unable to run custom command %s" % util.escape(self.com_index)), util.escape(str(err))).run()
def __add_new_tag(self, model, tag, value): if tag in self.__songinfo and not self.__songinfo.multiple_values: title = _("Unable to add tag") msg = _( "Unable to add <b>%s</b>\n\nThe files currently" " selected do not support multiple values." ) % util.escape(tag) qltk.ErrorMessage(self, title, msg).run() return iters = [row.iter for row in model if row[TAG] == tag] row = [tag, util.escape(value), True, True, False, None, False, None] if len(iters): model.insert_after(iters[-1], row=row) else: model.append(row=row)
def set_plugin(self, plugin): label = self.desc if plugin is None: label.set_markup("") else: name = util.escape(plugin.name) text = "<big><b>%s</b></big>" % name if plugin.description: text += "<span font='4'>\n\n</span>" text += plugin.description label.set_markup(text) label.connect("activate-link", show_uri) frame = self.prefs if frame.get_child(): frame.get_child().destroy() if plugin is not None: instance_or_cls = plugin.get_instance() or plugin.cls if plugin and hasattr(instance_or_cls, 'PluginPreferences'): try: prefs = instance_or_cls.PluginPreferences(self) except: util.print_exc() frame.hide() else: if isinstance(prefs, Gtk.Window): b = Button(_("_Preferences"), Icons.PREFERENCES_SYSTEM) connect_obj(b, 'clicked', Gtk.Window.show, prefs) connect_obj(b, 'destroy', Gtk.Window.destroy, prefs) frame.add(b) frame.get_child().set_border_width(6) else: frame.add(prefs) frame.show_all() else: frame.hide()
def __preview(self, songs): model = self.view.get_model() if songs is None: songs = [row[0] for row in model] pattern = self.combo.get_child().get_text().decode("utf-8") try: pattern = FileFromPattern(pattern) except ValueError: qltk.ErrorMessage( self, _("Path is not absolute"), _("The pattern\n\t<b>%s</b>\ncontains / but " "does not start from root. To avoid misnamed " "folders, root your pattern by starting " "it with / or ~/.") % (util.escape(pattern))).run() return else: if self.combo.get_child().get_text(): self.combo.prepend_text(self.combo.get_child().get_text()) self.combo.write(const.NBP) orignames = [song["~filename"] for song in songs] newnames = [fsdecode(fsencode(pattern.format(song))) for song in songs] for f in self.filters: if f.active: newnames = f.filter_list(orignames, newnames) model.clear() for song, newname in zip(songs, newnames): basename = fsdecode(song("~basename")) model.append(row=[song, basename, newname]) self.preview.set_sensitive(False) self.save.set_sensitive(bool(self.combo.get_child().get_text())) for song in songs: if not song.is_file: self.set_sensitive(False) break else: self.set_sensitive(True)
def __add_stations(self, irfs: Collection[IRFile], uri: str) -> None: print_d(f"Got {len(irfs)} station(s) from {uri}") assert self.__fav_stations is not None if not irfs: msg = ErrorMessage( self, _("No stations found"), _("No Internet radio stations were found at %s.") % util.escape(uri)) msg.run() return fav_uris = {af("~uri") for af in self.__fav_stations} irfs = {af for af in irfs if af("~uri") not in fav_uris} if irfs: print_d(f"Adding {irfs} to favourites") self.__fav_stations.add(irfs) else: message = WarningMessage( self, _("Nothing to add"), _("All stations listed are already in your library.")) message.run()
def __add_tag(self, activator, model, library): add = AddTagDialog(self, self.__songinfo.can_change(), library) while True: resp = add.run() if resp != Gtk.ResponseType.OK: break tag = add.get_tag() value = add.get_value() if tag in massagers.tags: value = massagers.tags[tag].validate(value) if not self.__songinfo.can_change(tag): title = _("Invalid tag") msg = _("Invalid tag <b>%s</b>\n\nThe files currently" " selected do not support editing this tag." ) % util.escape(tag) qltk.ErrorMessage(self, title, msg).run() else: self.__add_new_tag(model, tag, value) break add.destroy()
def confirm_remove_playlist_dialog_invoke( parent, playlist, Confirmer=ConfirmationPrompt): """Creates and invokes a confirmation dialog that asks the user whether or not to go forth with the deletion of the selected playlist. Confirmer needs to accept the arguments for constructing a dialog, have a run-method returning a response, and have a RESPONSE_INVOKE attribute. returns the result of comparing the result of run to RESPONSE_INVOKE """ title = (_("Are you sure you want to delete the playlist '%s'?") % escape(playlist.name)) description = (_("All information about the selected playlist " "will be deleted and can not be restored.")) ok_text = _("_Delete") ok_icon = Icons.EDIT_DELETE dialog = Confirmer(parent, title, description, ok_text, ok_icon) prompt = dialog.run() response = (prompt == Confirmer.RESPONSE_INVOKE) return response
def _file(self, song, box): def ftime(t): if t == 0: return _("Unknown") else: timestr = time.strftime("%c", time.localtime(t)) encoding = util.get_locale_encoding() return timestr.decode(encoding) fn = fsdecode(unexpand(song["~filename"])) length = util.format_time_long(song.get("~#length", 0)) size = util.format_size( song.get("~#filesize") or filesize(song["~filename"])) mtime = ftime(util.path.mtime(song["~filename"])) bitrate = song.get("~#bitrate", 0) if bitrate != 0: bitrate = _("%d kbps") % int(bitrate) else: bitrate = False t = Gtk.Table(n_rows=4, n_columns=2) t.set_col_spacings(6) t.set_homogeneous(False) table = [(_("length"), length), (_("file size"), size), (_("modified"), mtime)] if bitrate: table.insert(1, (_("bitrate"), bitrate)) fnlab = Label(fn) fnlab.set_ellipsize(Pango.EllipsizeMode.MIDDLE) t.attach(fnlab, 0, 2, 0, 1, xoptions=Gtk.AttachOptions.FILL) for i, (l, r) in enumerate(table): l = "<b>%s</b>" % util.capitalize(util.escape(l) + ":") lab = Label() lab.set_markup(l) t.attach(lab, 0, 1, i + 1, i + 2, xoptions=Gtk.AttachOptions.FILL) t.attach(Label(r), 1, 2, i + 1, i + 2) box.pack_start(Frame(_("File"), t), False, False, 0)
def check_wrapper_changed(library, parent, songs): need_write = [s for s in songs if s._needs_write] if need_write: win = WritingWindow(parent, len(need_write)) win.show() for song in need_write: try: song._song.write() except AudioFileError as e: qltk.ErrorMessage( None, _("Unable to edit song"), _("Saving <b>%s</b> failed. The file " "may be read-only, corrupted, or you " "do not have permission to edit it.") % util.escape(song('~basename'))).run() print_w("Couldn't save song %s (%s)" % (song("~filename"), e)) if win.step(): break win.destroy() _inform_library_of_changed(library, songs)
def _library(self, song, box): def counter(i): if i == 0: return _("Never") else: return ngettext("%(n)d time", "%(n)d times", i) % {"n": i} def ftime(t): if t == 0: return _("Unknown") else: timestr = time.strftime("%c", time.localtime(t)) return timestr.decode(const.ENCODING) playcount = counter(song.get("~#playcount", 0)) skipcount = counter(song.get("~#skipcount", 0)) lastplayed = ftime(song.get("~#lastplayed", 0)) if lastplayed == _("Unknown"): lastplayed = _("Never") added = ftime(song.get("~#added", 0)) rating = song("~rating") t = Gtk.Table(5, 2) t.set_col_spacings(6) t.set_homogeneous(False) table = [(_("added"), added), (_("last played"), lastplayed), (_("plays"), playcount), (_("skips"), skipcount), (_("rating"), rating)] for i, (l, r) in enumerate(table): l = "<b>%s</b>" % util.capitalize(util.escape(l) + ":") lab = Label() lab.set_markup(l) t.attach(lab, 0, 1, i + 1, i + 2, xoptions=Gtk.AttachOptions.FILL) t.attach(Label(r), 1, 2, i + 1, i + 2) box.pack_start(Frame(_("Library"), t), False, False, 0)
def add_station(uri): """Fetches the URI content and extracts IRFiles Returns None in error, else a possibly filled list of stations""" irfs = [] if isinstance(uri, unicode): uri = uri.encode('utf-8') if uri.lower().endswith(".pls") or uri.lower().endswith(".m3u"): try: sock = urlopen(uri) except EnvironmentError as e: print_d("Got %s from %s" % (uri, e)) encoding = util.get_locale_encoding() try: err = e.strerror.decode(encoding, 'replace') except TypeError: err = e.strerror[1].decode(encoding, 'replace') except AttributeError: # Give up and display the exception - may be useful HTTP info err = str(e) ErrorMessage(None, _("Unable to add station"), escape(err)).run() return None if uri.lower().endswith(".pls"): irfs = ParsePLS(sock) elif uri.lower().endswith(".m3u"): irfs = ParseM3U(sock) sock.close() else: try: irfs = [IRFile(uri)] except ValueError as err: ErrorMessage(None, _("Unable to add station"), err).run() return irfs
def _file(self, song, box): def ftime(t): if t == 0: return _("Unknown") else: return text_type(time.strftime("%c", time.localtime(t))) fn = fsn2text(unexpand(song["~filename"])) length = util.format_time_preferred(song.get("~#length", 0)) size = util.format_size( song.get("~#filesize") or filesize(song["~filename"])) mtime = ftime(util.path.mtime(song["~filename"])) format_ = song("~format") codec = song("~codec") encoding = song.comma("~encoding") bitrate = song("~bitrate") table = [(_("path"), fn), (_("length"), length), (_("format"), format_), (_("codec"), codec), (_("encoding"), encoding), (_("bitrate"), bitrate), (_("file size"), size), (_("modified"), mtime)] t = Table(len(table)) for i, (tag_, text) in enumerate(table): tag_ = util.capitalize(util.escape(tag_) + ":") lab = Label(text) lab.set_ellipsize(Pango.EllipsizeMode.MIDDLE) t.attach(Label(tag_), 0, 1, i, i + 1, xoptions=Gtk.AttachOptions.FILL) t.attach(lab, 1, 2, i, i + 1) box.pack_start(Frame(_("File"), t), False, False, 0)
def _people(self, song, box): data = [] if "artist" in song: title = (_("artist") if len(song.list("artist")) == 1 else _("artists")) title = util.capitalize(title) data.append((title, song["artist"])) for tag_ in ["performer", "lyricist", "arranger", "composer", "conductor", "author"]: if tag_ in song: name = (tag(tag_) if len(song.list(tag_)) == 1 else readable(tag_, plural=True)) data.append((name, song[tag_])) performers = defaultdict(list) for tag_ in song: if "performer:" in tag_: for person in song.list(tag_): role = util.title(tag_.split(':', 1)[1]) performers[role].append(person) if performers: text = '\n'.join("%s (%s)" % (', '.join(names), part) for part, names in performers.iteritems()) name = (tag("performer") if len(performers) == 1 else _("performers")) data.append((name, text)) table = Table(len(data)) for i, (key, text) in enumerate(data): key = util.capitalize(util.escape(key) + ":") table.attach(Label(markup=key), 0, 1, i, i + 1, xoptions=Gtk.AttachOptions.FILL) label = Label(text, ellipsize=True) table.attach(label, 1, 2, i, i + 1) box.pack_start(Frame(tag("~people"), table), False, False, 0)
def celldata(layout, cell, model, iter_, data): release = model.get_value(iter_) extra_info = ", ".join( filter(None, [util.escape(release.date), util.escape(release.country), util.escape(release.medium_format)])) artist_names = [a.name for a in release.artists] disc_count = release.disc_count track_count = release.track_count discs_format = ngettext( "%d disc", "%d discs", disc_count) % disc_count tracks_format = ngettext( "%d track", "%d tracks", track_count) % track_count markup = "<b>%s</b>\n%s - %s, %s (%s)" % ( util.escape(release.title), util.escape(", ".join(artist_names)), util.escape(discs_format), util.escape(tracks_format), extra_info) cell.set_property('markup', markup)
def get_shared_text(self): if self.shared: return util.escape(self.text) return ''
def __make_row(cls, song): """Construct GTK row for a song, with all columns""" return [song] + [util.escape(str(f(song.comma(tag)))) for (tag, f) in cls.TAG_MAP]
def cell_data(col, render, model, iter, data): if model[iter][0].changed: render.markup = "<b>%s</b>" % util.escape(model[iter][0].name) else: render.markup = util.escape(model[iter][0].name) render.set_property('markup', render.markup)
EXPAND = Gtk.AttachOptions.EXPAND FILL = Gtk.AttachOptions.FILL class Config(object): STATUS_SONGLESS = 'no_song_text', "" PAT_PLAYING = 'playing_pattern', "♫ <~artist~title> ♫" PAT_PAUSED = 'paused_pattern', "<~artist~title> [%s]" % _("paused") HOST = 'host', "localhost" PORT = 'port', 1883 TOPIC = 'topic', 'quodlibet/now-playing' _ACCEPTS_PATTERNS = (_("Accepts QL Patterns e.g. %s") % monospace(escape('<~artist~title>'))) class MqttPublisherPlugin(EventPlugin, PluginConfigMixin): PLUGIN_ID = "MQTT Status" PLUGIN_NAME = _("MQTT Publisher") PLUGIN_DESC = _("Publishes status messages to an MQTT topic.") PLUGIN_ICON = Icons.FACE_SMILE def on_connect(self, client, userdata, flags, rc): """Callback for when the client receives a CONNACK response from the server.""" print_d("Connected to %s at %s:%d with result code %s" % (self.topic, self.host, self.port, rc)) def _subscribe(self, client, topic):
def test_roundtrip(self): for s in ["foo&", "<&>", "&", "&", "<&testing&>amp;"]: esc = util.escape(s) self.failIfEqual(s, esc) self.failUnlessEqual(s, util.unescape(esc))
def _title(self, songs, box): self.title = ngettext("%d song", "%d songs", len(songs)) % len(songs) markup = util.escape(self.title) box.pack_start(TitleLabel(markup, is_markup=True), False, False, 0)
def __save_files(self, save, revert, model, library): updated = {} deleted = {} added = {} renamed = {} for row in model: if row[EDITED] and not (row[DELETED] or row[RENAMED]): if row[ORIGVALUE] is not None: updated.setdefault(row[TAG], []) updated[row[TAG]].append( (decode(row[VALUE]), decode(row[ORIGVALUE]))) else: added.setdefault(row[TAG], []) added[row[TAG]].append(decode(row[VALUE])) if row[EDITED] and row[DELETED]: if row[ORIGVALUE] is not None: deleted.setdefault(row[TAG], []) deleted[row[TAG]].append(decode(row[ORIGVALUE])) if row[EDITED] and row[RENAMED] and not row[DELETED]: renamed.setdefault(row[TAG], []) renamed[row[TAG]].append( (decode(row[ORIGTAG]), decode(row[VALUE]), decode(row[ORIGVALUE]))) was_changed = set() songs = self.__songinfo.songs win = WritingWindow(self, len(songs)) win.show() for song in songs: if not song.valid() and not qltk.ConfirmAction( self, _("Tag may not be accurate"), _("<b>%s</b> changed while the program was running. " "Saving without refreshing your library may " "overwrite other changes to the song.\n\n" "Save this song anyway?") % util.escape(util.fsdecode(song("~basename")))).run(): break changed = False for key, values in updated.iteritems(): for (new_value, old_value) in values: new_value = util.unescape(new_value) if song.can_change(key): if old_value is None: song.add(key, new_value) else: song.change(key, old_value, new_value) changed = True for key, values in added.iteritems(): for value in values: value = util.unescape(value) if song.can_change(key): song.add(key, value) changed = True for key, values in deleted.iteritems(): for value in values: value = util.unescape(value) if song.can_change(key) and key in song: song.remove(key, value) changed = True save_rename = [] for new_tag, values in renamed.iteritems(): for old_tag, new_value, old_value in values: old_tag = util.unescape(old_tag) old_value = util.unescape(old_value) new_value = util.unescape(new_value) if (song.can_change(new_tag) and song.can_change(old_tag) and old_tag in song): if not is_special(new_value): song.remove(old_tag, old_value) save_rename.append((new_tag, new_value)) elif is_missing(new_value): value = strip_missing(old_value) song.remove(old_tag, old_value) save_rename.append((new_tag, new_value)) else: save_rename.append((new_tag, song[old_tag])) song.remove(old_tag, None) changed = True for tag, value in save_rename: song.add(tag, value) if changed: try: song.write() except: util.print_exc() qltk.ErrorMessage( self, _("Unable to save song"), _("Saving <b>%s</b> failed. The file " "may be read-only, corrupted, or you " "do not have permission to edit it.") % (util.escape(fsdecode(song('~basename'))))).run() library.reload(song, changed=was_changed) break was_changed.add(song) if win.step(): break win.destroy() library.changed(was_changed) for b in [save, revert]: b.set_sensitive(False)
def __preview(self, songs): if songs is None: songs = [row[0].song for row in (self.view.get_model() or [])] if songs: pattern_text = self.combo.get_child().get_text().decode("utf-8") else: pattern_text = "" try: pattern = TagsFromPattern(pattern_text) except re.error: qltk.ErrorMessage( self, _("Invalid pattern"), _("The pattern\n\t<b>%s</b>\nis invalid. " "Possibly it contains the same tag twice or " "it has unbalanced brackets (< / >).") % (util.escape(pattern_text))).run() return else: if pattern_text: self.combo.prepend_text(pattern_text) self.combo.write(const.TBP) invalid = [] for header in pattern.headers: if not min([song.can_change(header) for song in songs]): invalid.append(header) if len(invalid) and songs: if len(invalid) == 1: title = _("Invalid tag") msg = _("Invalid tag <b>%s</b>\n\nThe files currently" " selected do not support editing this tag.") else: title = _("Invalid tags") msg = _("Invalid tags <b>%s</b>\n\nThe files currently" " selected do not support editing these tags.") qltk.ErrorMessage(self, title, msg % ", ".join(invalid)).run() pattern = TagsFromPattern("") self.view.set_model(None) model = ObjectStore() for col in self.view.get_columns(): self.view.remove_column(col) render = Gtk.CellRendererText() col = TreeViewColumn(_('File'), render) col.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) def cell_data_file(column, cell, model, iter_, data): entry = model.get_value(iter_) cell.set_property("text", entry.name) col.set_cell_data_func(render, cell_data_file) def cell_data_header(column, cell, model, iter_, header): entry = model.get_value(iter_) cell.set_property("text", entry.get_match(header)) self.view.append_column(col) for i, header in enumerate(pattern.headers): render = Gtk.CellRendererText() render.set_property('editable', True) render.connect('edited', self.__row_edited, model, header) escaped_title = header.replace("_", "__") col = Gtk.TreeViewColumn(escaped_title, render) col.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) col.set_cell_data_func(render, cell_data_header, header) self.view.append_column(col) for song in songs: entry = ListEntry(song) match = pattern.match(song) for h in pattern.headers: text = match.get(h, '') for f in self.filter_box.filters: if f.active: text = f.filter(h, text) if not song.can_multiple_values(h): text = u", ".join(text.split("\n")) entry.matches[h] = text model.append([entry]) # save for last to potentially save time if songs: self.view.set_model(model) self.preview.set_sensitive(False) self.save.set_sensitive(len(pattern.headers) > 0)
def _title(self, song, box): text = song.comma("title") if "version" in song: text += "\n" + util.escape(song.comma("version")) box.pack_start(TitleLabel(text), False, False, 0) self.title = song.comma("title")
def escape_data(data): for rep in ('\n', '\t', '\r', '\v'): data = data.replace(rep, ' ') return util.escape(' '.join(data.split()))
def _format(self, key, value): return util.escape(value)
def __init__(self, text, is_markup=False): super(TitleLabel, self).__init__() self.set_ellipsize(Pango.EllipsizeMode.END) markup = text if is_markup else ("<i>%s</i>" % util.escape(text)) text = "<span size='xx-large'>%s</span>" % markup self.set_markup(text)
def __init__(self): super(PatternEditor, self).__init__(spacing=6) self.__headers = headers = {} buttons = [] group = None for tags in self.PRESETS: tied = "~" + "~".join(tags) group = Gtk.RadioButton(group=group, label="_" + util.tag(tied), use_underline=True) headers[group] = tags buttons.append(group) group = Gtk.RadioButton(group=group, label=_("_Custom"), use_underline=True) self.__custom = group headers[group] = [] buttons.append(group) button_box = Gtk.HBox(spacing=6) self.__model = model = Gtk.ListStore(str) radio_box = Gtk.VBox(spacing=6) for button in buttons: radio_box.pack_start(button, False, True, 0) button.connect('toggled', self.__toggled, button_box, model) self.pack_start(radio_box, False, True, 0) tooltip = _("Tag pattern with optional markup " "e.g. <tt>composer</tt> or\n<tt>%s</tt>" % escape(self._COMPLEX_PATTERN_EXAMPLE)) cb = TagsComboBoxEntry(self.COMPLETION, tooltip_markup=tooltip) view = BaseView(model=model) view.set_reorderable(True) view.set_headers_visible(False) ctrl_box = Gtk.VBox(spacing=6) add = Button(_("_Add"), Icons.LIST_ADD) ctrl_box.pack_start(add, False, True, 0) add.connect('clicked', self.__add, model, cb) remove = Button(_("_Remove"), Icons.LIST_REMOVE) ctrl_box.pack_start(remove, False, True, 0) remove.connect('clicked', self.__remove, view) selection = view.get_selection() selection.connect('changed', self.__selection_changed, remove) selection.emit('changed') sw = Gtk.ScrolledWindow() sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) sw.set_shadow_type(Gtk.ShadowType.IN) sw.add(view) edit_box = Gtk.VBox(spacing=6) edit_box.pack_start(cb, False, True, 0) edit_box.pack_start(sw, True, True, 0) button_box.pack_start(edit_box, True, True, 0) button_box.pack_start(ctrl_box, False, True, 0) self.pack_start(button_box, True, True, 0) render = Gtk.CellRendererText() render.set_property("editable", True) def edited_cb(render, path, text, model): model[path][0] = text render.connect("edited", edited_cb, model) column = Gtk.TreeViewColumn(None, render, text=0) view.append_column(column)
def __str__(self): return util.escape(self)
def f(v): return "<b>%s</b>" % escape(format_version(v))
def test_escape_empty(self): self.failUnlessEqual(util.escape(""), "")
def PluginPreferences(self, song): def changed_cb(hscale, key): val = hscale.get_value() self.weights[key] = val config.set("plugins", "randomalbum_%s" % key, val) def delay_changed_cb(spin): self.delay = int(spin.get_value()) config.set("plugins", "randomalbum_delay", str(self.delay)) def toggled_cb(check, widgets): self.use_weights = check.get_active() for w in widgets: w.set_sensitive(self.use_weights) config.set("plugins", "randomalbum_use_weights", str(int(self.use_weights))) vbox = Gtk.VBox(spacing=12) table = Gtk.Table(n_rows=len(self.keys) + 1, n_columns=3) table.set_border_width(3) hbox = Gtk.HBox(spacing=6) spin = Gtk.SpinButton( adjustment=Gtk.Adjustment.new(self.delay, 0, 3600, 1, 10, 0)) spin.connect("value-changed", delay_changed_cb) hbox.pack_start(spin, False, True, 0) lbl = Gtk.Label(label=_("seconds before starting next album")) hbox.pack_start(lbl, False, True, 0) vbox.pack_start(hbox, True, True, 0) frame = Gtk.Frame(label=_("Weights")) check = Gtk.CheckButton(label=_("Play some albums more than others")) vbox.pack_start(check, False, True, 0) # Toggle both frame and contained table; frame doesn't always work? check.connect("toggled", toggled_cb, [frame, table]) check.set_active(self.use_weights) toggled_cb(check, [frame, table]) frame.add(table) vbox.pack_start(frame, True, True, 0) # Less label less_lbl = Gtk.Label() arr = Gtk.Arrow(arrow_type=Gtk.ArrowType.LEFT, shadow_type=Gtk.ShadowType.OUT) less_lbl.set_markup("<i>%s</i>" % util.escape(_("avoid"))) less_lbl.set_alignment(0, 0) hb = Gtk.HBox(spacing=0) hb.pack_start(arr, False, True, 0) hb.pack_start(less_lbl, True, True, 0) table.attach(hb, 1, 2, 0, 1, xpadding=3, xoptions=Gtk.AttachOptions.FILL) # More label more_lbl = Gtk.Label() arr = Gtk.Arrow(arrow_type=Gtk.ArrowType.RIGHT, shadow_type=Gtk.ShadowType.OUT) more_lbl.set_markup("<i>%s</i>" % util.escape(_("prefer"))) more_lbl.set_alignment(1, 0) hb = Gtk.HBox(spacing=0) hb.pack_end(arr, False, True, 0) hb.pack_end(more_lbl, True, True, 0) table.attach(hb, 2, 3, 0, 1, xpadding=3, xoptions=Gtk.AttachOptions.FILL) for (idx, (key, text, func)) in enumerate(self.keys): lbl = Gtk.Label(label=text) lbl.set_alignment(0, 0) table.attach(lbl, 0, 1, idx + 1, idx + 2, xoptions=Gtk.AttachOptions.FILL, xpadding=3, ypadding=3) adj = Gtk.Adjustment(lower=-1.0, upper=1.0, step_increment=0.1) hscale = Gtk.HScale(adjustment=adj) hscale.set_value(self.weights[key]) hscale.set_draw_value(False) hscale.set_show_fill_level(False) hscale.connect("value-changed", changed_cb, key) lbl.set_mnemonic_widget(hscale) table.attach(hscale, 1, 3, idx + 1, idx + 2, xpadding=3, ypadding=3) return vbox
def format(args): date, song, album = args markup = "<big><i>%s</i></big>" % util.escape(album) return "%s (%s)" % (markup, date[:4]) if date else markup