1# Copyright 2010, 2012-2014 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 config
11from quodlibet import util
12from quodlibet import qltk
13from quodlibet import _
14from quodlibet.qltk.tagscombobox import TagsComboBoxEntry
15from quodlibet.qltk.views import BaseView
16from quodlibet.qltk import Button, Icons
17from quodlibet.util import connect_obj
18
19
20def get_headers():
21    result = []
22    headers = config.get("browsers", "collection_headers")
23    for h in headers.splitlines():
24        values = h.split()
25        if len(values) != 2:
26            continue
27        tag, merge = values
28        try:
29            result.append((tag, bool(int(merge))))
30        except ValueError:
31            continue
32    return result
33
34
35def save_headers(headers):
36    headers = "\n".join(["%s %d" % (t, m) for (t, m) in headers])
37    config.set("browsers", "collection_headers", headers)
38
39
40class PatternEditor(Gtk.HBox):
41
42    PRESETS = [
43        [("~people", False)],
44        [("~year", False)],
45        [("genre", False)],
46        [("genre", False), ("artist", False)],
47    ]
48
49    COMPLETION = ["genre", "grouping", "~people", "artist", "album", "~year"]
50
51    def __init__(self):
52        super(PatternEditor, self).__init__(spacing=12)
53
54        self.__headers = headers = {}
55        buttons = []
56
57        group = None
58        for tags in self.PRESETS:
59            tied = "~" + "~".join([t[0] for t in tags])
60            group = Gtk.RadioButton(group=group, label="_" + util.tag(tied),
61                                    use_underline=True)
62            headers[group] = tags
63            buttons.append(group)
64
65        group = Gtk.RadioButton(group=group, label=_("_Custom"),
66                                use_underline=True)
67        self.__custom = group
68        headers[group] = []
69        buttons.append(group)
70
71        button_box = Gtk.HBox(spacing=6)
72        self.__model = model = Gtk.ListStore(str, bool)
73
74        radio_box = Gtk.VBox(spacing=6)
75        for button in buttons:
76            radio_box.pack_start(button, False, True, 0)
77            button.connect('toggled', self.__toggled, button_box, model)
78
79        self.pack_start(radio_box, False, True, 0)
80
81        cb = TagsComboBoxEntry(self.COMPLETION)
82
83        view = BaseView(model=model)
84        view.set_reorderable(True)
85        view.set_headers_visible(True)
86
87        ctrl_box = Gtk.VBox(spacing=6)
88
89        add = Button(_("_Add"), Icons.LIST_ADD)
90        ctrl_box.pack_start(add, False, True, 0)
91        add.connect('clicked', self.__add, model, cb)
92
93        remove = Button(_("_Remove"), Icons.LIST_REMOVE)
94        ctrl_box.pack_start(remove, False, True, 0)
95        remove.connect('clicked', self.__remove, view)
96
97        selection = view.get_selection()
98        selection.connect('changed', self.__selection_changed, remove)
99        selection.emit('changed')
100
101        sw = Gtk.ScrolledWindow()
102        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
103        sw.set_shadow_type(Gtk.ShadowType.IN)
104        sw.add(view)
105
106        edit_box = Gtk.VBox(spacing=6)
107        edit_box.pack_start(cb, False, True, 0)
108        edit_box.pack_start(sw, True, True, 0)
109
110        button_box.pack_start(edit_box, True, True, 0)
111        button_box.pack_start(ctrl_box, False, True, 0)
112        self.pack_start(button_box, True, True, 0)
113
114        render = Gtk.CellRendererText()
115        render.set_property("editable", True)
116
117        def edited_cb(render, path, text, model):
118            model[path][0] = text
119        render.connect("edited", edited_cb, model)
120
121        column = Gtk.TreeViewColumn(_("Tag"), render, text=0)
122        column.set_expand(True)
123        view.append_column(column)
124
125        toggle = Gtk.CellRendererToggle()
126        toggle.connect("toggled", self.__toggeled, model)
127        toggle_column = Gtk.TreeViewColumn(_("Merge"), toggle, active=1)
128        view.append_column(toggle_column)
129
130    def __toggeled(self, render, path, model):
131        model[path][1] = not model[path][1]
132
133    @property
134    def headers(self):
135        for button in self.__headers.keys():
136            if button.get_active():
137                if button == self.__custom:
138                    model_headers = [(row[0], row[1]) for row in self.__model]
139                    self.__headers[self.__custom] = model_headers
140                return self.__headers[button]
141
142    @headers.setter
143    def headers(self, new_headers):
144        for button, headers in self.__headers.items():
145            if headers == new_headers:
146                button.set_active(True)
147                button.emit("toggled")
148                break
149        else:
150            self.__headers[self.__custom] = new_headers
151            self.__custom.set_active(True)
152
153    def __selection_changed(self, selection, remove):
154        remove.set_sensitive(bool(selection.get_selected()[1]))
155
156    def __add(self, button, model, cb):
157        if cb.tag:
158            model.append(row=[cb.tag, False])
159
160    def __remove(self, button, view):
161        view.remove_selection()
162
163    def __toggled(self, button, edit_widget, model):
164        tags = self.__headers[button]
165
166        if tags:
167            model.clear()
168            for tag, merge in tags:
169                model.append(row=[tag, merge])
170
171        edit_widget.set_sensitive(
172            button.get_active() and button is self.__custom)
173
174
175class Preferences(qltk.UniqueWindow):
176    def __init__(self, browser):
177        if self.is_not_unique():
178            return
179        super(Preferences, self).__init__()
180
181        self._browser = browser
182
183        self.set_transient_for(qltk.get_top_parent(browser))
184        self.set_default_size(350, 225)
185        self.set_border_width(12)
186
187        self.set_title(_("Album Collection Preferences"))
188
189        vbox = Gtk.VBox(spacing=12)
190
191        editor = PatternEditor()
192        editor.headers = get_headers()
193
194        apply = Button(_("_Apply"))
195        connect_obj(apply, "clicked", self.__apply, editor, False)
196
197        cancel = Button(_("_Cancel"))
198        cancel.connect("clicked", lambda x: self.destroy())
199
200        box = Gtk.HButtonBox()
201        box.set_spacing(6)
202        box.set_layout(Gtk.ButtonBoxStyle.END)
203        box.pack_start(apply, True, True, 0)
204        self.use_header_bar()
205        if not self.has_close_button():
206            box.pack_start(cancel, True, True, 0)
207
208        vbox.pack_start(editor, True, True, 0)
209        vbox.pack_start(box, False, True, 0)
210
211        self.add(vbox)
212
213        apply.grab_focus()
214        self.show_all()
215
216    def __apply(self, editor, close):
217        if editor.headers != get_headers():
218            save_headers(editor.headers)
219            self._browser.set_hierarchy()
220
221        if close:
222            self.destroy()
223