Beispiel #1
0
        def __init__(self):
            super(BlaPreferences.Keybindings, self).__init__(
                "Global keybindings")

            from blakeys import BlaKeys
            blakeys = BlaKeys()

            actions = [
                ("Play/Pause", "playpause"),
                ("Pause", "pause"),
                ("Stop", "stop"),
                ("Previous track", "previous"),
                ("Next track", "next"),
                ("Volume up", "volup"),
                ("Volume down", "voldown"),
                ("Mute", "mute")
            ]
            bindings = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
            for label, action in actions:
                accel = blacfg.getstring("keybindings", action)
                bindings.append([label, accel])

            treeview = gtk.TreeView()
            treeview.set_property("rules_hint", True)
            treeview.insert_column_with_attributes(
                -1, "Action", gtk.CellRendererText(), text=0)

            def edited(renderer, path, key, mod, *args):
                action = actions[int(path)][-1]
                accel = gtk.accelerator_name(key, mod)
                blakeys.bind(action, accel)
                bindings.set_value(bindings.get_iter(path), 1, accel)
                blacfg.set("keybindings", action, accel)

            def cleared(renderer, path):
                bindings.set_value(bindings.get_iter(path), 1, None)
                action = actions[int(path)][-1]
                blakeys.unbind(blacfg.getstring("keybindings", action))
                blacfg.set("keybindings", action, "")

            renderer = gtk.CellRendererAccel()
            renderer.set_property("editable", True)
            renderer.connect("accel_edited", edited)
            renderer.connect("accel_cleared", cleared)
            treeview.insert_column_with_attributes(
                -1, "Binding", renderer, text=1)
            treeview.set_model(bindings)

            sw = BlaScrolledWindow()
            sw.set_shadow_type(gtk.SHADOW_IN)
            sw.add(treeview)

            self.pack_start(sw, expand=True)
            if not blakeys.can_bind():
                label = gtk.Label()
                label.set_markup(
                    "<b>Note</b>: The <i>keybinder</i> module is not "
                    "available on the system.\nAs a result, the settings on "
                    "this page will have no effect.")
                self.pack_start(label, expand=False, padding=20)
Beispiel #2
0
    def __init__(self, is_editable, playlist_manager):
        super(BlaMetadataViewer, self).__init__(spacing=blaconst.WIDGET_SPACING)

        model = gtk.ListStore(gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)

        self._treeview = BlaMetadataViewer.TreeView(model, is_editable=is_editable)
        self._treeview.set_reorderable(False)
        self._treeview.set_rubber_banding(True)
        self._treeview.set_property("rules_hint", True)

        # Name column
        r = gtk.CellRendererText()
        self._treeview.insert_column_with_data_func(-1, "Name", r, self.__cdf_name)

        # Value column
        r = gtk.CellRendererText()
        r.set_property("ellipsize", pango.ELLIPSIZE_END)
        r.set_property("editable", is_editable)
        self._treeview.insert_column_with_data_func(-1, "Value", r, self.__cdf_value)
        if is_editable:

            def editing_started(renderer, editable, path):
                self._treeview.set_cursor(path)
                model = self._treeview.get_model()
                # Remove the "Varies between tracks" label.
                if model[path][1] is None:
                    editable.set_text("")

            r.connect("editing_started", editing_started)

            def edited(renderer, path, text):
                row = self._treeview.get_model()[path]
                identifier = row[0]
                if row[1] != text:
                    row[1] = text
                    self.emit("value_changed", identifier, text)

            r.connect("edited", edited)

        for column in self._treeview.get_columns():
            column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)

        # Wrap the treeview.
        sw = BlaScrolledWindow()
        sw.add(self._treeview)

        self._pb = gtk.ProgressBar()
        self._pb.set_visible(False)

        self.pack_start(sw, expand=True)
        self.pack_start(self._pb, expand=False)

        playlist_manager.connect_object("selection_changed", BlaMetadataViewer._update_model, self)

        self._uris = []

        sw.show_all()
        self.show()
