Пример #1
0
    def test_iter_append_many_empty(self):
        m = ObjectStore()
        for x in m.iter_append_many([]):
            pass

        for x in m.iter_append_many(iter([])):
            pass
Пример #2
0
 def test_plugin_list(self):
     model = ObjectStore()
     model.append([PLUGIN])
     plist = PluginListView()
     plist.set_model(model)
     with realized(plist):
         plist.select_by_plugin_id("foobar")
     plist.destroy()
Пример #3
0
    def test_filter(self):
        m = ObjectStore()
        f = ObjectModelFilter(child_model=m)
        m.insert_many(0, range(10))

        def filter_func(model, iter_, data):
            if model.get_value(iter_) % 2 == 0:
                return True
            return False

        f.set_visible_func(filter_func)
        f.refilter()
        self.failUnlessEqual(range(0, 10, 2), list(f.itervalues()))
Пример #4
0
    def test_sort(self):
        m = ObjectStore()
        f = ObjectModelSort(model=m)
        m.insert_many(0, range(10))

        def sort_func(model, iter_a, iter_b, data):
            a = model.get_value(iter_a, 0)
            b = model.get_value(iter_b, 0)
            return -cmp(a, b)

        f.set_default_sort_func(sort_func)

        self.failUnlessEqual(sorted(range(10), reverse=True),
                             list(f.itervalues()))
Пример #5
0
    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
Пример #6
0
    def __init__(self, album):
        self.album = album
        self._release = None
        self.model = ObjectStore()
        self.model.append_many(album)

        super(ResultTreeView, self).__init__(self.model)
        self.set_headers_clickable(True)
        self.set_rules_hint(True)
        self.set_reorderable(True)
        self.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)

        mode = Pango.EllipsizeMode
        cols = [
                (_('Filename'), self.__name_datafunc, True, mode.MIDDLE),
                (_('Disc'), self.__disc_datafunc, False, mode.END),
                (_('Track'), self.__track_datafunc, False, mode.END),
                (_('Title'), self.__title_datafunc, True, mode.END),
                (_('Artist'), self.__artist_datafunc, True, mode.END),
            ]

        for title, func, resize, mode in cols:
            render = Gtk.CellRendererText()
            render.set_property('ellipsize', mode)
            col = Gtk.TreeViewColumn(title, render)
            col.set_cell_data_func(render, func)
            col.set_resizable(resize)
            col.set_expand(resize)
            self.append_column(col)
    def _render_column(self, column, **kwargs):
        view = Gtk.TreeView()
        model = ObjectStore()
        view.set_model(model)
        song = AudioFile({"~filename": "/dev/null", "~#rating": 0.6666})
        song.update(kwargs)
        model.append(row=[song])
        view.append_column(column)
        if column.get_resizable():
            column.set_expand(True)

        with visible(view):
            view.columns_autosize()

        text = column.get_cells()[0].get_property("text")
        self.assertIsNot(text, None)
        return text
Пример #8
0
 def test_is_empty(self):
     m = ObjectStore()
     self.assertTrue(m.is_empty())
     iter_ = m.append(row=[1])
     self.assertFalse(m.is_empty())
     m.remove(iter_)
     self.assertTrue(m.is_empty())
Пример #9
0
 def test__sort_on_value(self):
     m = ObjectStore()
     iterBob = m.append(row=["bob"])
     iterAlice = m.append(row=["alice"])
     m.append(row=["charlie"])
     result = ObjectStore._sort_on_value(m, iterAlice, iterBob, None)
     self.assertEqual(result, cmp("alice", "bob"))
Пример #10
0
    def test_insert_many_append(self):
        m = ObjectStore()
        m.insert_many(-1, range(10))
        self.failUnlessEqual([r[0] for r in m], range(10))

        m = ObjectStore()
        m.insert_many(99, range(10))
        self.failUnlessEqual([r[0] for r in m], range(10))
Пример #11
0
    def __init__(self,
                 songs_lib: SongFileLibrary,
                 Confirmer=ConfirmationPrompt):
        super().__init__(spacing=6)
        self._lists = ObjectModelSort(model=ObjectStore())
        self._lists.set_default_sort_func(ObjectStore._sort_on_value)

        self.songs_lib = songs_lib
        try:
            self.pl_lib: PlaylistLibrary = songs_lib.playlists
        except (AttributeError, TypeError):
            print_w("No playlist library available")
        else:
            model = self._lists.get_model()
            print_d(f"Reading playlists from library: {self.pl_lib}")
            for playlist in self.pl_lib:
                model.append(row=[playlist])

        # this is instanced with the necessary gtkdialog-settings, and afterwards
        # its run-method is called to get a to-be-compared Gtk.ResponseType
        self.Confirmer = Confirmer
        self.set_orientation(Gtk.Orientation.VERTICAL)
        self.__render = self.__create_cell_renderer()
        self.__view = view = self.__create_playlists_view(self.__render)
        self.__embed_in_scrolledwin(view)
        self.__configure_buttons(songs_lib)
        self.__configure_dnd(view)
        self.__connect_signals(view)
        self._sb_box = self.__create_searchbar(songs_lib)
        self._rh_box = None
        self._main_box = self.__create_box()
        self.show_all()

        for child in self.get_children():
            child.show_all()

        if hasattr(self, "pl_lib"):
            self._ids = [
                self.pl_lib.connect('removed', self.__removed),
                self.pl_lib.connect('added', self.__added),
                self.pl_lib.connect('changed', self.__changed),
            ]
            print_d(
                f"Connected signals: {self._ids} from {self.pl_lib!r} for {self}"
            )
        else:
            self._ids = []
        self.connect("destroy", self._destroy)
Пример #12
0
class ResultTreeView(HintedTreeView, MultiDragTreeView):
    """The result treeview"""

    def __init__(self, album):
        self.album = album
        self._release = None
        self.model = ObjectStore()
        self.model.append_many(album)

        super(ResultTreeView, self).__init__(self.model)
        self.set_headers_clickable(True)
        self.set_rules_hint(True)
        self.set_reorderable(True)
        self.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)

        mode = Pango.EllipsizeMode
        cols = [
                (_('Filename'), self.__name_datafunc, True, mode.MIDDLE),
                (_('Disc'), self.__disc_datafunc, False, mode.END),
                (_('Track'), self.__track_datafunc, False, mode.END),
                (_('Title'), self.__title_datafunc, True, mode.END),
                (_('Artist'), self.__artist_datafunc, True, mode.END),
            ]

        for title, func, resize, mode in cols:
            render = Gtk.CellRendererText()
            render.set_property('ellipsize', mode)
            col = Gtk.TreeViewColumn(title, render)
            col.set_cell_data_func(render, func)
            col.set_resizable(resize)
            col.set_expand(resize)
            self.append_column(col)

    def iter_tracks(self):
        """Yields tuples of (release, track, song) combinations as they
        are shown in the list.
        """

        tracks = self._tracks
        for idx, (song, ) in enumerate(self.model):
            if song is None:
                continue
            if idx >= len(tracks):
                continue
            track = tracks[idx]
            yield (self._release, track, song)

    def update_release(self, full_release):
        """Updates the TreeView, handling results with a different number of
        tracks than the album being tagged.

        Passing in None will reset the list.
        """

        if full_release is not None:
            tracks = full_release.tracks
        else:
            tracks = []

        for i in range(len(self.model), len(tracks)):
            self.model.append((None, ))
        for i in range(len(self.model), len(tracks), -1):
            if self.model[-1][0] is not None:
                break
            itr = self.model.get_iter_from_string(str(len(self.model) - 1))
            self.model.remove(itr)

        self._release = full_release

        for row in self.model:
            self.model.row_changed(row.path, row.iter)

        # Only show artists if we have any
        has_artists = bool(filter(lambda t: t.artists, tracks))
        col = self.get_column(4)
        col.set_visible(has_artists)

        # Only show discs column if we have more than one disc
        col = self.get_column(1)
        col.set_visible(
            bool(full_release) and bool(full_release.disc_count > 1))

        self.columns_autosize()

    @property
    def _tracks(self):
        if self._release is None:
            return []
        return self._release.tracks

    def __name_datafunc(self, col, cell, model, itr, data):
        song = model[itr][0]
        if song:
            cell.set_property('text', path.fsdecode(song("~basename")))
        else:
            cell.set_property('text', '')

    def __track_datafunc(self, col, cell, model, itr, data):
        idx = model.get_path(itr)[0]
        if idx >= len(self._tracks):
            cell.set_property('text', '')
        else:
            cell.set_property('text', self._tracks[idx].tracknumber)

    def __disc_datafunc(self, col, cell, model, itr, data):
        idx = model.get_path(itr)[0]
        if idx >= len(self._tracks):
            cell.set_property('text', '')
        else:
            cell.set_property('text', self._tracks[idx].discnumber)

    def __title_datafunc(self, col, cell, model, itr, data):
        idx = model.get_path(itr)[0]
        if idx >= len(self._tracks):
            cell.set_property('text', '')
        else:
            cell.set_property('text', self._tracks[idx].title)

    def __artist_datafunc(self, col, cell, model, itr, data):
        idx = model.get_path(itr)[0]
        if idx >= len(self._tracks):
            cell.set_property('text', '')
        else:
            names = [a.name for a in self._tracks[idx].artists]
            cell.set_property('text', ", ".join(names))
Пример #13
0
 def test_iter_values(self):
     m = ObjectStore()
     f = ObjectModelSort(model=m)
     m.insert_many(0, range(10))
     self.failUnlessEqual(range(10), list(f.itervalues()))
Пример #14
0
    def test_iter_path_changed(self):
        m = ObjectStore()

        def handler(model, path, iter_, result):
            result[0] += 1

        result = [0]
        m.connect("row-changed", handler, result)
        m.append([object()])

        iter_ = m.get_iter_first()
        m.iter_changed(iter_)
        self.assertEqual(result[0], 1)
        m.path_changed(m.get_path(iter_))
        self.assertEqual(result[0], 2)
Пример #15
0
 def test_insert_many(self):
     m = ObjectStore()
     m.append(row=[42])
     m.append(row=[24])
     m.insert_many(1, range(10))
     self.failUnlessEqual([r[0] for r in m], [42] + range(10) + [24])
Пример #16
0
 def test_append_many(self):
     m = ObjectStore()
     m.append_many(range(10))
     self.failUnlessEqual([r[0] for r in m], range(10))
