def __init__(self, col_names=(('Artist', 150, 1.0), ('Album', 200, 0.0), ('Title', 250, 1.0))):
        Gtk.ScrolledWindow.__init__(self)
        self.set_policy(
            Gtk.PolicyType.AUTOMATIC,
            Gtk.PolicyType.AUTOMATIC
        )

        self._menu = None
        self._model = PlaylistTreeModel([])

        # Treeview Configuration
        self._view = Gtk.TreeView(model=None)
        self._view.set_fixed_height_mode(True)
        # self._view.set_rules_hint(True)
        self._view.set_enable_search(False)
        self._view.set_search_column(-1)

        self._view.connect('row-activated', self._on_row_activated)
        self._view.connect('button-press-event', self._on_button_press_event)

        add_keybindings(self._view, {
            '<Shift>space': lambda view, key: self.jump_to_selection()
        })

        selection = self._view.get_selection()
        selection.set_mode(Gtk.SelectionMode.MULTIPLE)

        for i, (text, width, align) in enumerate(col_names):
            # TODO: Think of a clearer API
            if '<pixbuf>' in text:
                _, header = text.split(':', maxsplit=1)
                renderer = Gtk.CellRendererPixbuf()
                col = Gtk.TreeViewColumn(header)
                col.pack_start(renderer, False)
                col.add_attribute(renderer, 'stock-id', i)
            elif '<progress>' in text:
                _, header = text.split(':', maxsplit=1)
                col = Gtk.TreeViewColumn(header)
                renderer = Gtk.CellRendererProgress()
                col.pack_start(renderer, False)
                col.add_attribute(renderer, 'text', i)
                col.add_attribute(renderer, 'value', i)
            else:
                renderer = Gtk.CellRendererText()
                renderer.set_fixed_height_from_font(1)
                renderer.set_alignment(align, 0.5)
                col = Gtk.TreeViewColumn(text, renderer, text=i)

            col.set_alignment(align)
            col.set_min_width(width)
            col.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
            col.set_clickable(True)
            col.connect('clicked', self.on_column_clicked)
            self._view.append_column(col)

        # Fill it initially with the full Queue
        self.add(self._view)
        self.show_all()
class PlaylistWidget(Gtk.ScrolledWindow):
    def __init__(self, col_names=(('Artist', 150, 1.0), ('Album', 200, 0.0), ('Title', 250, 1.0))):
        Gtk.ScrolledWindow.__init__(self)
        self.set_policy(
            Gtk.PolicyType.AUTOMATIC,
            Gtk.PolicyType.AUTOMATIC
        )

        self._menu = None
        self._model = PlaylistTreeModel([])

        # Treeview Configuration
        self._view = Gtk.TreeView(model=None)
        self._view.set_fixed_height_mode(True)
        # self._view.set_rules_hint(True)
        self._view.set_enable_search(False)
        self._view.set_search_column(-1)

        self._view.connect('row-activated', self._on_row_activated)
        self._view.connect('button-press-event', self._on_button_press_event)

        add_keybindings(self._view, {
            '<Shift>space': lambda view, key: self.jump_to_selection()
        })

        selection = self._view.get_selection()
        selection.set_mode(Gtk.SelectionMode.MULTIPLE)

        for i, (text, width, align) in enumerate(col_names):
            # TODO: Think of a clearer API
            if '<pixbuf>' in text:
                _, header = text.split(':', maxsplit=1)
                renderer = Gtk.CellRendererPixbuf()
                col = Gtk.TreeViewColumn(header)
                col.pack_start(renderer, False)
                col.add_attribute(renderer, 'stock-id', i)
            elif '<progress>' in text:
                _, header = text.split(':', maxsplit=1)
                col = Gtk.TreeViewColumn(header)
                renderer = Gtk.CellRendererProgress()
                col.pack_start(renderer, False)
                col.add_attribute(renderer, 'text', i)
                col.add_attribute(renderer, 'value', i)
            else:
                renderer = Gtk.CellRendererText()
                renderer.set_fixed_height_from_font(1)
                renderer.set_alignment(align, 0.5)
                col = Gtk.TreeViewColumn(text, renderer, text=i)

            col.set_alignment(align)
            col.set_min_width(width)
            col.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
            col.set_clickable(True)
            col.connect('clicked', self.on_column_clicked)
            self._view.append_column(col)

        # Fill it initially with the full Queue
        self.add(self._view)
        self.show_all()

    def set_model(self, model):
        self._model = model
        self._view.set_model(model)

    def set_menu(self, menu):
        self._menu = menu

    def get_selected_rows(self):
        model, paths = self._view.get_selection().get_selected_rows()
        return model, (model[path] for path in paths)

    def jump_to_selection(self):
        rows = self._view.get_selection().get_selected_rows()
        if rows:
            self._view.scroll_to_cell(rows[int(len(rows) / 2)], None, False, 0, 0)
        return True

    ############################
    #  Private Helper Methods  #
    ############################

    def _check_query_syntax(self, query_text):
        try:
            LOGGER.debug('Compiled Query: ' + parse_query(query_text))
            return None, -1
        except QueryParseException as error:
            logging.debug('Invalid Query Syntax: ' + str(error.msg))
            return error.msg, error.pos

    ######################
    #  Subclass Methods  #
    ######################

    def do_search(self, query):
        raise NotImplementedError(
            'PlaylistWidget subclasses should implement do_search()'
        )

    def do_row_activated(self, row):
        raise NotImplementedError(
            'PlaylistWidget subclasses should implement do_row_activated()'
        )

    ###########################
    #  Signal Implementation  #
    ###########################

    def on_column_clicked(self, column):
        scale = Gtk.Scale.new_with_range(
            Gtk.Orientation.HORIZONTAL,
            0.0, 1.0, 0.05
        )
        scale.set_size_request(170, -1)

        entry = Gtk.Entry()
        entry.set_size_request(170, -1)

        grid = Gtk.Grid()
        grid.set_border_width(5)
        grid.attach(make_configure_row('Name:', entry), 0, 0, 1, 1)
        grid.attach(make_configure_row('Alignment:', scale), 0, 1, 1, 1)

        popover = create_action_popover(
            column.get_button(), grid,
            'Configure Column'
        )
        popover.show_all()

    def _on_row_activated(self, view, path, tv_column):
        self.do_row_activated(self._model.data_from_path(path))

    def _on_button_press_event(self, treeview, event):
        if self._menu is not None:
            if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
                self._menu.simple_popup(event)
                return True
        return False