Beispiel #3
0
    def __init__(self):
        super(BlaRadio, self).__init__(spacing=3)

        hbox = gtk.HBox(spacing=3)

        entry = gtk.Entry()
        entry.connect(
            "activate", lambda *x: self.__add_station(entry.get_text()))
        hbox.pack_start(entry, expand=True)

        button = gtk.Button("Add")
        button.connect(
            "clicked", lambda *x: self.__add_station(entry.get_text()))
        hbox.pack_start(button, expand=False)

        button = gtk.Button("Remove")
        button.connect("clicked", self.__remove_stations)
        hbox.pack_start(button, expand=False)

        def open_(*args):
            diag = gtk.FileChooserDialog(
                "Select files",
                action=gtk.FILE_CHOOSER_ACTION_OPEN,
                buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                         gtk.STOCK_OPEN, gtk.RESPONSE_OK))
            diag.set_local_only(True)
            response = diag.run()
            filename = diag.get_filename()
            diag.destroy()

            if response == gtk.RESPONSE_OK and filename:
                filename = filename.strip()
                filename and self.__add_station(filename)

        button = gtk.Button("Open...")
        button.connect("clicked", open_)
        hbox.pack_start(button, expand=False)

        model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
        self.__treeview = blaguiutils.BlaTreeViewBase()
        self.__treeview.set_model(model)
        self.__treeview.set_enable_search(True)
        self.__treeview.set_rubber_banding(True)
        self.__treeview.set_property("rules_hint", True)

        # Playing column
        r = gtk.CellRendererPixbuf()
        self.__treeview.insert_column_with_attributes(
            -1, "Playing", r, stock_id=0)
        r.set_property("stock-size", gtk.ICON_SIZE_MENU)
        r.set_property("xalign", 0.5)
        self.__treeview.get_columns()[-1].set_alignment(0.5)

        # The remaining columns
        def cell_data_func(column, renderer, model, iterator, identifier):
            renderer.set_property("text", model[iterator][1][identifier])

        columns = [
            ("Organization", "organization"), ("Station", "station"),
            ("URI", "uri"), ("Location", "location")
        ]
        for header, identifier in columns:
            r = gtk.CellRendererText()
            r.set_property("ellipsize", pango.ELLIPSIZE_END)
            c = gtk.TreeViewColumn(header)
            c.set_resizable(True)
            c.set_expand(True)
            c.pack_start(r)
            c.set_cell_data_func(r, cell_data_func, identifier)
            self.__treeview.append_column(c)

        sw = BlaScrolledWindow()
        sw.set_shadow_type(gtk.SHADOW_IN)
        sw.add(self.__treeview)

        self.pack_start(hbox, expand=False)
        self.pack_start(sw, expand=True)

        self.__treeview.enable_model_drag_dest(
            [("radio", gtk.TARGET_OTHER_APP, 0),
             ("text/uri-list", gtk.TARGET_OTHER_APP, 1),
             ("text/plain", gtk.TARGET_OTHER_APP, 2),
             ("TEXT", gtk.TARGET_OTHER_APP, 3),
             ("STRING", gtk.TARGET_OTHER_APP, 4),
             ("COMPOUND_TEXT", gtk.TARGET_OTHER_APP, 5),
             ("UTF8_STRING", gtk.TARGET_OTHER_APP, 6)],
            gtk.gdk.ACTION_COPY)
        self.__treeview.enable_model_drag_source(
            gtk.gdk.BUTTON1_MASK, [("radio", gtk.TARGET_SAME_WIDGET, 0)],
            gtk.gdk.ACTION_MOVE)
        self.__treeview.connect("drag_data_get", self.__drag_data_get)
        self.__treeview.connect("drag_data_received", self.__drag_data_recv)
        self.__treeview.connect("popup", self.__popup_menu)
        self.__treeview.connect(
            "row_activated",
            lambda treeview, path, column: self.__get_station(path))
        self.__treeview.connect("key_press_event", self.__key_press_event)

        player.connect_object("get_station", BlaRadio.__get_station, self)
        player.connect_object("state_changed", BlaRadio.__update_rows, self)

        blaplay.bla.register_for_cleanup(self)
        self.show_all()