Пример #17
0
    def __init__(self, parent=None):
        if self.is_not_unique():
            return
        on_top = config.getboolean("settings", "plugins_window_on_top", False)
        super().__init__(dialog=on_top)
        self.set_title(_("Plugins"))
        self.set_default_size(750, 500)
        if parent and on_top:
            self.set_transient_for(parent)
        self.set_type_hint(Gdk.WindowTypeHint.NORMAL)
        self.enable_window_tracking("plugin_prefs")

        model = ObjectStore()
        filter_model = ObjectModelFilter(child_model=model)

        self._list_view = plv = PluginListView()
        plv.set_model(filter_model)
        plv.set_rules_hint(True)

        plv.connect("plugin-toggled", self.__plugin_toggled)
        sw = ScrolledWindow()
        sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.ALWAYS)
        sw.add(plv)
        sw.set_shadow_type(Gtk.ShadowType.IN)

        fb = Gtk.HBox(spacing=6)

        enabled_combo = PluginEnabledFilterCombo()
        enabled_combo.connect("changed", lambda s: filter_model.refilter())
        enabled_combo.set_tooltip_text(_("Filter by plugin state / tag"))
        fb.pack_start(enabled_combo, True, True, 0)
        self._enabled_combo = enabled_combo

        type_combo = PluginTypeFilterCombo()
        type_combo.connect("changed", lambda s: filter_model.refilter())
        type_combo.set_tooltip_text(_("Filter by plugin type"))
        fb.pack_start(type_combo, True, True, 0)
        self._type_combo = type_combo

        self._filter_entry = fe = UndoSearchEntry()
        fe.set_tooltip_text(_("Filter by plugin name or description"))
        fe.connect("changed", lambda s: filter_model.refilter())

        errors = qltk.Button(_("Show _Errors"), Icons.DIALOG_WARNING)
        errors.set_focus_on_click(False)
        errors.connect('clicked', self.__show_errors)
        errors.show()
        errors = Align(errors, top=6, bottom=6)
        errors.set_no_show_all(True)
        bbox = Gtk.VBox()
        bbox.pack_start(errors, True, True, 0)

        pref_box = PluginPreferencesContainer()

        if const.DEBUG:
            refresh = qltk.Button(_("_Refresh"), Icons.VIEW_REFRESH)
            refresh.set_focus_on_click(False)
            refresh.connect('clicked', self.__refresh, plv, pref_box, errors,
                            enabled_combo)
            bbox.pack_start(refresh, True, True, 0)

        filter_box = Gtk.VBox(spacing=6)
        filter_box.pack_start(fb, False, True, 0)
        filter_box.pack_start(fe, False, True, 0)

        vbox = Gtk.VBox()
        vbox.pack_start(Align(filter_box, border=6, right=-6), False, False, 0)
        vbox.pack_start(sw, True, True, 0)
        vbox.pack_start(Align(bbox, left=3, right=3, top=0), False, False, 3)
        paned = Paned()
        paned.pack1(vbox, False, False)

        close = qltk.Button(_("_Close"), Icons.WINDOW_CLOSE)
        close.connect('clicked', lambda *x: self.destroy())
        bb_align = Align(halign=Gtk.Align.END, valign=Gtk.Align.END)
        bb = Gtk.HButtonBox()
        bb.set_layout(Gtk.ButtonBoxStyle.END)
        bb.pack_start(close, True, True, 0)
        bb_align.add(bb)

        selection = plv.get_selection()
        selection.connect('changed', self.__selection_changed, pref_box)
        selection.emit('changed')

        right_box = Gtk.VBox()
        right_box.pack_start(pref_box, True, True, 0)
        if not self.has_close_button():
            right_box.pack_start(bb_align, True, True, 0)

        align = Align(right_box, left=6, right=15, top=12, bottom=3)
        paned.pack2(align, True, False)
        paned.set_position(290)

        self.add(paned)

        self.__refill(plv, pref_box, errors, enabled_combo)

        self.connect('destroy', self.__destroy)
        filter_model.set_visible_func(
            self.__filter, (fe, enabled_combo, type_combo))

        self.get_child().show_all()
        fe.grab_focus()

        restore_id = config.get("memory", "plugin_selection")
        plv.select_by_plugin_id(restore_id)
Пример #18
0
class SearchWindow(Dialog):
    def __init__(self, parent, album):
        self.album = album
        self.album.sort(key=lambda s: sort_key(s))

        self._resultlist = ObjectStore()
        self._releasecache = {}
        self._qthread = QueryThread()
        self.current_release = None

        super(SearchWindow, self).__init__(_("MusicBrainz lookup"))

        self.add_button(_("_Cancel"), Gtk.ResponseType.REJECT)
        self.add_icon_button(_("_Save"), Icons.DOCUMENT_SAVE,
                             Gtk.ResponseType.ACCEPT)

        self.set_default_size(650, 500)
        self.set_border_width(5)
        self.set_transient_for(parent)

        save_button = self.get_widget_for_response(Gtk.ResponseType.ACCEPT)
        save_button.set_sensitive(False)

        vb = Gtk.VBox()
        vb.set_spacing(8)

        hb = Gtk.HBox()
        hb.set_spacing(8)
        sq = self.search_query = Gtk.Entry()
        sq.connect('activate', self._do_query)

        sq.set_text(build_query(album))

        lbl = Gtk.Label(label=_("_Query:"))
        lbl.set_use_underline(True)
        lbl.set_mnemonic_widget(sq)
        stb = self.search_button = Gtk.Button(_('S_earch'), use_underline=True)
        stb.connect('clicked', self._do_query)
        hb.pack_start(lbl, False, True, 0)
        hb.pack_start(sq, True, True, 0)
        hb.pack_start(stb, False, True, 0)
        vb.pack_start(hb, False, True, 0)

        self.result_combo = ResultComboBox(self._resultlist)
        self.result_combo.connect('changed', self._result_changed)
        vb.pack_start(self.result_combo, False, True, 0)

        rhb = Gtk.HBox()
        rl = Gtk.Label()
        rl.set_markup(_("Results <i>(drag to reorder)</i>"))
        rl.set_alignment(0, 0.5)
        rhb.pack_start(rl, False, True, 0)
        rl = self.result_label = Gtk.Label(label="")
        rhb.pack_end(rl, False, True, 0)
        vb.pack_start(rhb, False, True, 0)
        sw = Gtk.ScrolledWindow()
        sw.set_shadow_type(Gtk.ShadowType.IN)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.ALWAYS)
        rtv = self.result_treeview = ResultTreeView(self.album)
        rtv.set_border_width(8)
        sw.add(rtv)
        vb.pack_start(sw, True, True, 0)

        self.get_action_area().set_border_width(4)
        self.get_content_area().pack_start(vb, True, True, 0)
        self.connect('response', self._on_response)
        self.connect("destroy", self._on_destroy)

        stb.emit('clicked')
        self.get_child().show_all()

    def _on_destroy(self, *args):
        self._qthread.stop()

    def _on_response(self, widget, response):
        if response != Gtk.ResponseType.ACCEPT:
            self.destroy()
            return

        self._save()

    def _save(self):
        """Writes values to Song objects."""

        year_only = pconfig.getboolean("year_only")
        albumartist = pconfig.getboolean("albumartist")
        artistsort = pconfig.getboolean("artist_sort")
        musicbrainz = pconfig.getboolean("standard")
        labelid = pconfig.getboolean("labelid2")

        for release, track, song in self.result_treeview.iter_tracks():
            meta = build_song_data(release, track)
            apply_options(meta, year_only, albumartist, artistsort,
                          musicbrainz, labelid)
            apply_to_song(meta, song)

        self.destroy()

    def _do_query(self, *args):
        """Search for album using the query text."""

        query = util.gdecode(self.search_query.get_text())

        if not query:
            self.result_label.set_markup("<b>%s</b>" %
                                         _("Please enter a query."))
            self.search_button.set_sensitive(True)
            return

        self.result_label.set_markup("<i>%s</i>" % _(u"Searching…"))

        self._qthread.add(self._process_results, search_releases, query)

    def _process_results(self, results):
        """Called when a query result is returned.

        `results` is None if an error occurred.
        """

        self._resultlist.clear()
        self.search_button.set_sensitive(True)

        if results is None:
            self.result_label.set_text(_("Error encountered. Please retry."))
            self.search_button.set_sensitive(True)
            return

        self._resultlist.append_many(results)

        if len(results) > 0:
            self.result_label.set_markup("<i>%s</i>" % _(u"Loading result…"))
            self.result_combo.set_active(0)
        else:
            self.result_label.set_markup(_("No results found."))

    def _result_changed(self, combo):
        """Called when a release is chosen from the result combo."""

        idx = combo.get_active()
        if idx == -1:
            return
        release = self._resultlist[idx][0]

        if release.id in self._releasecache:
            self._update_result(self._releasecache[release.id])
        else:
            self.result_label.set_markup("<i>%s</i>" % _(u"Loading result…"))
            self.result_treeview.update_release(None)
            self._qthread.add(self._update_result, release.fetch_full)

    def _update_result(self, full_release):
        """Callback for release detail download from result combo."""

        if full_release is None:
            self.result_label.set_text(_("Error encountered. Please retry."))
            return

        self.result_label.set_text(u"")
        self._releasecache.setdefault(full_release.id, full_release)

        self.result_treeview.update_release(full_release)
        self.current_release = full_release
        save_button = self.get_widget_for_response(Gtk.ResponseType.ACCEPT)
        save_button.set_sensitive(True)
Пример #19
0
 def test_validate(self):
     self.failUnlessRaises(ValueError, ObjectStore, int)
     ObjectStore()
     ObjectStore(object)
     self.failUnlessRaises(ValueError, ObjectStore, object, object)
Пример #20
0
    def test_iter_path_changed(self):
        m = ObjectStore()

        def handler(model, path, iter_, result):
            result[0] += 1

        result = [0]
        m.connect("row-changed", handler, result)
        m.append([object()])

        iter_ = m.get_iter_first()
        m.iter_changed(iter_)
        self.assertEqual(result[0], 1)
        m.path_changed(m.get_path(iter_))
        self.assertEqual(result[0], 2)
Пример #21
0
    def test_signal_count(self):
        m = ObjectStore()

        def handler(model, path, iter_, result):
            result[0] += 1

        inserted = [0]
        m.connect("row-inserted", handler, inserted)
        changed = [0]
        m.connect("row-changed", handler, changed)

        m.append([1])
        m.prepend([8])
        m.insert(0, [1])
        m.insert_before(None, [1])
        m.insert_after(None, [1])
        m.insert_many(0, [1, 2, 3])
        m.append_many([1, 2, 3])
        list(m.iter_append_many([1, 2, 3]))
        list(m.iter_append_many(xrange(3)))

        self.assertEqual(changed[0], 0)
        self.assertEqual(inserted[0], len(m))
Пример #22
0
 def test_iter_values(self):
     m = ObjectStore()
     f = ObjectModelSort(model=m)
     m.insert_many(0, range(10))
     self.failUnlessEqual(range(10), list(f.itervalues()))
Пример #23
0
 def test_iterrows_empty(self):
     m = ObjectStore()
     self.assertEqual(list(m.iterrows()), [])
Пример #24
0
 def test_iterrows(self):
     m = ObjectStore()
     m.insert_many(0, range(10))
     for iter_, value in m.iterrows():
         self.failUnlessEqual(m.get_value(iter_), value)
Пример #25
0
 def test_itervalues(self):
     m = ObjectStore()
     m.insert_many(0, range(10))
     self.failUnlessEqual(range(10), list(m.itervalues()))
