Example #1
0
    def __init__(self):
        super(BlaQueue, self).__init__()

        self.__size = 0
        self.__length = 0
        self.clipboard = []

        self.__treeview = BlaTreeView(view_id=blaconst.VIEW_QUEUE)
        self.__treeview.set_model(gtk.ListStore(*self.__layout))
        self.__treeview.set_enable_search(False)
        self.__treeview.set_property("rules_hint", True)

        self.set_shadow_type(gtk.SHADOW_IN)
        self.add(self.__treeview)

        self.__treeview.enable_model_drag_dest(
            [("queue", gtk.TARGET_SAME_WIDGET, 3)], gtk.gdk.ACTION_COPY)
        self.__treeview.enable_model_drag_source(
            gtk.gdk.BUTTON1_MASK,
            [("queue", gtk.TARGET_SAME_WIDGET, 3)],
            gtk.gdk.ACTION_COPY)

        self.__treeview.connect("popup", popup, blaconst.VIEW_QUEUE, self)
        self.__treeview.connect("row_activated", self.play_item)
        self.__treeview.connect(
            "button_press_event", self.__button_press_event)
        self.__treeview.connect("key_press_event", self.__key_press_event)
        self.__treeview.connect("drag_data_get", self.__drag_data_get)
        self.__treeview.connect("drag_data_received", self.__drag_data_recv)

        update_columns(self.__treeview, view_id=blaconst.VIEW_QUEUE)
        self.show_all()