Beispiel #4
0
    def __init__(self, parent):
        super(BlaFileBrowser, self).__init__()

        self.__pixbufs = {
            "directory": self.__get_pixbuf(gtk.STOCK_DIRECTORY),
            "file": self.__get_pixbuf(gtk.STOCK_FILE)
        }

        self.__history = BlaFileBrowser.History()

        vbox = gtk.VBox()

        # The toolbar
        table = gtk.Table(rows=1, columns=6, homogeneous=False)

        buttons = [
            (gtk.STOCK_GO_BACK,
             lambda *x: self.__update_from_history(backwards=True)),
            (gtk.STOCK_GO_UP,
             lambda *x: self.__update_directory(
                os.path.dirname(self.__directory))),
            (gtk.STOCK_GO_FORWARD,
             lambda *x: self.__update_from_history(backwards=False)),
            (gtk.STOCK_HOME,
             lambda *x: self.__update_directory(os.path.expanduser("~")))
        ]

        def add_button(icon, callback, idx):
            button = gtk.Button()
            button.add(
                gtk.image_new_from_stock(icon, gtk.ICON_SIZE_SMALL_TOOLBAR))
            button.set_relief(gtk.RELIEF_NONE)
            button.connect("clicked", callback)
            table.attach(button, idx, idx+1, 0, 1, xoptions=not gtk.EXPAND)

        idx = 0
        for icon, callback in buttons:
            add_button(icon, callback, idx)
            idx += 1

        # Add the entry field separately.
        self.__entry = gtk.Entry()
        self.__entry.connect(
            "activate",
            lambda *x: self.__update_directory(self.__entry.get_text()))
        def key_press_event_entry(entry, event):
            if (blagui.is_accel(event, "Escape") or
                blagui.is_accel(event, "<Ctrl>L")):
                self.__entry.select_region(-1, -1)
                self.__treeview.grab_focus()
                return True
            elif (blagui.is_accel(event, "Up") or
                  blagui.is_accel(event, "Down")):
                return True
            return False
        self.__entry.connect("key_press_event", key_press_event_entry)
        table.attach(self.__entry, idx, idx+1, 0, 1)
        idx += 1

        add_button(gtk.STOCK_REFRESH,
                   lambda *x: self.__update_directory(refresh=True), idx)

        vbox.pack_start(table, expand=False, fill=False)

        # The treeview
        self.__treeview = BlaTreeView(parent=parent, multicol=True,
                                      browser_id=blaconst.BROWSER_FILESYSTEM)
        self.__treeview.set_enable_search(True)
        self.__treeview.set_search_column(2)
        self.__treeview.enable_model_drag_source(
            gtk.gdk.BUTTON1_MASK,
            [blagui.DND_TARGETS[blagui.DND_URIS]],
            gtk.gdk.ACTION_COPY)
        self.__treeview.connect_object(
            "drag_data_get", BlaFileBrowser.__drag_data_get, self)
        def key_press_event(treeview, event):
            if blagui.is_accel(event, "<Ctrl>L"):
                self.__entry.grab_focus()
                return True
            return False
        self.__treeview.connect("key_press_event", key_press_event)
        model = gtk.ListStore(*self.__layout)
        self.__filt = model.filter_new()
        self.__filt.set_visible_func(self.__visible_func)
        self.__treeview.set_model(self.__filt)
        self.__directory = blacfg.getstring("general", "filesystem.directory")

        # Name column
        c = gtk.TreeViewColumn("Name")
        c.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
        c.set_resizable(True)
        c.set_expand(True)
        c.set_fixed_width(1)

        r = gtk.CellRendererPixbuf()
        r.set_property("xalign", 0.0)
        c.pack_start(r, expand=False)
        c.add_attribute(r, "pixbuf", 1)

        r = gtk.CellRendererText()
        r.set_property("xalign", 0.0)
        c.pack_start(r)
        # TODO: Use a cdf instead.
        c.add_attribute(r, "text", 2)
        r.set_property("ellipsize", pango.ELLIPSIZE_END)

        self.__treeview.append_column(c)

        # TODO: turn this into nemo's size column (for files, display the size,
        #       for directories the number of items)
        # Last modified column
        c = gtk.TreeViewColumn()
        c.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
        c.set_resizable(True)

        title = "Last modified"
        label = gtk.Label(title)
        width = label.create_pango_layout(title).get_pixel_size()[0]
        c.set_title(title)

        r = gtk.CellRendererText()
        r.set_property("ellipsize", pango.ELLIPSIZE_END)
        c.pack_start(r)
        c.set_cell_data_func(r, self.__last_modified_cb)
        c.set_min_width(width + 12 + r.get_property("xpad"))

        self.__treeview.append_column(c)
        self.__treeview.connect("row_activated", self.__open)
        self.__update_directory(self.__directory)
        self.__treeview.columns_autosize()

        sw = BlaScrolledWindow()
        sw.add(self.__treeview)
        vbox.pack_start(sw, expand=True)

        # The search bar
        hbox = gtk.HBox()
        hbox.pack_start(gtk.Label("Filter:"), expand=False, padding=2)

        self.__filter_entry = gtk.Entry()
        self.__filter_entry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY,
                                                gtk.STOCK_CLEAR)
        self.__filter_entry.connect(
            "icon_release", lambda *x: x[0].delete_text(0, -1))
        self.__filter_entry.connect(
            "changed", self.__filter_parameters_changed)
        self.__filter_entry.connect(
            "activate", lambda *x: self.__filt.refilter())
        hbox.pack_start(self.__filter_entry, expand=True)

        button = gtk.Button()
        button.add(
            gtk.image_new_from_stock(gtk.STOCK_FIND,
                                     gtk.ICON_SIZE_SMALL_TOOLBAR))
        button.connect("clicked", lambda *x: self.__filt.refilter())
        hbox.pack_start(button, expand=False)
        vbox.pack_start(hbox, expand=False)

        self.pack_start(vbox)