Пример #26
0
class SearchWindow(Dialog):

    def __init__(self, parent, album):
        self.album = album
        self.album.sort(key=lambda s: sort_key(s))

        self._resultlist = ObjectStore()
        self._releasecache = {}
        self._qthread = QueryThread()
        self.current_release = None

        super(SearchWindow, self).__init__(_("MusicBrainz lookup"))

        self.add_button(_("_Cancel"), Gtk.ResponseType.REJECT)
        self.add_icon_button(_("_Save"), Icons.DOCUMENT_SAVE,
                             Gtk.ResponseType.ACCEPT)

        self.set_default_size(650, 500)
        self.set_border_width(5)
        self.set_transient_for(parent)

        save_button = self.get_widget_for_response(Gtk.ResponseType.ACCEPT)
        save_button.set_sensitive(False)

        vb = Gtk.VBox()
        vb.set_spacing(8)

        hb = Gtk.HBox()
        hb.set_spacing(8)
        sq = self.search_query = Gtk.Entry()
        sq.connect('activate', self._do_query)

        sq.set_text(build_query(album))

        lbl = Gtk.Label(label=_("_Query:"))
        lbl.set_use_underline(True)
        lbl.set_mnemonic_widget(sq)
        stb = self.search_button = Gtk.Button(_('S_earch'), use_underline=True)
        stb.connect('clicked', self._do_query)
        hb.pack_start(lbl, False, True, 0)
        hb.pack_start(sq, True, True, 0)
        hb.pack_start(stb, False, True, 0)
        vb.pack_start(hb, False, True, 0)

        self.result_combo = ResultComboBox(self._resultlist)
        self.result_combo.connect('changed', self._result_changed)
        vb.pack_start(self.result_combo, False, True, 0)

        rhb = Gtk.HBox()
        rl = Gtk.Label()
        rl.set_markup(_("Results <i>(drag to reorder)</i>"))
        rl.set_alignment(0, 0.5)
        rhb.pack_start(rl, False, True, 0)
        rl = self.result_label = Gtk.Label(label="")
        rhb.pack_end(rl, False, True, 0)
        vb.pack_start(rhb, False, True, 0)
        sw = Gtk.ScrolledWindow()
        sw.set_shadow_type(Gtk.ShadowType.IN)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.ALWAYS)
        rtv = self.result_treeview = ResultTreeView(self.album)
        rtv.set_border_width(8)
        sw.add(rtv)
        vb.pack_start(sw, True, True, 0)

        self.get_action_area().set_border_width(4)
        self.get_content_area().pack_start(vb, True, True, 0)
        self.connect('response', self._on_response)
        self.connect("destroy", self._on_destroy)

        stb.emit('clicked')
        self.get_child().show_all()

    def _on_destroy(self, *args):
        self._qthread.stop()

    def _on_response(self, widget, response):
        if response != Gtk.ResponseType.ACCEPT:
            self.destroy()
            return

        self._save()

    def _save(self):
        """Writes values to Song objects."""

        year_only = pconfig.getboolean("year_only")
        albumartist = pconfig.getboolean("albumartist")
        artistsort = pconfig.getboolean("artist_sort")
        musicbrainz = pconfig.getboolean("standard")
        labelid = pconfig.getboolean("labelid2")

        for release, track, song in self.result_treeview.iter_tracks():
            meta = build_song_data(release, track)
            apply_options(
                meta, year_only, albumartist, artistsort, musicbrainz, labelid)
            apply_to_song(meta, song)

        self.destroy()

    def _do_query(self, *args):
        """Search for album using the query text."""

        query = util.gdecode(self.search_query.get_text())

        if not query:
            self.result_label.set_markup(
                "<b>%s</b>" % _("Please enter a query."))
            self.search_button.set_sensitive(True)
            return

        self.result_label.set_markup("<i>%s</i>" % _(u"Searching…"))

        self._qthread.add(self._process_results, search_releases, query)

    def _process_results(self, results):
        """Called when a query result is returned.

        `results` is None if an error occurred.
        """

        self._resultlist.clear()
        self.search_button.set_sensitive(True)

        if results is None:
            self.result_label.set_text(_("Error encountered. Please retry."))
            self.search_button.set_sensitive(True)
            return

        self._resultlist.append_many(results)

        if len(results) > 0:
            self.result_label.set_markup("<i>%s</i>" % _(u"Loading result…"))
            self.result_combo.set_active(0)
        else:
            self.result_label.set_markup(_("No results found."))

    def _result_changed(self, combo):
        """Called when a release is chosen from the result combo."""

        idx = combo.get_active()
        if idx == -1:
            return
        release = self._resultlist[idx][0]

        if release.id in self._releasecache:
            self._update_result(self._releasecache[release.id])
        else:
            self.result_label.set_markup("<i>%s</i>" % _(u"Loading result…"))
            self.result_treeview.update_release(None)
            self._qthread.add(self._update_result, release.fetch_full)

    def _update_result(self, full_release):
        """Callback for release detail download from result combo."""

        if full_release is None:
            self.result_label.set_text(_("Error encountered. Please retry."))
            return

        self.result_label.set_text(u"")
        self._releasecache.setdefault(full_release.id, full_release)

        self.result_treeview.update_release(full_release)
        self.current_release = full_release
        save_button = self.get_widget_for_response(Gtk.ResponseType.ACCEPT)
        save_button.set_sensitive(True)
Пример #27
0
    def __init__(self, parent, library):
        super(RenameFiles, self).__init__(spacing=6)
        self.__skip_interactive = False
        self.set_border_width(12)

        hbox = Gtk.HBox(spacing=6)
        cbes_defaults = NBP_EXAMPLES.split("\n")
        self.combo = ComboBoxEntrySave(NBP,
                                       cbes_defaults,
                                       title=_("Path Patterns"),
                                       edit_title=_(u"Edit saved patterns…"))
        self.combo.show_all()
        hbox.pack_start(self.combo, True, True, 0)
        self.preview = qltk.Button(_("_Preview"), Icons.VIEW_REFRESH)
        self.preview.show()
        hbox.pack_start(self.preview, False, True, 0)
        self.pack_start(hbox, False, True, 0)
        self.combo.get_child().connect('changed', self._changed)

        model = ObjectStore()
        self.view = Gtk.TreeView(model=model)
        self.view.show()

        sw = Gtk.ScrolledWindow()
        sw.set_shadow_type(Gtk.ShadowType.IN)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        sw.add(self.view)
        self.pack_start(sw, True, True, 0)

        self.pack_start(Gtk.VBox(), False, True, 0)

        # rename options
        rename_options = Gtk.HBox()

        # file name options
        filter_box = FilterPluginBox(self.handler, self.FILTERS)
        filter_box.connect("preview", self.__filter_preview)
        filter_box.connect("changed", self.__filter_changed)
        self.filter_box = filter_box

        frame_filename_options = Frame(_("File names"), filter_box)
        frame_filename_options.show_all()
        rename_options.pack_start(frame_filename_options, False, True, 0)

        # album art options
        albumart_box = Gtk.VBox()

        # move art
        moveart_box = Gtk.VBox()
        self.moveart = ConfigCheckButton(_('_Move album art'),
                                         "rename",
                                         "move_art",
                                         populate=True)
        self.moveart.set_tooltip_text(
            _("See '[albumart] filenames' config entry " +
              "for image search strings"))
        self.moveart.show()
        moveart_box.pack_start(self.moveart, False, True, 0)
        self.moveart_overwrite = ConfigCheckButton(
            _('_Overwrite album art at target'),
            "rename",
            "move_art_overwrite",
            populate=True)
        self.moveart_overwrite.show()
        moveart_box.pack_start(self.moveart_overwrite, False, True, 0)
        albumart_box.pack_start(moveart_box, False, True, 0)
        # remove empty
        removeemptydirs_box = Gtk.VBox()
        self.removeemptydirs = ConfigCheckButton(
            _('_Remove empty directories'),
            "rename",
            "remove_empty_dirs",
            populate=True)
        self.removeemptydirs.show()
        removeemptydirs_box.pack_start(self.removeemptydirs, False, True, 0)
        albumart_box.pack_start(removeemptydirs_box, False, True, 0)

        frame_albumart_options = Frame(_("Album art"), albumart_box)
        frame_albumart_options.show_all()
        rename_options.pack_start(frame_albumart_options, False, True, 0)

        self.pack_start(rename_options, False, True, 0)

        # Save button
        self.save = Button(_("_Save"), Icons.DOCUMENT_SAVE)
        self.save.show()
        bbox = Gtk.HButtonBox()
        bbox.set_layout(Gtk.ButtonBoxStyle.END)
        bbox.pack_start(self.save, True, True, 0)
        self.pack_start(bbox, False, True, 0)

        render = Gtk.CellRendererText()
        column = TreeViewColumn(title=_('File'))
        column.pack_start(render, True)

        def cell_data_file(column, cell, model, iter_, data):
            entry = model.get_value(iter_)
            cell.set_property("text", entry.name)

        column.set_cell_data_func(render, cell_data_file)

        column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.view.append_column(column)

        render = Gtk.CellRendererText()
        render.set_property('editable', True)
        column = TreeViewColumn(title=_('New Name'))
        column.pack_start(render, True)

        def cell_data_new_name(column, cell, model, iter_, data):
            entry = model.get_value(iter_)
            cell.set_property("text", entry.new_name or u"")

        column.set_cell_data_func(render, cell_data_new_name)

        column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.view.append_column(column)

        connect_obj(self.preview, 'clicked', self._preview, None)

        connect_obj(parent, 'changed', self.__class__._preview, self)
        connect_obj(self.save, 'clicked', self._rename, library)

        render.connect('edited', self.__row_edited)

        for child in self.get_children():
            child.show()
Пример #28
0
    def __init__(self, parent=None):
        if self.is_not_unique():
            return
        super(PluginWindow, self).__init__()
        self.set_title(_("Plugins"))
        self.set_default_size(700, 500)
        self.set_transient_for(parent)
        self.enable_window_tracking("plugin_prefs")

        paned = Paned()
        vbox = Gtk.VBox()

        sw = ScrolledWindow()
        sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.ALWAYS)

        model = ObjectStore()
        filter_model = ObjectModelFilter(child_model=model)

        tv = PluginListView()
        tv.set_model(filter_model)
        tv.set_rules_hint(True)

        tv.connect("plugin-toggled", self.__plugin_toggled)

        fb = Gtk.HBox(spacing=6)

        filter_combo = PluginFilterCombo()
        filter_combo.connect("changed", lambda s: filter_model.refilter())
        fb.pack_start(filter_combo, False, True, 0)

        filter_entry = ClearEntry()
        filter_entry.connect("changed", lambda s: filter_model.refilter())
        filter_entry.enable_clear_button()
        fb.pack_start(filter_entry, True, True, 0)

        sw.add(tv)
        sw.set_shadow_type(Gtk.ShadowType.IN)
        sw.set_size_request(200, -1)

        bbox = Gtk.HBox(homogeneous=True, spacing=12)

        errors = qltk.Button(_("Show _Errors"), Icons.DIALOG_WARNING)
        errors.set_focus_on_click(False)
        errors.connect('clicked', self.__show_errors)
        errors.set_no_show_all(True)
        bbox.pack_start(Align(errors, border=6, right=-6), True, True, 0)

        pref_box = PluginPreferencesContainer()

        if const.DEBUG:
            refresh = qltk.Button(_("_Refresh"), Icons.VIEW_REFRESH)
            refresh.set_focus_on_click(False)
            refresh.connect('clicked', self.__refresh, tv, pref_box, errors,
                            filter_combo)
            bbox.pack_start(Align(refresh, border=6), True, True, 0)

        vbox.pack_start(Align(fb, border=6, right=-6), False, True, 0)
        vbox.pack_start(sw, True, True, 0)
        vbox.pack_start(bbox, False, True, 0)
        paned.pack1(vbox, False, False)

        close = qltk.Button(_("_Close"), Icons.WINDOW_CLOSE)
        close.connect('clicked', lambda *x: self.destroy())
        bb_align = Align(halign=Gtk.Align.END, valign=Gtk.Align.END)
        bb = Gtk.HButtonBox()
        bb.set_layout(Gtk.ButtonBoxStyle.END)
        bb.pack_start(close, True, True, 0)
        bb_align.add(bb)

        selection = tv.get_selection()
        selection.connect('changed', self.__selection_changed, pref_box)
        selection.emit('changed')

        right_box = Gtk.VBox(spacing=12)
        right_box.pack_start(pref_box, True, True, 0)
        self.use_header_bar()
        if not self.has_close_button():
            right_box.pack_start(bb_align, True, True, 0)

        paned.pack2(Align(right_box, border=12), True, False)
        paned.set_position(250)

        self.add(paned)

        self.__refill(tv, pref_box, errors, filter_combo)

        self.connect('destroy', self.__destroy)
        filter_model.set_visible_func(self.__filter,
                                      (filter_entry, filter_combo))

        self.get_child().show_all()
        filter_entry.grab_focus()

        restore_id = config.get("memory", "plugin_selection")
        tv.select_by_plugin_id(restore_id)