Example #2
0
class BlaQueue(BlaScrolledWindow):
    __metaclass__ = BlaViewMeta("Queue")

    __layout = (
        gobject.TYPE_PYOBJECT,  # An instance of BlaTrackListItem
        gobject.TYPE_STRING     # Position in the queue
    )

    def __init__(self):
        super(BlaQueue, self).__init__()

        self.__size = 0
        self.__length = 0
        self.clipboard = []

        self.__treeview = BlaTreeView(view_id=blaconst.VIEW_QUEUE)
        self.__treeview.set_model(gtk.ListStore(*self.__layout))
        self.__treeview.set_enable_search(False)
        self.__treeview.set_property("rules_hint", True)

        self.set_shadow_type(gtk.SHADOW_IN)
        self.add(self.__treeview)

        self.__treeview.enable_model_drag_dest(
            [("queue", gtk.TARGET_SAME_WIDGET, 3)], gtk.gdk.ACTION_COPY)
        self.__treeview.enable_model_drag_source(
            gtk.gdk.BUTTON1_MASK,
            [("queue", gtk.TARGET_SAME_WIDGET, 3)],
            gtk.gdk.ACTION_COPY)

        self.__treeview.connect("popup", popup, blaconst.VIEW_QUEUE, self)
        self.__treeview.connect("row_activated", self.play_item)
        self.__treeview.connect(
            "button_press_event", self.__button_press_event)
        self.__treeview.connect("key_press_event", self.__key_press_event)
        self.__treeview.connect("drag_data_get", self.__drag_data_get)
        self.__treeview.connect("drag_data_received", self.__drag_data_recv)

        update_columns(self.__treeview, view_id=blaconst.VIEW_QUEUE)
        self.show_all()

    def __button_press_event(self, treeview, event):
        if (event.button == 2 and
            event.type not in [gtk.gdk._2BUTTON_PRESS,
                               gtk.gdk._3BUTTON_PRESS]):
            self.paste()
            return True

    def __key_press_event(self, treeview, event):
        if blagui.is_accel(event, "<Ctrl>X"):
            self.cut()
        elif blagui.is_accel(event, "<Ctrl>C"):
            self.copy()
        elif blagui.is_accel(event, "<Ctrl>V"):
            self.paste()
        elif blagui.is_accel(event, "Delete"):
            self.remove()
        return False

    def __drag_data_get(self, treeview, drag_context, selection_data, info,
                        time):
        data = pickle.dumps(treeview.get_selection().get_selected_rows()[-1],
                            pickle.HIGHEST_PROTOCOL)
        selection_data.set("", 8, data)

    def __drag_data_recv(self, treeview, drag_context, x, y, selection_data,
                         info, time):
        drop_info = treeview.get_dest_row_at_pos(x, y)
        model = self.__treeview.get_model()
        paths = pickle.loads(selection_data.data)

        # TODO: factor this out so we can use the same for the playlist
        if drop_info:
            path, pos = drop_info
            iterator = model.get_iter(path)

            if (pos == gtk.TREE_VIEW_DROP_BEFORE or
                pos == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):
                move_before = model.move_before
                def move_func(it):
                    move_before(it, iterator)
            else:
                move_after = model.move_after
                def move_func(it):
                    move_after(it, iterator)
                paths.reverse()
        else:
            iterator = None
            move_before = model.move_before
            def move_func(it):
                move_before(it, iterator)

        get_iter = model.get_iter
        iterators = map(get_iter, paths)
        map(move_func, iterators)
        self.update_queue_positions()

    def __add_items(self, items, path=None, select_rows=False):
        treeview = self.__treeview
        model = treeview.get_model()
        iterator = None

        try:
            if (not treeview.get_selection().get_selected_rows()[-1] or
                path == -1):
                raise TypeError
            if not path:
                path, column = treeview.get_cursor()
        except TypeError:
            path = (len(model),)
            append = model.append
            def insert_func(iterator, item):
                append(item)
        else:
            iterator = model.get_iter(path)
            insert_func = model.insert_before
            items.reverse()

        for item in items:
            iterator = insert_func(iterator, [item, None])

        if select_rows:
            treeview.freeze_notify()
            selection = treeview.get_selection()
            selection.unselect_all()
            select_path = selection.select_path
            map(select_path, xrange(path[0], path[0] + len(items)))
            treeview.thaw_notify()

        self.update_queue_positions()

    def __get_items(self, remove=True):
        treeview = self.__treeview
        model, selections = treeview.get_selection().get_selected_rows()
        if selections:
            get_iter = model.get_iter
            iterators = map(get_iter, selections)
            items = [model[iterator][0] for iterator in iterators]
            if remove:
                remove = model.remove
                map(remove, iterators)
                self.update_queue_positions()
            return items
        return []

    def play_item(self, treeview, path, column=None):
        model = treeview.get_model()
        iterator = model.get_iter(path)
        model[iterator][0].play()
        if blacfg.getboolean("general", "queue.remove.when.activated"):
            model.remove(iterator)
            self.update_queue_positions()

    def update_statusbar(self):
        model = self.__treeview.get_model()
        count = len(model)
        if count == 0:
            info = ""
        else:
            info = parse_track_list_stats(count, self.__size, self.__length)
        BlaStatusbar.set_view_info(blaconst.VIEW_QUEUE, info)

    def select(self, type_):
        treeview = self.__treeview
        selection = treeview.get_selection()
        model, selected_paths = selection.get_selected_rows()

        if type_ == blaconst.SELECT_ALL:
            selection.select_all()
            return
        elif type_ == blaconst.SELECT_COMPLEMENT:
            selected_paths = set(selected_paths)
            paths = set([(p,) for p in xrange(len(model))])
            paths.difference_update(selected_paths)
            selection.unselect_all()
            select_path = selection.select_path
            map(select_path, paths)
            return
        elif type_ == blaconst.SELECT_BY_ARTISTS:
            column_id = COLUMN_ARTIST
        elif type_ == blaconst.SELECT_BY_ALBUMS:
            column_id = COLUMN_ALBUM
        elif type_ == blaconst.SELECT_BY_ALBUM_ARTISTS:
            column_id = COLUMN_ALBUM_ARTIST
        else:
            column_id = COLUMN_GENRE

        items = [model[path][0] for path in selected_paths]
        eval_ = BlaEval(column_id).eval
        values = set()
        for item in items:
            values.add(eval_(item.track).lower())
        if not values:
            return
        r = re.compile(
            r"^(%s)$" % "|".join(values), re.UNICODE | re.IGNORECASE)
        items = [row[0] for row in model if r.match(eval_(row[0].track))]
        paths = [row.path for row in model if row[0] in items]
        selection.unselect_all()
        select_path = selection.select_path
        map(select_path, paths)

    def update_queue_positions(self):
        model = self.__treeview.get_model()

        # Update the position labels for our own treeview.
        for idx, row in enumerate(model):
            model[row.path][1] = idx+1

        # Invalidate the visible rows of the current playlists so the
        # position labels also get updated in playlists.
        playlist = playlist_manager.get_current_playlist()
        playlist.invalidate_visible_rows()

        # Calculate size and length of the queue and update the statusbar.
        size = length = 0
        for row in model:
            track = row[0].track
            size += track[FILESIZE]
            length += track[LENGTH]
        self.__size, self.__length = size, length
        self.emit("count_changed", blaconst.VIEW_QUEUE, self.n_items)
        self.update_statusbar()

    def get_queue_positions(self, item):
        model = self.__treeview.get_model()
        return [row[1] for row in model if row[0] == item]

    def queue_items(self, items):
        if not items:
            return

        # If any of the items is not an instance of BlaTrackListItem it means
        # all of the items are actually just URIs which stem from the library
        # browser and are not part of a playlist.
        if not isinstance(items[0], BlaTrackListItem):
            items = map(BlaTrackListItem, items)

        count = blaconst.QUEUE_MAX_ITEMS - self.n_items
        self.__add_items(items[:count], path=-1)

    def remove_items(self, items):
        # This is invoked by playlists who want to remove tracks from the
        # queue.
        model = self.__treeview.get_model()
        for row in model:
            if row[0] in items:
                model.remove(row.iter)
        self.update_queue_positions()

    def get_queue(self):
        queue = []
        playlists = playlist_manager.get_playlists()

        for row in self.__treeview.get_model():
            item = row[0]
            playlist = item.playlist

            try:
                playlist_idx = playlists.index(playlist)
            except ValueError:
                item = (item.uri,)
            else:
                item = (playlist_idx,
                        playlist.get_path_from_item(item, all_=True))

            queue.append(item)

        return queue

    def restore(self, items):
        print_i("Restoring the play queue")

        if not items:
            return

        playlists = playlist_manager.get_playlists()

        for idx, item in enumerate(items):
            try:
                playlist_idx, path = item
            except ValueError:
                # Library tracks that are not part of a playlist.
                item = BlaTrackListItem(item)
            else:
                item = playlists[playlist_idx].get_item_from_path(path)
            items[idx] = item

        self.queue_items(items)

    def cut(self, *args):
        self.clipboard = self.__get_items(remove=True)
        ui_manager.update_menu(blaconst.VIEW_QUEUE)

    def copy(self, *args):
        # We specifically don't create actual copies of items here as it's not
        # desired to have unique ones in the queue. Copied and pasted tracks
        # should still refer to the same BlaTrackListItem instances which are
        # possibly part of a playlist.
        self.clipboard = self.__get_items(remove=False)
        ui_manager.update_menu(blaconst.VIEW_QUEUE)

    def paste(self, *args, **kwargs):
        self.__add_items(items=self.clipboard, select_rows=True)

    def remove(self, *args):
        self.__get_items(remove=True)

    def remove_duplicates(self):
        unique = set()
        model = self.__treeview.get_model()
        for row in model:
            uri = row[0].uri
            if uri not in unique:
                unique.add(uri)
            else:
                model.remove(row.iter)
        self.update_queue_positions()

    def remove_invalid_tracks(self):
        model = self.__treeview.get_model()
        isfile = os.path.isfile

        for row in model:
            uri = row[0].uri
            if not isfile(uri):
                model.remove(row.iter)
        self.update_queue_positions()

    def clear(self):
        self.__treeview.get_model().clear()
        self.update_queue_positions()

    def get_item(self):
        model = self.__treeview.get_model()
        iterator = model.get_iter_first()
        if iterator:
            item = model[iterator][0]
            model.remove(iterator)
            self.update_queue_positions()
            return item
        return None

    @property
    def n_items(self):
        return len(self.__treeview.get_model())