Beispiel #5
0
    def __init__(self, parent):
        super(BlaLibraryBrowser, self).__init__()

        self.__treeview = BlaTreeView(parent=parent, multicol=False,
                                      browser_id=blaconst.BROWSER_LIBRARY)
        self.__treeview.set_headers_visible(False)
        column = gtk.TreeViewColumn()
        column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
        self.__treeview.append_column(column)
        self.__treeview.connect("row_collapsed", self.__row_collapsed)
        self.__treeview.connect("row_expanded", self.__row_expanded)

        self.__treeview.enable_model_drag_source(
            gtk.gdk.BUTTON1_MASK,
            [blagui.DND_TARGETS[blagui.DND_LIBRARY]],
            gtk.gdk.ACTION_COPY)
        self.__treeview.connect_object(
            "drag_data_get", BlaLibraryBrowser.__drag_data_get, self)

        sw = BlaScrolledWindow()
        sw.add(self.__treeview)

        hbox = gtk.HBox()

        cb = gtk.combo_box_new_text()
        for label in ["directory", "artist", "artist - album", "album",
                      "genre", "year"]:
            cb.append_text(label)
        cb.set_active(blacfg.getint("library", "organize.by"))
        cb.connect("changed", self.__organize_by_changed)

        alignment = gtk.Alignment()
        alignment.add(gtk.Label("Organize by:"))
        table = gtk.Table(rows=2, columns=1, homogeneous=False)
        table.attach(alignment, 0, 1, 0, 1, xpadding=2, ypadding=2)
        table.attach(cb, 0, 1, 1, 2)
        hbox.pack_start(table, expand=False)

        def queue_model_update(*args):
            self.__queue_model_update(blacfg.getint("library", "organize.by"))

        self.__entry = gtk.Entry()
        self.__entry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY,
                                         gtk.STOCK_CLEAR)
        self.__entry.connect(
            "icon_release", lambda *x: x[0].delete_text(0, -1))
        self.__entry.connect("changed", self.__filter_parameters_changed)
        self.__entry.connect("activate", queue_model_update)

        button = gtk.Button()
        button.add(
            gtk.image_new_from_stock(gtk.STOCK_FIND,
                                     gtk.ICON_SIZE_SMALL_TOOLBAR))
        button.connect("clicked", queue_model_update)

        alignment = gtk.Alignment()
        alignment.add(gtk.Label("Filter:"))
        table = gtk.Table(rows=2, columns=1, homogeneous=False)
        table.attach(alignment, 0, 1, 0, 1, xpadding=2, ypadding=2)
        hbox2 = gtk.HBox()
        hbox2.pack_start(self.__entry, expand=True)
        hbox2.pack_start(button, expand=False)
        table.attach(hbox2, 0, 1, 1, 2)
        hbox.pack_start(table)

        self.pack_start(sw, expand=True)
        self.pack_start(hbox, expand=False)

        self.update_treeview_style()
        self.update_tree_lines()
        def config_changed(cfg, section, key):
            if section == "library":
                if key == "custom.browser":
                    self.update_treeview_style()
                elif key == "draw.tree.lines":
                    self.update_tree_lines()
        blacfg.connect("changed", config_changed)

        library.connect("library_updated", queue_model_update)
        queue_model_update()