Пример #29
0
class PlaylistsBrowser(Browser, DisplayPatternMixin):

    name = _("Playlists")
    accelerated_name = _("_Playlists")
    keys = ["Playlists", "PlaylistsBrowser"]
    priority = 2
    replaygain_profiles = ["track"]
    __last_render = None
    _PATTERN_FN = os.path.join(quodlibet.get_user_dir(), "playlist_pattern")
    _DEFAULT_PATTERN_TEXT = DEFAULT_PATTERN_TEXT

    def pack(self, songpane):
        self._main_box.pack1(self, True, False)
        self._rh_box = rhbox = Gtk.VBox(spacing=6)
        align = Align(self._sb_box, left=0, right=6, top=6)
        rhbox.pack_start(align, False, True, 0)
        rhbox.pack_start(songpane, True, True, 0)
        self._main_box.pack2(rhbox, True, False)
        rhbox.show()
        align.show_all()
        return self._main_box

    def unpack(self, container, songpane):
        self._rh_box.remove(songpane)
        container.remove(self._rh_box)
        container.remove(self)

    @classmethod
    def init(klass, library):
        klass.library = library
        model = klass.__lists.get_model()
        for playlist in os.listdir(PLAYLISTS):
            try:
                playlist = FileBackedPlaylist(
                    PLAYLISTS,
                    FileBackedPlaylist.unquote(playlist),
                    library=library)
                model.append(row=[playlist])
            except EnvironmentError:
                print_w("Invalid Playlist '%s'" % playlist)
                pass

        klass._ids = [
            library.connect('removed', klass.__removed),
            library.connect('added', klass.__added),
            library.connect('changed', klass.__changed),
        ]
        klass.load_pattern()

    @classmethod
    def deinit(cls, library):
        model = cls.__lists.get_model()
        model.clear()

        for id_ in cls._ids:
            library.disconnect(id_)
        del cls._ids

    @classmethod
    def playlists(klass):
        return [row[0] for row in klass.__lists]

    @classmethod
    def changed(klass, playlist, refresh=True):
        model = klass.__lists
        for row in model:
            if row[0] is playlist:
                if refresh:
                    print_d("Refreshing playlist %s..." % row[0])
                    klass.__lists.row_changed(row.path, row.iter)
                playlist.write()
                break
        else:
            model.get_model().append(row=[playlist])
            playlist.write()

    @classmethod
    def __removed(klass, library, songs):
        for playlist in klass.playlists():
            if playlist.remove_songs(songs):
                klass.changed(playlist)

    @classmethod
    def __added(klass, library, songs):
        filenames = {song("~filename") for song in songs}
        for playlist in klass.playlists():
            if playlist.add_songs(filenames, library):
                klass.changed(playlist)

    @classmethod
    def __changed(klass, library, songs):
        for playlist in klass.playlists():
            for song in songs:
                if song in playlist.songs:
                    klass.changed(playlist)
                    break

    def cell_data(self, col, cell, model, iter, data):
        playlist = model[iter][0]
        cell.markup = markup = self.display_pattern % playlist
        if self.__last_render == markup:
            return
        self.__last_render = markup
        cell.markup = markup
        cell.set_property('markup', markup)

    def Menu(self, songs, library, items):
        model, iters = self.__get_selected_songs()
        remove = qltk.MenuItem(_("_Remove from Playlist"), Icons.LIST_REMOVE)
        qltk.add_fake_accel(remove, "Delete")
        connect_obj(remove, 'activate', self.__remove, iters, model)
        playlist_iter = self.__view.get_selection().get_selected()[1]
        remove.set_sensitive(bool(playlist_iter))
        items.append([remove])
        menu = super(PlaylistsBrowser, self).Menu(songs, library, items)
        return menu

    def __get_selected_songs(self):
        songlist = qltk.get_top_parent(self).songlist
        model, rows = songlist.get_selection().get_selected_rows()
        iters = map(model.get_iter, rows)
        return model, iters

    __lists = ObjectModelSort(model=ObjectStore())
    __lists.set_default_sort_func(ObjectStore._sort_on_value)

    def __init__(self, library):
        self.library = library
        super(PlaylistsBrowser, self).__init__(spacing=6)
        self.set_orientation(Gtk.Orientation.VERTICAL)
        self.__render = self.__create_cell_renderer()
        self.__view = view = self.__create_playlists_view(self.__render)
        self.__embed_in_scrolledwin(view)
        self.__configure_buttons(library)
        self.__configure_dnd(view, library)
        self.__connect_signals(view, library)
        self._sb_box = self.__create_searchbar(library)
        self._main_box = self.__create_box()
        self.show_all()
        self._query = None

        for child in self.get_children():
            child.show_all()

    def __destroy(self, *args):
        del self._sb_box

    def __create_box(self):
        box = qltk.ConfigRHPaned("browsers", "playlistsbrowser_pos", 0.4)
        box.show_all()
        return box

    def __create_searchbar(self, library):
        self.accelerators = Gtk.AccelGroup()
        completion = LibraryTagCompletion(library.librarian)
        sbb = SearchBarBox(completion=completion,
                           accel_group=self.accelerators)
        sbb.connect('query-changed', self.__text_parse)
        sbb.connect('focus-out', self.__focus)
        return sbb

    def __embed_in_scrolledwin(self, view):
        swin = ScrolledWindow()
        swin.set_shadow_type(Gtk.ShadowType.IN)
        swin.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
        swin.add(view)
        self.pack_start(swin, True, True, 0)

    def __configure_buttons(self, library):
        new_pl = qltk.Button(_("_New"), Icons.DOCUMENT_NEW, Gtk.IconSize.MENU)
        new_pl.connect('clicked', self.__new_playlist)
        import_pl = qltk.Button(_("_Import"), Icons.LIST_ADD,
                                Gtk.IconSize.MENU)
        import_pl.connect('clicked', self.__import, library)
        hb = Gtk.HBox(spacing=6)
        hb.set_homogeneous(False)
        hb.pack_start(new_pl, True, True, 0)
        hb.pack_start(import_pl, True, True, 0)
        hb2 = Gtk.HBox(spacing=0)
        hb2.pack_start(hb, True, True, 0)
        hb2.pack_start(PreferencesButton(self), False, False, 6)
        self.pack_start(Align(hb2, left=3, bottom=3), False, False, 0)

    def __create_playlists_view(self, render):
        view = RCMHintedTreeView()
        view.set_enable_search(True)
        view.set_search_column(0)
        view.set_search_equal_func(
            lambda model, col, key, iter, data: not model[iter][col].name.
            lower().startswith(key.lower()), None)
        col = Gtk.TreeViewColumn("Playlists", render)
        col.set_cell_data_func(render, self.cell_data)
        view.append_column(col)
        view.set_model(self.__lists)
        view.set_rules_hint(True)
        view.set_headers_visible(False)
        return view

    def __configure_dnd(self, view, library):
        targets = [("text/x-quodlibet-songs", Gtk.TargetFlags.SAME_APP,
                    DND_QL), ("text/uri-list", 0, DND_URI_LIST),
                   ("text/x-moz-url", 0, DND_MOZ_URL)]
        targets = [Gtk.TargetEntry.new(*t) for t in targets]
        view.drag_dest_set(Gtk.DestDefaults.ALL, targets, Gdk.DragAction.COPY)
        view.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, targets[:2],
                             Gdk.DragAction.COPY)
        view.connect('drag-data-received', self.__drag_data_received, library)
        view.connect('drag-data-get', self.__drag_data_get)
        view.connect('drag-motion', self.__drag_motion)
        view.connect('drag-leave', self.__drag_leave)

    def __connect_signals(self, view, library):
        view.connect('row-activated', lambda *x: self.songs_activated())
        view.connect('popup-menu', self.__popup_menu, library)
        view.get_selection().connect('changed', self.activate)
        model = view.get_model()
        s = model.connect('row-changed', self.__check_current)
        connect_obj(self, 'destroy', model.disconnect, s)
        self.connect('key-press-event', self.__key_pressed)

    def __create_cell_renderer(self):
        render = Gtk.CellRendererText()
        render.set_padding(3, 3)
        render.set_property('ellipsize', Pango.EllipsizeMode.END)
        render.connect('editing-started', self.__start_editing)
        render.connect('edited', self.__edited)
        return render

    def key_pressed(self, event):
        if qltk.is_accel(event, "Delete"):
            self.__handle_songlist_delete()
            return True
        return False

    def __handle_songlist_delete(self, *args):
        model, iters = self.__get_selected_songs()
        self.__remove(iters, model)

    def __key_pressed(self, widget, event):
        if qltk.is_accel(event, "Delete"):
            model, iter = self.__view.get_selection().get_selected()
            if not iter:
                return False

            playlist = model[iter][0]
            dialog = ConfirmRemovePlaylistDialog(self, playlist)
            if dialog.run() == Gtk.ResponseType.YES:
                playlist.delete()
                model.get_model().remove(
                    model.convert_iter_to_child_iter(iter))
            return True
        elif qltk.is_accel(event, "F2"):
            model, iter = self.__view.get_selection().get_selected()
            if iter:
                self._start_rename(model.get_path(iter))
            return True
        return False

    def __check_current(self, model, path, iter):
        model, citer = self.__view.get_selection().get_selected()
        if citer and model.get_path(citer) == path:
            songlist = qltk.get_top_parent(self).songlist
            self.activate(resort=not songlist.is_sorted())

    def __drag_motion(self, view, ctx, x, y, time):
        targets = [t.name() for t in ctx.list_targets()]
        if "text/x-quodlibet-songs" in targets:
            view.set_drag_dest(x, y, into_only=True)
            return True
        else:
            # Highlighting the view itself doesn't work.
            view.get_parent().drag_highlight()
            return True

    def __drag_leave(self, view, ctx, time):
        view.get_parent().drag_unhighlight()

    def __remove(self, iters, smodel):
        def song_at(itr):
            return smodel[smodel.get_path(itr)][0]

        def remove_from_model(iters, smodel):
            for it in iters:
                smodel.remove(it)

        model, iter = self.__view.get_selection().get_selected()
        if iter:
            playlist = model[iter][0]
            removals = [song_at(iter_remove) for iter_remove in iters]
            if self._query is None or not self.get_filter_text():
                # Calling playlist.remove_songs(songs) won't remove the
                # right ones if there are duplicates
                remove_from_model(iters, smodel)
                self.__rebuild_playlist_from_songs_model(playlist, smodel)
                # Emit manually
                self.library.emit('changed', removals)
            else:
                print_d("Removing %d song(s) from %s" %
                        (len(removals), playlist))
                playlist.remove_songs(removals, True)
                remove_from_model(iters, smodel)
            self.changed(playlist)
            self.activate()

    def __rebuild_playlist_from_songs_model(self, playlist, smodel):
        playlist.inhibit = True
        playlist.clear()
        playlist.extend([row[0] for row in smodel])
        playlist.inhibit = False

    def __drag_data_received(self, view, ctx, x, y, sel, tid, etime, library):
        # TreeModelSort doesn't support GtkTreeDragDestDrop.
        view.emit_stop_by_name('drag-data-received')
        model = view.get_model()
        if tid == DND_QL:
            filenames = qltk.selection_get_filenames(sel)
            songs = filter(None, map(library.get, filenames))
            if not songs:
                Gtk.drag_finish(ctx, False, False, etime)
                return
            try:
                path, pos = view.get_dest_row_at_pos(x, y)
            except TypeError:
                playlist = FileBackedPlaylist.from_songs(
                    PLAYLISTS, songs, library)
                GLib.idle_add(self._select_playlist, playlist)
            else:
                playlist = model[path][0]
                playlist.extend(songs)
            self.changed(playlist)
            Gtk.drag_finish(ctx, True, False, etime)
        else:
            if tid == DND_URI_LIST:
                uri = sel.get_uris()[0]
                name = os.path.basename(uri)
            elif tid == DND_MOZ_URL:
                data = sel.get_data()
                uri, name = data.decode('utf16', 'replace').split('\n')
            else:
                Gtk.drag_finish(ctx, False, False, etime)
                return
            name = name or os.path.basename(uri) or _("New Playlist")
            uri = uri.encode('utf-8')
            try:
                sock = urllib.urlopen(uri)
                f = NamedTemporaryFile()
                f.write(sock.read())
                f.flush()
                if uri.lower().endswith('.pls'):
                    playlist = parse_pls(f.name, library=library)
                elif uri.lower().endswith('.m3u'):
                    playlist = parse_m3u(f.name, library=library)
                else:
                    raise IOError
                library.add_filename(playlist)
                if name:
                    playlist.rename(name)
                self.changed(playlist)
                Gtk.drag_finish(ctx, True, False, etime)
            except IOError:
                Gtk.drag_finish(ctx, False, False, etime)
                qltk.ErrorMessage(
                    qltk.get_top_parent(self), _("Unable to import playlist"),
                    _("Quod Libet can only import playlists in the M3U "
                      "and PLS formats.")).run()

    def __drag_data_get(self, view, ctx, sel, tid, etime):
        model, iters = self.__view.get_selection().get_selected_rows()
        songs = []
        for iter in filter(lambda i: i, iters):
            songs += list(model[iter][0])
        if tid == 0:
            qltk.selection_set_songs(sel, songs)
        else:
            sel.set_uris([song("~uri") for song in songs])

    def _select_playlist(self, playlist, scroll=False):
        view = self.__view
        model = view.get_model()
        for row in model:
            if row[0] is playlist:
                view.get_selection().select_iter(row.iter)
                if scroll:
                    view.scroll_to_cell(row.path,
                                        use_align=True,
                                        row_align=0.5)

    def __popup_menu(self, view, library):
        model, itr = view.get_selection().get_selected()
        if itr is None:
            return
        songs = list(model[itr][0])
        songs = filter(lambda s: isinstance(s, AudioFile), songs)
        menu = SongsMenu(library,
                         songs,
                         playlists=False,
                         remove=False,
                         ratings=False)
        menu.preseparate()

        def _remove(model, itr):
            playlist = model[itr][0]
            dialog = ConfirmRemovePlaylistDialog(self, playlist)
            if dialog.run() == Gtk.ResponseType.YES:
                playlist.delete()
                model.get_model().remove(model.convert_iter_to_child_iter(itr))

        rem = MenuItem(_("_Delete"), Icons.EDIT_DELETE)
        connect_obj(rem, 'activate', _remove, model, itr)
        menu.prepend(rem)

        def _rename(path):
            self._start_rename(path)

        ren = qltk.MenuItem(_("_Rename"), Icons.EDIT)
        qltk.add_fake_accel(ren, "F2")
        connect_obj(ren, 'activate', _rename, model.get_path(itr))
        menu.prepend(ren)

        playlist = model[itr][0]
        PLAYLIST_HANDLER.populate_menu(menu, library, self, [playlist])
        menu.show_all()
        return view.popup_menu(menu, 0, Gtk.get_current_event_time())

    def _start_rename(self, path):
        view = self.__view
        self.__render.set_property('editable', True)
        view.set_cursor(path, view.get_columns()[0], start_editing=True)

    def __focus(self, widget, *args):
        qltk.get_top_parent(widget).songlist.grab_focus()

    def __text_parse(self, bar, text):
        self.activate()

    def _set_text(self, text):
        self._sb_box.set_text(text)

    def activate(self, widget=None, resort=True):
        songs = self._get_playlist_songs()

        text = self.get_filter_text()
        # TODO: remove static dependency on Query
        if Query.is_parsable(text):
            self._query = Query(text, SongList.star)
            songs = self._query.filter(songs)
        GLib.idle_add(self.songs_selected, songs, resort)

    @classmethod
    def refresh_all(cls):
        model = cls.__lists.get_model()
        for iter_, value in model.iterrows():
            model.row_changed(model.get_path(iter_), iter_)

    @property
    def model(self):
        return self.__lists.get_model()

    def _get_playlist_songs(self):
        model, iter = self.__view.get_selection().get_selected()
        songs = iter and list(model[iter][0]) or []
        songs = filter(lambda s: isinstance(s, AudioFile), songs)
        return songs

    def can_filter_text(self):
        return True

    def filter_text(self, text):
        self._set_text(text)
        self.activate()

    def get_filter_text(self):
        return self._sb_box.get_text()

    def can_filter(self, key):
        # TODO: special-case the ~playlists tag maybe?
        return super(PlaylistsBrowser, self).can_filter(key)

    def finalize(self, restore):
        config.set("browsers", "query_text", "")

    def unfilter(self):
        self.filter_text("")

    def active_filter(self, song):
        return (song in self._get_playlist_songs()
                and (self._query is None or self._query.search(song)))

    def save(self):
        model, iter = self.__view.get_selection().get_selected()
        name = iter and model[iter][0].name or ""
        config.set("browsers", "playlist", name)
        text = self.get_filter_text()
        config.set("browsers", "query_text", text)

    def __new_playlist(self, activator):
        playlist = FileBackedPlaylist.new(PLAYLISTS)
        self.model.append(row=[playlist])
        self._select_playlist(playlist, scroll=True)

        model, iter = self.__view.get_selection().get_selected()
        path = model.get_path(iter)
        GLib.idle_add(self._start_rename, path)

    def __start_editing(self, render, editable, path):
        editable.set_text(self.__lists[path][0].name)

    def __edited(self, render, path, newname):
        return self._rename(path, newname)

    def _rename(self, path, newname):
        playlist = self.__lists[path][0]
        try:
            playlist.rename(newname)
        except ValueError as s:
            qltk.ErrorMessage(None, _("Unable to rename playlist"), s).run()
        else:
            row = self.__lists[path]
            child_model = self.model
            child_model.remove(
                self.__lists.convert_iter_to_child_iter(row.iter))
            child_model.append(row=[playlist])
            self._select_playlist(playlist, scroll=True)

    def __import(self, activator, library):
        filt = lambda fn: fn.endswith(".pls") or fn.endswith(".m3u")
        from quodlibet.qltk.chooser import FileChooser
        chooser = FileChooser(self, _("Import Playlist"), filt, get_home_dir())
        files = chooser.run()
        chooser.destroy()
        for filename in files:
            if filename.endswith(".m3u"):
                playlist = parse_m3u(filename, library=library)
            elif filename.endswith(".pls"):
                playlist = parse_pls(filename, library=library)
            else:
                qltk.ErrorMessage(
                    qltk.get_top_parent(self), _("Unable to import playlist"),
                    _("Quod Libet can only import playlists in the M3U "
                      "and PLS formats.")).run()
                return
            self.changed(playlist)
            library.add(playlist)

    def restore(self):
        try:
            name = config.get("browsers", "playlist")
        except config.Error as e:
            print_d("Couldn't get last playlist from config: %s" % e)
        else:
            self.__view.select_by_func(lambda r: r[0].name == name, one=True)
        try:
            text = config.get("browsers", "query_text")
        except config.Error as e:
            print_d("Couldn't get last search string from config: %s" % e)
        else:
            self._set_text(text)

    can_reorder = True

    def scroll(self, song):
        self.__view.iter_select_by_func(lambda r: song in r[0])

    def reordered(self, songs):
        model, iter = self.__view.get_selection().get_selected()
        playlist = None
        if iter:
            playlist = model[iter][0]
            playlist[:] = songs
        elif songs:
            playlist = FileBackedPlaylist.from_songs(PLAYLISTS, songs)
            GLib.idle_add(self._select_playlist, playlist)
        if playlist:
            self.changed(playlist, refresh=False)
