1# Copyright 2012 Christoph Reiter
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7
8from gi.repository import Gtk
9
10from quodlibet import _
11from quodlibet.plugins.songsmenu import SongsMenuPlugin
12from quodlibet.util.tags import MACHINE_TAGS
13from quodlibet.util import build_filter_query
14from quodlibet.qltk import Window, Icons, Button
15
16
17class SelectionWindow(Window):
18    def __init__(self, filters, browser, parent=None):
19        super(SelectionWindow, self).__init__()
20        self.set_border_width(10)
21        self.set_title(FilterAll.PLUGIN_NAME)
22        self.set_default_size(200, 250)
23        self.set_transient_for(parent)
24
25        model = Gtk.ListStore(bool, str, str)
26        for key, value in sorted(filters.items()):
27            model.append(row=[False, key, value])
28
29        toggle = Gtk.CellRendererToggle()
30        toggle.connect("toggled", self.__toggeled, model, browser)
31        text = Gtk.CellRendererText()
32
33        toggle_column = Gtk.TreeViewColumn("", toggle, active=0)
34        column = Gtk.TreeViewColumn(_("Tag"), text, text=1)
35
36        view = Gtk.TreeView(model)
37        view.append_column(toggle_column)
38        view.append_column(column)
39
40        sw = Gtk.ScrolledWindow()
41        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
42        sw.set_shadow_type(Gtk.ShadowType.IN)
43        sw.add(view)
44
45        buttons = Gtk.HButtonBox()
46        buttons.set_spacing(6)
47        buttons.set_layout(Gtk.ButtonBoxStyle.END)
48        close = Button(_("_Close"), Icons.WINDOW_CLOSE)
49        close.connect('clicked', lambda *x: self.destroy())
50        buttons.pack_start(close, True, True, 0)
51
52        box = Gtk.VBox(spacing=12)
53        box.pack_start(sw, True, True, 0)
54        box.pack_start(buttons, False, True, 0)
55
56        self.add(box)
57
58        self.show_all()
59
60    def __filter(self, model, browser):
61        selected = {}
62        for row in model:
63            sel, key, value = row
64            if sel:
65                selected[key] = value
66
67        joined = ", ".join(sorted(selected.values()))
68        if len(selected) >= 2:
69            joined = "&(%s)" % joined
70
71        browser.filter_text(joined)
72
73    def __toggeled(self, render, path, model, browser):
74        model[path][0] = not model[path][0]
75        self.__filter(model, browser)
76
77
78class FilterAll(SongsMenuPlugin):
79    PLUGIN_ID = "FilterAll"
80    PLUGIN_NAME = _("Filter on Any Tag")
81    PLUGIN_DESC = _("Creates a search query based on "
82                    "tags of the selected songs.")
83    PLUGIN_ICON = Icons.EDIT_FIND
84    REQUIRES_ACTION = True
85
86    def plugin_songs(self, songs):
87        browser = self.plugin_window.browser
88        if not browser.can_filter_text():
89            return
90
91        keys = {key for song in songs
92                for key in song.realkeys()}
93        keys.difference_update(MACHINE_TAGS)
94
95        filters = {}
96        for key in keys:
97            values = set()
98            for song in songs:
99                values.update(song.list(key))
100            filters[key] = build_filter_query(key, values)
101
102        SelectionWindow(filters, browser, parent=self.plugin_window)
103