Beispiel #6
0
    def __init__(self, views):
        super(BlaSidePane, self).__init__(spacing=blaconst.WIDGET_SPACING)

        notebook = gtk.Notebook()
        notebook.set_scrollable(True)

        # Set up the lyrics textview.
        self.__tv = gtk.TextView()
        self.__tv.set_size_request(self.__MIN_WIDTH, -1)
        self.__tv.set_editable(False)
        self.__tv.set_cursor_visible(False)
        self.__tv.set_wrap_mode(gtk.WRAP_WORD)
        self.__tv.set_justification(gtk.JUSTIFY_CENTER)

        sw = BlaScrolledWindow()
        sw.set_shadow_type(gtk.SHADOW_NONE)
        sw.add(self.__tv)
        sw.show_all()

        self.__style = self.__tv.get_modifier_style().copy()
        self.__tb = self.__tv.get_buffer()
        self.__tb.create_tag("bold", weight=pango.WEIGHT_BOLD)
        self.__tb.create_tag("large", scale=pango.SCALE_LARGE)
        self.__tb.create_tag("italic", style=pango.STYLE_ITALIC)
        self.__tag = self.__tb.create_tag("color")

        # Set up the view selector.
        viewport = gtk.Viewport()
        viewport.set_shadow_type(gtk.SHADOW_IN)
        self.__treeview = blaguiutils.BlaTreeViewBase(
            allow_empty_selection=False)
        self.__treeview.get_selection().set_mode(gtk.SELECTION_SINGLE)
        self.__treeview.set_headers_visible(False)
        self.__treeview.set_property("rules_hint", True)
        r = gtk.CellRendererText()
        r.set_property("ellipsize", pango.ELLIPSIZE_END)
        c = gtk.TreeViewColumn()
        c.pack_start(r, expand=True)
        c.add_attribute(r, "text", 0)
        r = gtk.CellRendererText()
        r.set_alignment(1.0, 0.5)
        c.pack_start(r, expand=False)
        def cell_data_func(column, renderer, model, iterator):
            count = model[iterator][1]
            renderer.set_property(
                "markup", "<i>(%d)</i>" % count if count > 0 else "")
        c.set_cell_data_func(r, cell_data_func)
        self.__treeview.append_column(c)
        viewport.add(self.__treeview)
        model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_INT)
        self.__treeview.set_model(model)
        [model.append([view.view_name, 0]) for view in views]
        selection = self.__treeview.get_selection()
        selection.select_path(blacfg.getint("general", "view"))
        self.__treeview.get_selection().connect(
            "changed", self.__selection_changed)

        self.cover_display = BlaSidePane.BlaCoverDisplay()

        hbox = gtk.HBox(spacing=blaconst.WIDGET_SPACING)
        hbox.pack_start(viewport, expand=True)
        hbox.pack_start(self.cover_display, expand=False, fill=True)

        notebook.append_page(BlaTagEditor(views[blaconst.VIEW_PLAYLISTS]),
                             gtk.Label("Tags"))
        notebook.append_page(BlaProperties(views[blaconst.VIEW_PLAYLISTS]),
                             gtk.Label("Properties"))
        notebook.append_page(sw, gtk.Label("Lyrics"))

        def switch_page(notebook, page, page_num):
            action_widget = notebook.get_action_widget(gtk.PACK_END)
            if action_widget.child is not None:
                action_widget.remove(action_widget.child)

            page = notebook.get_nth_page(page_num)
            blacfg.set("general", "metadata.view", page_num)

            if page_num == blaconst.METADATA_TAGS:
                widget = page.get_control_widget()
            elif page_num == blaconst.METADATA_LYRICS:
                button = gtk.Button()
                button.set_tooltip_text("Edit lyrics")
                button.set_relief(gtk.RELIEF_NONE)
                button.set_focus_on_click(False)
                button.add(
                    gtk.image_new_from_stock(gtk.STOCK_EDIT,
                                             gtk.ICON_SIZE_MENU))
                style = gtk.RcStyle()
                style.xthickness = style.ythickness = 0
                button.modify_style(style)
                # TODO: Implement a widget to edit metadata.
                button.connect("clicked", lambda *x: False)
                button.show_all()
                widget = button

                action_widget.set_visible(False)
                return
            else:
                action_widget.set_visible(False)
                return

            action_widget.add(widget)
            action_widget.set_visible(True)
            action_widget.show_all()

        viewport = gtk.Viewport()
        viewport.set_shadow_type(gtk.SHADOW_NONE)
        notebook.set_action_widget(viewport, gtk.PACK_END)
        notebook.connect("switch_page", switch_page)
        page_num = blacfg.getint("general", "metadata.view")
        notebook.set_current_page(page_num)
        # Make sure the notebook's action widget gets initialized.
        switch_page(notebook, None, page_num)

        self.pack_start(notebook, expand=True)
        self.pack_start(hbox, expand=False)

        # Hook up the metadata callbacks.
        BlaSidePane.fetcher = blametadata.BlaFetcher()
        self.fetcher.connect_object(
            "lyrics", BlaSidePane.__update_lyrics, self)
        self.fetcher.connect_object(
            "cover", type(self.cover_display).update, self.cover_display)

        notebook.show()
        hbox.show_all()
        self.show()