Пример #30
0
    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 (&lt; / &gt;).") %
                (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)
Пример #31
0
 def test_append_many(self):
     m = ObjectStore()
     m.append_many(range(10))
     self.failUnlessEqual([r[0] for r in m], range(10))
Пример #32
0
    def __init__(self, prop, library):
        super().__init__(spacing=6)
        self.title = _("Track Numbers")
        self.set_border_width(12)

        label_start = Gtk.Label(label=_("Start fro_m:"), halign=Gtk.Align.END)
        label_start.set_use_underline(True)
        spin_start = Gtk.SpinButton()
        spin_start.set_range(0, 999)
        spin_start.set_increments(1, 10)
        spin_start.set_value(1)
        label_start.set_mnemonic_widget(spin_start)

        label_total = Gtk.Label(label=_("_Total tracks:"),
                                halign=Gtk.Align.END)
        label_total.set_use_underline(True)
        spin_total = Gtk.SpinButton()
        spin_total.set_range(0, 999)
        spin_total.set_increments(1, 10)
        label_total.set_mnemonic_widget(spin_total)

        preview = qltk.Button(_("_Preview"), Icons.VIEW_REFRESH)

        grid = Gtk.Grid(row_spacing=4, column_spacing=4)
        grid.add(label_start)
        grid.attach_next_to(spin_start, label_start, Gtk.PositionType.RIGHT, 1,
                            1)
        grid.attach_next_to(label_total, label_start, Gtk.PositionType.BOTTOM,
                            1, 1)
        grid.attach_next_to(spin_total, label_total, Gtk.PositionType.RIGHT, 1,
                            1)
        grid.attach_next_to(Align(preview, halign=Gtk.Align.END), spin_start,
                            Gtk.PositionType.RIGHT, 1, 1)
        preview.props.hexpand = True

        model = ObjectStore()
        view = HintedTreeView(model=model)

        self.pack_start(grid, False, True, 0)

        render = Gtk.CellRendererText()
        column = TreeViewColumn(title=_('File'))
        column.pack_start(render, True)
        column.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)

        column.set_cell_data_func(render, cell_data_file)

        view.append_column(column)
        render = Gtk.CellRendererText()
        render.set_property('editable', True)
        column = TreeViewColumn(title=_('Track'))
        column.pack_start(render, True)
        column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)

        def cell_data_track(column, cell, model, iter_, data):
            entry = model.get_value(iter_)
            cell.set_property("text", entry.tracknumber)

        column.set_cell_data_func(render, cell_data_track)

        view.append_column(column)
        view.set_reorderable(True)
        w = Gtk.ScrolledWindow()
        w.set_shadow_type(Gtk.ShadowType.IN)
        w.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        w.add(view)
        self.pack_start(w, True, True, 0)

        bbox = Gtk.HButtonBox()
        bbox.set_spacing(6)
        bbox.set_layout(Gtk.ButtonBoxStyle.END)
        save = Button(_("_Save"), Icons.DOCUMENT_SAVE)
        self.save = save
        connect_obj(save, 'clicked', self.__save_files, prop, model, library)
        revert = Button(_("_Revert"), Icons.DOCUMENT_REVERT)
        self.revert = revert
        bbox.pack_start(revert, True, True, 0)
        bbox.pack_start(save, True, True, 0)
        self.pack_start(bbox, False, True, 0)

        preview_args = [spin_start, spin_total, model, save, revert]
        preview.connect('clicked', self.__preview_tracks, *preview_args)
        connect_obj(revert, 'clicked', self.__update, None, *preview_args[1:])
        spin_total.connect('value-changed', self.__preview_tracks,
                           *preview_args)
        spin_start.connect('value-changed', self.__preview_tracks,
                           *preview_args)
        connect_obj(view, 'drag-end', self.__class__.__preview_tracks, self,
                    *preview_args)
        render.connect('edited', self.__row_edited, model, preview, save)

        connect_obj(prop, 'changed', self.__class__.__update, self, spin_total,
                    model, save, revert)

        for child in self.get_children():
            child.show_all()
Пример #33
0
 def test_append_many_set(self):
     m = ObjectStore()
     m.append_many(set())
     m.append_many(set(range(10)))
     self.failUnlessEqual({r[0] for r in m}, set(range(10)))
Пример #34
0
    def test_signal_count(self):
        m = ObjectStore()

        def handler(model, path, iter_, result):
            result[0] += 1

        inserted = [0]
        m.connect("row-inserted", handler, inserted)
        changed = [0]
        m.connect("row-changed", handler, changed)

        m.append([1])
        m.prepend([8])
        m.insert(0, [1])
        m.insert_before(None, [1])
        m.insert_after(None, [1])
        m.insert_many(0, [1, 2, 3])
        m.append_many([1, 2, 3])
        list(m.iter_append_many([1, 2, 3]))
        list(m.iter_append_many(xrange(3)))

        self.assertEqual(changed[0], 0)
        self.assertEqual(inserted[0], len(m))
Пример #35
0
 def test_iter_append_many(self):
     m = ObjectStore()
     iters = list(m.iter_append_many(range(10)))
     self.failUnlessEqual([r[0] for r in m], range(10))
     values = [m.get_value(i) for i in iters]
     self.failUnlessEqual(values, range(10))
Пример #36
0
    def __init__(self, songs, title=None):
        super().__init__(
            default_width=800, default_height=400, border_width=12,
            title=title)

        self._thread = AcoustidLookupThread(self.__lookup_cb)

        sw = Gtk.ScrolledWindow()
        sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
        sw.set_shadow_type(Gtk.ShadowType.IN)

        model = ObjectStore()
        self.view = view = ResultView()
        view.set_model(model)
        self.model = model

        self._iter_map = {}
        for song in songs:
            iter_ = self.model.append([SearchEntry(song)])
            self._iter_map[song] = iter_

        sw.add(view)

        self.pool = pool = FingerPrintPool()
        pool.connect('fingerprint-done', self.__fp_done_cb)
        pool.connect('fingerprint-error', self.__fp_error_cb)
        pool.connect('fingerprint-started', self.__fp_started_cb)
        for song in songs:
            pool.push(song)

        outer_box = Gtk.VBox(spacing=12)

        bbox = Gtk.HButtonBox()
        bbox.set_layout(Gtk.ButtonBoxStyle.END)
        bbox.set_spacing(6)
        self.__save = save = Button(_("_Save"), Icons.DOCUMENT_SAVE)
        save.connect("clicked", self.__on_save)
        save.set_sensitive(False)
        cancel = Button(_("_Cancel"))
        cancel.connect("clicked", lambda *x: self.destroy())
        bbox.pack_start(save, True, True, 0)
        bbox.pack_start(cancel, True, True, 0)

        inner_box = Gtk.VBox(spacing=6)
        inner_box.pack_start(sw, True, True, 0)

        ccb = ConfigCheckButton(
            _("Write MusicBrainz tags"),
            "plugins", "fingerprint_write_mb_tags")
        ccb.set_active(get_write_mb_tags())
        inner_box.pack_start(ccb, False, True, 0)

        ccb = ConfigCheckButton(
            _("Group by directory"),
            "plugins", "fingerprint_group_by_dir")
        ccb.set_active(get_group_by_dir())
        ccb.connect("toggled", self.__group_toggled)
        self._group_ccb = ccb

        outer_box.pack_start(inner_box, True, True, 0)

        bottom_box = Gtk.HBox(spacing=12)
        mode_button = Gtk.ToggleButton(label=_("Album Mode"))
        mode_button.set_tooltip_text(
            _("Write album related tags and try to "
              "reduce the number of different album releases"))
        mode_button.set_active(True)
        mode_button.connect("toggled", self.__mode_toggle)
        bottom_box.pack_start(mode_button, False, True, 0)
        bottom_box.pack_start(self._group_ccb, False, True, 0)
        bottom_box.pack_start(bbox, True, True, 0)

        outer_box.pack_start(bottom_box, False, True, 0)

        outer_box.show_all()
        self.add(outer_box)

        self.__album_mode = True
        self.__group_by_dir = True
        self._release_scores = {}
        self._directory_scores = {}
        self.__done = 0

        self.connect("destroy", self.__destroy)
