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