Beispiel #7
0
        def __init__(self):
            super(BlaPreferences.LibraryBrowsersSettings, self).__init__(
                "Library/Browsers")

            restrict_string = blacfg.getstring("library", "restrict.to")
            exclude_string = blacfg.getstring("library", "exclude")
            def destroy(*args):
                if (restrict_string !=
                    blacfg.getstring("library", "restrict.to") or
                    exclude_string != blacfg.getstring("library", "exclude")):
                    library.sync()
            self.connect("destroy", destroy)

            hbox = gtk.HBox(spacing=10)

            model = gtk.ListStore(gobject.TYPE_STRING)
            treeview = gtk.TreeView(model)
            treeview.set_property("rules_hint", True)
            r = gtk.CellRendererText()
            treeview.insert_column_with_attributes(
                -1, "Directories", r, text=0)

            sw = BlaScrolledWindow()
            sw.set_shadow_type(gtk.SHADOW_IN)
            sw.set_size_request(-1, 140)
            sw.add(treeview)

            directories = blacfg.getdotliststr("library", "directories")
            for f in directories:
                model.append([f])

            table = gtk.Table(rows=2, columns=1)
            items = [
                ("Add...", self.__add_directory),
                ("Remove", self.__remove_directory),
                ("Rescan all", self.__rescan_all)
            ]
            for idx, (label, callback) in enumerate(items):
                button = gtk.Button(label)
                button.connect("clicked", callback, treeview)
                table.attach(button, 0, 1, idx, idx+1, yoptions=not gtk.EXPAND)

            hbox.pack_start(sw, expand=True)
            hbox.pack_start(table, expand=False, fill=False)

            # Update library checkbutton
            update_library = gtk.CheckButton("Update library on startup")
            update_library.set_active(
                blacfg.getboolean("library", "update.on.startup"))
            update_library.connect(
                "toggled",
                lambda cb: blacfg.setboolean("library", "update.on.startup",
                                             cb.get_active()))

            # The file types
            restrict_to_entry = gtk.Entry()
            restrict_to_entry.set_tooltip_text(
                "Comma-separated list, works on filenames")
            restrict_to_entry.set_text(
                blacfg.getstring("library", "restrict.to"))
            restrict_to_entry.connect(
                "changed",
                lambda entry: blacfg.set("library", "restrict.to",
                                         entry.get_text()))

            exclude_entry = gtk.Entry()
            exclude_entry.set_tooltip_text(
                "Comma-separated list, works on filenames")
            exclude_entry.set_text(
                blacfg.getstring("library", "exclude"))
            exclude_entry.connect(
                "changed",
                lambda entry: blacfg.set("library", "exclude",
                                         entry.get_text()))

            pairs = [
                (blaconst.ACTION_SEND_TO_CURRENT, "send to current playlist"),
                (blaconst.ACTION_ADD_TO_CURRENT, "add to current playlist"),
                (blaconst.ACTION_SEND_TO_NEW, "send to new playlist"),
                (blaconst.ACTION_EXPAND_COLLAPSE, "expand/collapse")
            ]
            # FIXME: Ugly!!
            actions = [""] * 4
            for idx, label in pairs:
                actions[idx] = label
            comboboxes = []

            def cb_changed(combobox, key):
                blacfg.set("library", "%s.action" % key, combobox.get_active())

            for key in ["doubleclick", "middleclick", "return"]:
                cb = gtk.combo_box_new_text()
                map(cb.append_text, actions)
                if key == "return":
                    cb.remove_text(3)
                cb.set_active(blacfg.getint("library", "%s.action" % key))
                cb.connect("changed", cb_changed, key)
                comboboxes.append(cb)

            widgets = [restrict_to_entry, exclude_entry] + comboboxes
            labels = ["Restrict to", "Exclude", "Double-click", "Middle-click",
                      "Return"]

            action_table = gtk.Table(rows=len(labels), columns=2,
                                     homogeneous=False)

            count = 0
            for label, widget in zip(labels, widgets):
                label = gtk.Label("%s:" % label)
                label.set_alignment(xalign=0.0, yalign=0.5)
                action_table.attach(label, 0, 1, count, count+1,
                                    xoptions=gtk.FILL, xpadding=5)
                action_table.attach(widget, 1, 2, count, count+1)
                count += 1

            hbox2 = gtk.HBox(spacing=10)

            draw_tree_lines = gtk.CheckButton("Draw tree lines in browsers")
            draw_tree_lines.set_active(
                blacfg.getboolean("library", "draw.tree.lines"))
            draw_tree_lines.connect("toggled", self.__tree_lines_changed)

            custom_library_browser = gtk.CheckButton(
                "Use custom treeview as library browser")
            custom_library_browser.set_active(
                blacfg.getboolean("library", "custom.browser"))
            custom_library_browser.connect(
                "toggled", self.__custom_library_browser_changed)

            hbox2.pack_start(draw_tree_lines)
            hbox2.pack_start(custom_library_browser)

            self.pack_start(hbox, expand=False)
            self.pack_start(update_library, expand=False)
            self.pack_start(action_table, expand=False)
            self.pack_start(hbox2, expand=False)