Пример #37
0
 def test_iter_append_many_iterable_int(self):
     m = ObjectStore()
     for x in m.iter_append_many((i for i in xrange(10))):
         pass
     self.failUnlessEqual([r[0] for r in m], range(10))
Пример #38
0
    def __init__(self, library, songs, parent=None):
        super(SongProperties, self).__init__(dialog=False)
        self.set_transient_for(qltk.get_top_parent(parent))

        default_width = 600
        config_suffix = ""
        if len(songs) <= 1:
            default_width -= 200
            config_suffix += "single"
        self.set_default_size(default_width, 400)

        self.enable_window_tracking("quodlibet_properties",
                                    size_suffix=config_suffix)

        self.auto_save_on_change = config.getboolean(
                'editing', 'auto_save_changes', False)

        paned = ConfigRPaned("memory", "quodlibet_properties_pos", 0.4)
        notebook = qltk.Notebook()
        notebook.props.scrollable = True
        pages = []
        pages.extend([Ctr(self, library) for Ctr in
                      [EditTags, TagsFromPath, RenameFiles]])
        if len(songs) > 1:
            pages.append(TrackNumbers(self, library))
        for page in pages:
            page.show()
            notebook.append_page(page)

        fbasemodel = ObjectStore()
        fmodel = ObjectModelSort(model=fbasemodel)
        fview = HintedTreeView(model=fmodel)
        fview.connect('button-press-event', self.__pre_selection_changed)
        fview.set_rules_hint(True)
        selection = fview.get_selection()
        selection.set_mode(Gtk.SelectionMode.MULTIPLE)
        self.__save = None

        render = Gtk.CellRendererText()
        c1 = Gtk.TreeViewColumn(_('File'), render)
        if fview.supports_hints():
            render.set_property('ellipsize', Pango.EllipsizeMode.END)
        render.set_property('xpad', 3)

        def cell_data(column, cell, model, iter_, data):
            entry = model.get_value(iter_)
            cell.set_property('text', entry.name)

        c1.set_cell_data_func(render, cell_data)

        def sort_func(model, a, b, data):
            a = model.get_value(a)
            b = model.get_value(b)
            return cmp(a.name, b.name)

        fmodel.set_sort_func(100, sort_func)
        c1.set_sort_column_id(100)
        fview.append_column(c1)

        sw = ScrolledWindow()
        sw.add(fview)
        sw.set_shadow_type(Gtk.ShadowType.IN)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)

        # only show the list if there are is more than one song
        if len(songs) > 1:
            sw.show_all()

        paned.pack1(sw, shrink=False, resize=True)

        for song in songs:
            fbasemodel.append(row=[_ListEntry(song)])

        self.connect("changed", self.__on_changed)

        selection.select_all()
        paned.pack2(notebook, shrink=False, resize=True)

        csig = selection.connect('changed', self.__selection_changed)
        connect_destroy(library,
            'changed', self.__on_library_changed, fbasemodel, fview)
        connect_destroy(library,
            'removed', self.__on_library_removed, fbasemodel, selection, csig)

        self.emit('changed', songs)
        self.add(paned)
        paned.set_position(175)
        notebook.show()
        paned.show()
Пример #39
0
 def test_iter_append_many_iterable_object(self):
     objects = [object() for i in xrange(10)]
     m = ObjectStore()
     for x in m.iter_append_many((i for i in objects)):
         pass
     self.failUnlessEqual([r[0] for r in m], objects)
Пример #40
0
    def __preview(self, songs):
        if songs is None:
            songs = [row[0].song for row in (self.view.get_model() or [])]

        if songs:
            pattern_text = gdecode(self.combo.get_child().get_text())
        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 (&lt; / &gt;).") % (
                util.escape(pattern_text))).run()
            return
        else:
            if pattern_text:
                self.combo.prepend_text(pattern_text)
                self.combo.write(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(title=_('File'))
        col.pack_start(render, True)
        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)
Пример #41
0
    def __init__(self, initial=None, filter=filesel_filter, folders=None):
        """
        initial -- a path to a file which should be shown initially
        filter -- a function which filters paths shown in the file list
        folders -- list of shown folders in the directory tree
        """

        super(FileSelector, self).__init__()
        self.__filter = filter

        if initial is not None:
            initial = util.fsnative(initial)

        if initial and os.path.isfile(initial):
            initial = os.path.dirname(initial)
        dirlist = DirectoryTree(initial, folders=folders)

        model = ObjectStore()
        filelist = AllTreeView(model)

        column = TreeViewColumn(_("Songs"))
        column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        render = Gtk.CellRendererPixbuf()
        render.set_property('stock_id', Gtk.STOCK_FILE)
        render.props.xpad = 3
        column.pack_start(render, False)
        render = Gtk.CellRendererText()
        column.pack_start(render, True)

        def cell_data(column, cell, model, iter_, userdata):
            value = model.get_value(iter_)
            cell.set_property('text', fsdecode(os.path.basename(value)))

        column.set_cell_data_func(render, cell_data)

        filelist.append_column(column)
        filelist.set_rules_hint(True)
        filelist.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)
        filelist.set_search_equal_func(search_func, False)
        # Allow to drag and drop files from outside
        filelist.enable_model_drag_dest([], Gdk.DragAction.COPY)
        filelist.drag_dest_add_uri_targets()
        filelist.connect('drag-data-received', self.__file_dropped)

        self.__sig = filelist.get_selection().connect('changed',
                                                      self.__changed)

        dirlist.get_selection().connect('changed',
                                        self.__dir_selection_changed, filelist)
        dirlist.get_selection().emit('changed')

        def select_all_files(view, path, col, fileselection):
            view.expand_row(path, False)
            fileselection.select_all()

        dirlist.connect('row-activated', select_all_files,
                        filelist.get_selection())

        sw = ScrolledWindow()
        sw.add(dirlist)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        sw.set_shadow_type(Gtk.ShadowType.IN)
        self.pack1(sw, resize=True)

        sw = ScrolledWindow()
        sw.add(filelist)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        sw.set_shadow_type(Gtk.ShadowType.IN)
        self.pack2(sw, resize=True)
Пример #42
0
    def __init__(self, parent, library):
        super(RenameFiles, self).__init__(spacing=6)
        self.set_border_width(12)

        hbox = Gtk.HBox(spacing=6)
        cbes_defaults = NBP_EXAMPLES.split("\n")
        self.combo = ComboBoxEntrySave(NBP,
                                       cbes_defaults,
                                       title=_("Path Patterns"),
                                       edit_title=_(u"Edit saved patterns…"))
        self.combo.show_all()
        hbox.pack_start(self.combo, True, True, 0)
        self.preview = qltk.Button(_("_Preview"), Icons.VIEW_REFRESH)
        self.preview.show()
        hbox.pack_start(self.preview, False, True, 0)
        self.pack_start(hbox, False, True, 0)
        self.combo.get_child().connect('changed', self._changed)

        model = ObjectStore()
        self.view = Gtk.TreeView(model=model)
        self.view.show()

        sw = Gtk.ScrolledWindow()
        sw.set_shadow_type(Gtk.ShadowType.IN)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        sw.add(self.view)
        self.pack_start(sw, True, True, 0)

        self.pack_start(Gtk.VBox(), False, True, 0)

        filter_box = FilterPluginBox(self.handler, self.FILTERS)
        filter_box.connect("preview", self.__filter_preview)
        filter_box.connect("changed", self.__filter_changed)
        self.filter_box = filter_box
        self.pack_start(filter_box, False, True, 0)

        # Save button
        self.save = Button(_("_Save"), Icons.DOCUMENT_SAVE)
        self.save.show()
        bbox = Gtk.HButtonBox()
        bbox.set_layout(Gtk.ButtonBoxStyle.END)
        bbox.pack_start(self.save, True, True, 0)
        self.pack_start(bbox, False, True, 0)

        render = Gtk.CellRendererText()
        column = TreeViewColumn(_('File'), render)

        def cell_data_file(column, cell, model, iter_, data):
            entry = model.get_value(iter_)
            cell.set_property("text", entry.name)

        column.set_cell_data_func(render, cell_data_file)

        column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.view.append_column(column)

        render = Gtk.CellRendererText()
        render.set_property('editable', True)
        column = TreeViewColumn(_('New Name'), render)

        def cell_data_new_name(column, cell, model, iter_, data):
            entry = model.get_value(iter_)
            cell.set_property("text", entry.new_name or u"")

        column.set_cell_data_func(render, cell_data_new_name)

        column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        self.view.append_column(column)

        connect_obj(self.preview, 'clicked', self.__preview, None)

        connect_obj(parent, 'changed', self.__class__.__preview, self)
        connect_obj(self.save, 'clicked', self.__rename, library)

        render.connect('edited', self.__row_edited)

        for child in self.get_children():
            child.show()
Пример #43
0
 def test_iter_append_many_iterable_int(self):
     m = ObjectStore()
     for x in m.iter_append_many((i for i in xrange(10))):
         pass
     self.failUnlessEqual([r[0] for r in m], range(10))
Пример #44
0
    def __init__(self, parent, album):
        self.album = album
        self.album.sort(key=lambda s: sort_key(s))

        self._resultlist = ObjectStore()
        self._releasecache = {}
        self._qthread = QueryThread()
        self.current_release = None

        super(SearchWindow, self).__init__(_("MusicBrainz lookup"))

        self.add_button(_("_Cancel"), Gtk.ResponseType.REJECT)
        self.add_icon_button(_("_Save"), Icons.DOCUMENT_SAVE,
                             Gtk.ResponseType.ACCEPT)

        self.set_default_size(650, 500)
        self.set_border_width(5)
        self.set_transient_for(parent)

        save_button = self.get_widget_for_response(Gtk.ResponseType.ACCEPT)
        save_button.set_sensitive(False)

        vb = Gtk.VBox()
        vb.set_spacing(8)

        hb = Gtk.HBox()
        hb.set_spacing(8)
        sq = self.search_query = Gtk.Entry()
        sq.connect('activate', self._do_query)

        sq.set_text(build_query(album))

        lbl = Gtk.Label(label=_("_Query:"))
        lbl.set_use_underline(True)
        lbl.set_mnemonic_widget(sq)
        stb = self.search_button = Gtk.Button(_('S_earch'), use_underline=True)
        stb.connect('clicked', self._do_query)
        hb.pack_start(lbl, False, True, 0)
        hb.pack_start(sq, True, True, 0)
        hb.pack_start(stb, False, True, 0)
        vb.pack_start(hb, False, True, 0)

        self.result_combo = ResultComboBox(self._resultlist)
        self.result_combo.connect('changed', self._result_changed)
        vb.pack_start(self.result_combo, False, True, 0)

        rhb = Gtk.HBox()
        rl = Gtk.Label()
        rl.set_markup(_("Results <i>(drag to reorder)</i>"))
        rl.set_alignment(0, 0.5)
        rhb.pack_start(rl, False, True, 0)
        rl = self.result_label = Gtk.Label(label="")
        rhb.pack_end(rl, False, True, 0)
        vb.pack_start(rhb, False, True, 0)
        sw = Gtk.ScrolledWindow()
        sw.set_shadow_type(Gtk.ShadowType.IN)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.ALWAYS)
        rtv = self.result_treeview = ResultTreeView(self.album)
        rtv.set_border_width(8)
        sw.add(rtv)
        vb.pack_start(sw, True, True, 0)

        self.get_action_area().set_border_width(4)
        self.get_content_area().pack_start(vb, True, True, 0)
        self.connect('response', self._on_response)
        self.connect("destroy", self._on_destroy)

        stb.emit('clicked')
        self.get_child().show_all()
Пример #45
0
 def test_iter_append_many(self):
     m = ObjectStore()
     iters = list(m.iter_append_many(range(10)))
     self.failUnlessEqual([r[0] for r in m], range(10))
     values = [m.get_value(i) for i in iters]
     self.failUnlessEqual(values, range(10))
Пример #46
0
    def __init__(self, parent, library):
        super(EditTags, self).__init__(spacing=12)
        self.title = _("Edit Tags")
        self.set_border_width(12)

        model = ObjectStore()
        view = RCMHintedTreeView(model=model)
        self._view = view
        selection = view.get_selection()
        render = Gtk.CellRendererPixbuf()
        column = TreeViewColumn()
        column.pack_start(render, True)
        column.set_fixed_width(24)
        column.set_expand(False)

        def cdf_write(col, rend, model, iter_, *args):
            entry = model.get_value(iter_)
            rend.set_property('sensitive', entry.edited or entry.deleted)
            if entry.canedit or entry.deleted:
                if entry.deleted:
                    rend.set_property('icon-name', Icons.EDIT_DELETE)
                else:
                    rend.set_property('icon-name', Icons.EDIT)
            else:
                rend.set_property('icon-name', Icons.CHANGES_PREVENT)
        column.set_cell_data_func(render, cdf_write)
        view.append_column(column)

        render = Gtk.CellRendererText()
        column = TreeViewColumn(title=_('Tag'))
        column.pack_start(render, True)

        def cell_data_tag(column, cell, model, iter_, data):
            entry = model.get_value(iter_)
            cell.set_property("text", entry.tag)
            cell.set_property("strikethrough", entry.deleted)

        column.set_cell_data_func(render, cell_data_tag)

        column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        render.set_property('editable', True)
        render.connect('edited', self.__edit_tag_name, model)
        render.connect(
            'editing-started', self.__tag_editing_started, model, library)
        view.append_column(column)

        render = Gtk.CellRendererText()
        render.set_property('ellipsize', Pango.EllipsizeMode.END)
        render.set_property('editable', True)
        render.connect('edited', self.__edit_tag, model)
        render.connect(
            'editing-started', self.__value_editing_started, model, library)
        column = TreeViewColumn(title=_('Value'))
        column.pack_start(render, True)

        def cell_data_value(column, cell, model, iter_, data):
            entry = model.get_value(iter_)
            markup = entry.value.get_markup()
            cell.markup = markup
            cell.set_property("markup", markup)
            cell.set_property("editable", entry.canedit)
            cell.set_property("strikethrough", entry.deleted)

        column.set_cell_data_func(render, cell_data_value)

        column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        view.append_column(column)

        sw = Gtk.ScrolledWindow()
        sw.set_shadow_type(Gtk.ShadowType.IN)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        sw.add(view)
        self.pack_start(sw, True, True, 0)

        cb = ConfigCheckButton(
            _("Show _programmatic tags"), 'editing', 'alltags', populate=True,
            tooltip=_("Access all tags, including machine-generated "
                      "ones e.g. MusicBrainz or Replay Gain tags"))
        cb.connect('toggled', self.__all_tags_toggled)
        self.pack_start(cb, False, True, 0)

        # Add and Remove [tags] buttons
        buttonbox = Gtk.HBox(spacing=18)
        bbox1 = Gtk.HButtonBox()
        bbox1.set_spacing(6)
        bbox1.set_layout(Gtk.ButtonBoxStyle.START)
        add = qltk.Button(_("_Add"), Icons.LIST_ADD)
        add.set_focus_on_click(False)
        self._add = add
        add.connect('clicked', self.__add_tag, model, library)
        bbox1.pack_start(add, True, True, 0)
        # Remove button
        remove = qltk.Button(_("_Remove"), Icons.LIST_REMOVE)
        remove.set_focus_on_click(False)
        remove.connect('clicked', self.__remove_tag, view)
        remove.set_sensitive(False)
        self._remove = remove

        bbox1.pack_start(remove, True, True, 0)

        # Revert and save buttons
        # Both can have customised translated text (and thus accels)
        bbox2 = Gtk.HButtonBox()
        bbox2.set_spacing(6)
        bbox2.set_layout(Gtk.ButtonBoxStyle.END)
        # Translators: Revert button in the tag editor
        revert = Button(C_("edittags", "_Revert"), Icons.DOCUMENT_REVERT)

        self._revert = revert
        revert.set_sensitive(False)
        # Translators: Save button in the tag editor
        save = Button(C_("edittags", "_Save"), Icons.DOCUMENT_SAVE)
        save.set_sensitive(False)
        self._save = save
        bbox2.pack_start(revert, True, True, 0)
        bbox2.pack_start(save, True, True, 0)

        buttonbox.pack_start(bbox1, True, True, 0)
        buttonbox.pack_start(bbox2, True, True, 0)
        self.pack_start(buttonbox, False, True, 0)
        self._buttonbox = buttonbox

        parent.connect('changed', self.__parent_changed)
        revert.connect('clicked', lambda *x: self._update())
        connect_obj(revert, 'clicked', parent.set_pending, None)

        save.connect('clicked', self.__save_files, revert, model, library)
        connect_obj(save, 'clicked', parent.set_pending, None)
        for sig in ['row-inserted', 'row-deleted', 'row-changed']:
            model.connect(sig, self.__enable_save, [save, revert])
            connect_obj(model, sig, parent.set_pending, save)

        view.connect('popup-menu', self.__popup_menu, parent)
        view.connect('button-press-event', self.__button_press)
        view.connect('key-press-event', self.__view_key_press_event)
        selection.connect('changed', self.__tag_select, remove)
        selection.set_mode(Gtk.SelectionMode.MULTIPLE)

        self._parent = parent

        for child in self.get_children():
            child.show_all()
Пример #47
0
 def test_itervalues(self):
     m = ObjectStore()
     m.insert_many(0, range(10))
     self.failUnlessEqual(range(10), list(m.itervalues()))
Пример #48
0
    def __init__(self, initial=None, filter=filesel_filter, folders=None):
        """
        initial -- a path to a file which should be shown initially
        filter -- a function which filters paths shown in the file list
        folders -- list of shown folders in the directory tree
        """

        super(FileSelector,
              self).__init__(orientation=Gtk.Orientation.VERTICAL)
        self.__filter = filter

        if initial is not None:
            assert isinstance(initial, fsnative)

        if initial and os.path.isfile(initial):
            initial = os.path.dirname(initial)
        dirlist = DirectoryTree(initial, folders=folders)

        model = ObjectStore()
        filelist = AllTreeView(model=model)
        filelist.connect("draw", self.__restore_scroll_pos_on_draw)

        column = TreeViewColumn(title=_("Songs"))
        column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        render = Gtk.CellRendererPixbuf()
        render.props.xpad = 3

        def cell_icon(column, cell, model, iter_, userdata):
            value = model.get_value(iter_)
            if is_image(value):
                cell.set_property('icon-name', Icons.IMAGE_X_GENERIC)
            else:
                cell.set_property('icon-name', Icons.AUDIO_X_GENERIC)

        column.set_cell_data_func(render, cell_icon)

        column.pack_start(render, False)
        render = Gtk.CellRendererText()
        if filelist.supports_hints():
            render.set_property('ellipsize', Pango.EllipsizeMode.END)
        column.pack_start(render, True)

        def cell_data(column, cell, model, iter_, userdata):
            value = model.get_value(iter_)
            cell.set_property('text', fsn2text(os.path.basename(value)))

        column.set_cell_data_func(render, cell_data)

        filelist.append_column(column)
        filelist.set_rules_hint(True)
        filelist.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)
        filelist.set_search_equal_func(search_func, False)
        filelist.set_search_column(0)

        self.__sig = filelist.get_selection().connect('changed',
                                                      self.__changed)

        dirlist.get_selection().connect('changed',
                                        self.__dir_selection_changed, filelist)
        dirlist.get_selection().emit('changed')

        def select_all_files(view, path, col, fileselection):
            view.expand_row(path, False)
            fileselection.select_all()

        dirlist.connect('row-activated', select_all_files,
                        filelist.get_selection())

        sw = ScrolledWindow()
        sw.add(dirlist)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        sw.set_shadow_type(Gtk.ShadowType.IN)
        self.pack1(sw, resize=True)

        sw = ScrolledWindow()
        sw.add(filelist)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        sw.set_shadow_type(Gtk.ShadowType.IN)
        self.pack2(sw, resize=True)
Пример #49
0
 def test_iterrows(self):
     m = ObjectStore()
     m.insert_many(0, range(10))
     for iter_, value in m.iterrows():
         self.failUnlessEqual(m.get_value(iter_), value)
Пример #50
0
class ResultTreeView(HintedTreeView, MultiDragTreeView):
    """The result treeview"""
    def __init__(self, album):
        self.album = album
        self._release = None
        self.model = ObjectStore()
        self.model.append_many(album)

        super(ResultTreeView, self).__init__(self.model)
        self.set_headers_clickable(True)
        self.set_rules_hint(True)
        self.set_reorderable(True)
        self.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)

        mode = Pango.EllipsizeMode
        cols = [
            (_('Filename'), self.__name_datafunc, True, mode.MIDDLE),
            (_('Disc'), self.__disc_datafunc, False, mode.END),
            (_('Track'), self.__track_datafunc, False, mode.END),
            (_('Title'), self.__title_datafunc, True, mode.END),
            (_('Artist'), self.__artist_datafunc, True, mode.END),
        ]

        for title, func, resize, mode in cols:
            render = Gtk.CellRendererText()
            render.set_property('ellipsize', mode)
            col = Gtk.TreeViewColumn(title, render)
            col.set_cell_data_func(render, func)
            col.set_resizable(resize)
            col.set_expand(resize)
            self.append_column(col)

    def iter_tracks(self):
        """Yields tuples of (release, track, song) combinations as they
        are shown in the list.
        """

        tracks = self._tracks
        for idx, (song, ) in enumerate(self.model):
            if song is None:
                continue
            if idx >= len(tracks):
                continue
            track = tracks[idx]
            yield (self._release, track, song)

    def update_release(self, full_release):
        """Updates the TreeView, handling results with a different number of
        tracks than the album being tagged.

        Passing in None will reset the list.
        """

        if full_release is not None:
            tracks = full_release.tracks
        else:
            tracks = []

        for i in range(len(self.model), len(tracks)):
            self.model.append((None, ))
        for i in range(len(self.model), len(tracks), -1):
            if self.model[-1][0] is not None:
                break
            itr = self.model.get_iter_from_string(str(len(self.model) - 1))
            self.model.remove(itr)

        self._release = full_release

        for row in self.model:
            self.model.row_changed(row.path, row.iter)

        # Only show artists if we have any
        has_artists = bool(filter(lambda t: t.artists, tracks))
        col = self.get_column(4)
        col.set_visible(has_artists)

        # Only show discs column if we have more than one disc
        col = self.get_column(1)
        col.set_visible(
            bool(full_release) and bool(full_release.disc_count > 1))

        self.columns_autosize()

    @property
    def _tracks(self):
        if self._release is None:
            return []
        return self._release.tracks

    def __name_datafunc(self, col, cell, model, itr, data):
        song = model[itr][0]
        if song:
            cell.set_property('text', fsn2text(song("~basename")))
        else:
            cell.set_property('text', '')

    def __track_datafunc(self, col, cell, model, itr, data):
        idx = model.get_path(itr)[0]
        if idx >= len(self._tracks):
            cell.set_property('text', '')
        else:
            cell.set_property('text', self._tracks[idx].tracknumber)

    def __disc_datafunc(self, col, cell, model, itr, data):
        idx = model.get_path(itr)[0]
        if idx >= len(self._tracks):
            cell.set_property('text', '')
        else:
            cell.set_property('text', self._tracks[idx].discnumber)

    def __title_datafunc(self, col, cell, model, itr, data):
        idx = model.get_path(itr)[0]
        if idx >= len(self._tracks):
            cell.set_property('text', '')
        else:
            cell.set_property('text', self._tracks[idx].title)

    def __artist_datafunc(self, col, cell, model, itr, data):
        idx = model.get_path(itr)[0]
        if idx >= len(self._tracks):
            cell.set_property('text', '')
        else:
            names = [a.name for a in self._tracks[idx].artists]
            cell.set_property('text', ", ".join(names))
Пример #51
0
 def test_iterrows_empty(self):
     m = ObjectStore()
     self.assertEqual(list(m.iterrows()), [])
Пример #52
0
    def __init__(self, parent, album):
        self.album = album
        self.album.sort(key=lambda s: sort_key(s))

        self._resultlist = ObjectStore()
        self._releasecache = {}
        self._qthread = QueryThread()
        self.current_release = None

        super(SearchWindow, self).__init__(_("MusicBrainz lookup"))

        self.add_button(_("_Cancel"), Gtk.ResponseType.REJECT)
        self.add_icon_button(_("_Save"), Icons.DOCUMENT_SAVE,
                             Gtk.ResponseType.ACCEPT)

        self.set_default_size(650, 500)
        self.set_border_width(5)
        self.set_transient_for(parent)

        save_button = self.get_widget_for_response(Gtk.ResponseType.ACCEPT)
        save_button.set_sensitive(False)

        vb = Gtk.VBox()
        vb.set_spacing(8)

        hb = Gtk.HBox()
        hb.set_spacing(8)
        sq = self.search_query = Gtk.Entry()
        sq.connect('activate', self._do_query)

        sq.set_text(build_query(album))

        lbl = Gtk.Label(label=_("_Query:"))
        lbl.set_use_underline(True)
        lbl.set_mnemonic_widget(sq)
        stb = self.search_button = Gtk.Button(_('S_earch'), use_underline=True)
        stb.connect('clicked', self._do_query)
        hb.pack_start(lbl, False, True, 0)
        hb.pack_start(sq, True, True, 0)
        hb.pack_start(stb, False, True, 0)
        vb.pack_start(hb, False, True, 0)

        self.result_combo = ResultComboBox(self._resultlist)
        self.result_combo.connect('changed', self._result_changed)
        vb.pack_start(self.result_combo, False, True, 0)

        rhb = Gtk.HBox()
        rl = Gtk.Label()
        rl.set_markup(_("Results <i>(drag to reorder)</i>"))
        rl.set_alignment(0, 0.5)
        rhb.pack_start(rl, False, True, 0)
        rl = self.result_label = Gtk.Label(label="")
        rhb.pack_end(rl, False, True, 0)
        vb.pack_start(rhb, False, True, 0)
        sw = Gtk.ScrolledWindow()
        sw.set_shadow_type(Gtk.ShadowType.IN)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.ALWAYS)
        rtv = self.result_treeview = ResultTreeView(self.album)
        rtv.set_border_width(8)
        sw.add(rtv)
        vb.pack_start(sw, True, True, 0)

        self.get_action_area().set_border_width(4)
        self.get_content_area().pack_start(vb, True, True, 0)
        self.connect('response', self._on_response)
        self.connect("destroy", self._on_destroy)

        stb.emit('clicked')
        self.get_child().show_all()
Пример #53
0
 def test_append_many_set(self):
     m = ObjectStore()
     m.append_many(set())
     m.append_many(set(range(10)))
     self.failUnlessEqual({r[0] for r in m}, set(range(10)))
Пример #54
0
    def __init__(self):
        super().__init__(spacing=6)

        self.model = model = ObjectStore()
        self.view = view = RCMHintedTreeView(model=model)
        view.set_fixed_height_mode(True)
        view.set_headers_visible(False)

        menu = Gtk.Menu()
        remove_item = MenuItem(_("_Remove"), Icons.LIST_REMOVE)
        menu.append(remove_item)
        menu.show_all()
        view.connect('popup-menu', self.__popup, menu)
        connect_obj(remove_item, 'activate', self.__remove, view)

        sw = Gtk.ScrolledWindow()
        sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
        sw.set_shadow_type(Gtk.ShadowType.IN)
        sw.add(view)
        sw.set_size_request(-1, max(sw.size_request().height, 80))
        sw.set_tooltip_text(
            _("Songs in the listed folders will be added "
              "to the library during a library refresh"))
        render = Gtk.CellRendererText()
        render.set_property('ellipsize', Pango.EllipsizeMode.END)

        def cdf(column, cell, model, iter_, data):
            path = model.get_value(iter_)
            cell.set_property('text', fsn2text(unexpand(path)))

        column = Gtk.TreeViewColumn(None, render)
        column.set_cell_data_func(render, cdf)
        column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
        view.append_column(column)

        add = Button(_("_Add"), Icons.LIST_ADD)
        add.set_tooltip_text(
            _("The new directory will be scanned after adding"))
        add.connect("clicked", self.__add)
        remove = Button(_("_Remove"), Icons.LIST_REMOVE)
        remove.set_tooltip_text(
            _("All songs in the selected directories "
              "will also be removed from the library"))

        move = Button(_("_Move"), Icons.EDIT_REDO)
        move.connect("clicked", self.__move)
        move.set_tooltip_text(
            _("Move a scan root (but not the files), "
              "migrating metadata for all included tracks."))

        selection = view.get_selection()
        selection.set_mode(Gtk.SelectionMode.MULTIPLE)
        selection.connect("changed", self.__select_changed, remove, move)
        selection.emit("changed")

        connect_obj(remove, "clicked", self.__remove, view)

        vbox = Gtk.VBox(spacing=6)
        vbox.pack_start(add, False, True, 0)
        vbox.pack_start(remove, False, True, 0)
        vbox.pack_start(move, False, True, 0)

        self.pack_start(sw, True, True, 0)
        self.pack_start(vbox, False, True, 0)

        for path in get_scan_dirs():
            model.append(row=[path])

        for child in self.get_children():
            child.show_all()
Пример #55
0
 def test_iter_append_many_iterable_object(self):
     objects = [object() for i in xrange(10)]
     m = ObjectStore()
     for x in m.iter_append_many((i for i in objects)):
         pass
     self.failUnlessEqual([r[0] for r in m], objects)
Пример #56
0
    def __init__(self, parent, library):
        super(EditTags, self).__init__(spacing=12)
        self.title = _("Edit Tags")
        self.set_border_width(12)

        model = ObjectStore()
        view = RCMHintedTreeView(model=model)
        self._view = view
        selection = view.get_selection()
        render = Gtk.CellRendererPixbuf()
        column = TreeViewColumn()
        column.pack_start(render, True)
        column.set_fixed_width(24)
        column.set_expand(False)

        def cdf_write(col, rend, model, iter_, *args):
            entry = model.get_value(iter_)
            rend.set_property('sensitive', entry.edited or entry.deleted)
            if entry.canedit or entry.deleted:
                if entry.deleted:
                    rend.set_property('icon-name', Icons.EDIT_DELETE)
                else:
                    rend.set_property('icon-name', Icons.EDIT)
            else:
                rend.set_property('icon-name', Icons.CHANGES_PREVENT)

        column.set_cell_data_func(render, cdf_write)
        view.append_column(column)

        render = Gtk.CellRendererText()
        column = TreeViewColumn(title=_('Tag'))
        column.pack_start(render, True)

        def cell_data_tag(column, cell, model, iter_, data):
            entry = model.get_value(iter_)
            cell.set_property("text", entry.tag)
            cell.set_property("strikethrough", entry.deleted)

        column.set_cell_data_func(render, cell_data_tag)

        column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        render.set_property('editable', True)
        render.connect('edited', self.__edit_tag_name, model)
        render.connect('editing-started', self.__tag_editing_started, model,
                       library)
        view.append_column(column)

        render = Gtk.CellRendererText()
        render.set_property('ellipsize', Pango.EllipsizeMode.END)
        render.set_property('editable', True)
        render.connect('edited', self.__edit_tag, model)
        render.connect('editing-started', self.__value_editing_started, model,
                       library)
        column = TreeViewColumn(title=_('Value'))
        column.pack_start(render, True)

        def cell_data_value(column, cell, model, iter_, data):
            entry = model.get_value(iter_)
            markup = entry.value.get_markup()
            cell.markup = markup
            cell.set_property("markup", markup)
            cell.set_property("editable", entry.canedit)
            cell.set_property("strikethrough", entry.deleted)

        column.set_cell_data_func(render, cell_data_value)

        column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        view.append_column(column)

        sw = Gtk.ScrolledWindow()
        sw.set_shadow_type(Gtk.ShadowType.IN)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        sw.add(view)
        self.pack_start(sw, True, True, 0)

        cb = ConfigCheckButton(
            _("Show _programmatic tags"),
            'editing',
            'alltags',
            populate=True,
            tooltip=_("Access all tags, including machine-generated "
                      "ones e.g. MusicBrainz or Replay Gain tags"))
        cb.connect('toggled', self.__all_tags_toggled)
        self.pack_start(cb, False, True, 0)

        # Add and Remove [tags] buttons
        buttonbox = Gtk.HBox(spacing=18)
        bbox1 = Gtk.HButtonBox()
        bbox1.set_spacing(6)
        bbox1.set_layout(Gtk.ButtonBoxStyle.START)
        add = qltk.Button(_("_Add"), Icons.LIST_ADD)
        add.set_focus_on_click(False)
        self._add = add
        add.connect('clicked', self.__add_tag, model, library)
        bbox1.pack_start(add, True, True, 0)
        # Remove button
        remove = qltk.Button(_("_Remove"), Icons.LIST_REMOVE)
        remove.set_focus_on_click(False)
        remove.connect('clicked', self.__remove_tag, view)
        remove.set_sensitive(False)
        self._remove = remove

        bbox1.pack_start(remove, True, True, 0)

        # Revert and save buttons
        # Both can have customised translated text (and thus accels)
        bbox2 = Gtk.HButtonBox()
        bbox2.set_spacing(6)
        bbox2.set_layout(Gtk.ButtonBoxStyle.END)
        # Translators: Revert button in the tag editor
        revert = Button(C_("edittags", "_Revert"), Icons.DOCUMENT_REVERT)

        self._revert = revert
        revert.set_sensitive(False)
        # Translators: Save button in the tag editor
        save = Button(C_("edittags", "_Save"), Icons.DOCUMENT_SAVE)
        save.set_sensitive(False)
        self._save = save
        bbox2.pack_start(revert, True, True, 0)
        bbox2.pack_start(save, True, True, 0)

        buttonbox.pack_start(bbox1, True, True, 0)
        buttonbox.pack_start(bbox2, True, True, 0)
        self.pack_start(buttonbox, False, True, 0)
        self._buttonbox = buttonbox

        parent.connect('changed', self.__parent_changed)
        revert.connect('clicked', lambda *x: self._update())
        connect_obj(revert, 'clicked', parent.set_pending, None)

        save.connect('clicked', self.__save_files, revert, model, library)
        connect_obj(save, 'clicked', parent.set_pending, None)
        for sig in ['row-inserted', 'row-deleted', 'row-changed']:
            model.connect(sig, self.__enable_save, [save, revert])
            connect_obj(model, sig, parent.set_pending, save)

        view.connect('popup-menu', self.__popup_menu, parent)
        view.connect('button-press-event', self.__button_press)
        view.connect('key-press-event', self.__view_key_press_event)
        selection.connect('changed', self.__tag_select, remove)
        selection.set_mode(Gtk.SelectionMode.MULTIPLE)

        self._parent = parent

        for child in self.get_children():
            child.show_all()
Пример #57
0
 def test_insert_many(self):
     m = ObjectStore()
     m.append(row=[42])
     m.append(row=[24])
     m.insert_many(1, range(10))
     self.failUnlessEqual([r[0] for r in m], [42] + range(10) + [24])