Example #1
0
    def sort(self):

        albums = SortedCollection(key=lambda album: getattr(album, 'name'))

        gs = GSetting()
        source_settings = gs.get_setting(gs.Path.PLUGIN)
        key = source_settings[gs.PluginKey.SORT_BY_ARTIST]
        order = source_settings[gs.PluginKey.SORT_ORDER_ARTIST]

        sort_keys = {
            'name_artist': ('album_sort', 'album_sort'),
            'year_artist': ('real_year', 'calc_year_sort'),
            'rating_artist': ('rating', 'album_sort')
        }

        props = sort_keys[key]

        def key_function(album):
            keys = [getattr(album, prop) for prop in props]
            return keys

        # remember the current sort then remove the sort order
        # because sorting will only work in unsorted lists
        sortSettings = self.store.get_sort_column_id()

        self.store.set_sort_column_id(-1, Gtk.SortType.ASCENDING)

        for artist in self._iters:
            albums.clear()
            albums.key = key_function

            if 'album' in self._iters[artist] and len(
                    self._iters[artist]['album']) > 1:
                # we only need to sort an artists albums if there is more than one album

                # sort all the artists albums
                for album_iter in self._iters[artist]['album']:
                    albums.insert(self._tree_store[album_iter][
                        self.columns['artist_album']])

                if not order:
                    albums = reversed(albums)

                # now we iterate through the sorted artist albums.  Look and swap iters
                # according to where they are in the tree store

                artist_iter = self._iters[artist]['iter']
                next_iter = self._tree_store.iter_children(artist_iter)

                for album in albums:
                    if self._tree_store[next_iter][
                            self.columns['artist_album']] != album:
                        self._tree_store.swap(next_iter,
                                              self._albumiters[album]['iter'])
                        next_iter = self._albumiters[album]['iter']
                    next_iter = self._tree_store.iter_next(next_iter)

        # now we have finished sorting, reapply the sort
        if sortSettings[0]:
            self.store.set_sort_column_id(*sortSettings)
Example #2
0
    def __init__(self, album_manager):
        super(ArtistsModel, self).__init__()

        self.album_manager = album_manager
        self._iters = {}
        self._albumiters = {}
        self._artists = SortedCollection(
            key=lambda artist: getattr(artist, 'name'))

        self._tree_store = Gtk.TreeStore(str, GdkPixbuf.Pixbuf, object, 
            bool,  str, str, str)
            
        # sorting idle call
        self._sort_process = None

        # create the filtered store that's used with the view
        self._filtered_store = self._tree_store.filter_new()
        self._filtered_store.set_visible_column(ArtistsModel.columns['show'])
        
        self._tree_sort = Gtk.TreeModelSort(model=self._filtered_store)     
        #self._tree_sort.set_default_sort_func(lambda *unused: 0)       
        self._tree_sort.set_sort_func(0, self._compare, None)
        
        self._connect_signals()
    def sort(self):

        albums = SortedCollection(key=lambda album: getattr(album, 'name'))

        gs = GSetting()
        source_settings = gs.get_setting(gs.Path.PLUGIN)
        key = source_settings[gs.PluginKey.SORT_BY_ARTIST]
        order = source_settings[gs.PluginKey.SORT_ORDER_ARTIST]

        sort_keys = {
            'name_artist': ('album_sort', 'album_sort'),
            'year_artist': ('real_year', 'calc_year_sort'),
            'rating_artist': ('rating', 'album_sort')
        }

        props = sort_keys[key]

        def key_function(album):
            keys = [getattr(album, prop) for prop in props]
            return keys

        # remember the current sort then remove the sort order
        # because sorting will only work in unsorted lists
        sortSettings = self.store.get_sort_column_id()

        self.store.set_sort_column_id(-1, Gtk.SortType.ASCENDING)

        for artist in self._iters:
            albums.clear()
            albums.key = key_function

            if 'album' in self._iters[artist] and len(self._iters[artist]['album']) > 1:
                # we only need to sort an artists albums if there is more than one album

                # sort all the artists albums
                for album_iter in self._iters[artist]['album']:
                    albums.insert(self._tree_store[album_iter][self.columns['artist_album']])

                if not order:
                    albums = reversed(albums)

                # now we iterate through the sorted artist albums.  Look and swap iters
                # according to where they are in the tree store

                artist_iter = self._iters[artist]['iter']
                next_iter = self._tree_store.iter_children(artist_iter)

                for album in albums:
                    if self._tree_store[next_iter][self.columns['artist_album']] != album:
                        self._tree_store.swap(next_iter, self._albumiters[album]['iter'])
                        next_iter = self._albumiters[album]['iter']
                    next_iter = self._tree_store.iter_next(next_iter)

        # now we have finished sorting, reapply the sort
        if sortSettings[0]:
            self.store.set_sort_column_id(*sortSettings)
    def __init__(self, album_manager):
        super(ArtistsModel, self).__init__()

        self.album_manager = album_manager
        self._iters = {}
        self._albumiters = {}
        self._artists = SortedCollection(
            key=lambda artist: getattr(artist, 'name'))

        self._tree_store = Gtk.TreeStore(str, GdkPixbuf.Pixbuf, object,
                                         bool, str, str, str)

        # sorting idle call
        self._sort_process = None

        # create the filtered store that's used with the view
        self._filtered_store = self._tree_store.filter_new()
        self._filtered_store.set_visible_column(ArtistsModel.columns['show'])

        self._tree_sort = Gtk.TreeModelSort(model=self._filtered_store)
        # self._tree_sort.set_default_sort_func(lambda *unused: 0)
        self._tree_sort.set_sort_func(0, self._compare, None)

        self._connect_signals()
Example #5
0
class ArtistsModel(GObject.Object):
    '''
    Model that contains artists, keeps them sorted, filtered and provides an
    external `Gtk.TreeModel` interface to use as part of a Gtk interface.

    The `Gtk.TreeModel` haves the following structure:
    column 0 -> string containing the artist name
    column 1 -> pixbuf of the artist's cover.
    column 2 -> instance of the artist or album itself.
    column 3 -> boolean that indicates if the row should be shown
    column 4 -> blank text column to pad the view correctly
    column 5 -> markup containing formatted text
    column 6 -> blank text for the expander column
    '''
    # signals
    __gsignals__ = {
        'update-path': (GObject.SIGNAL_RUN_LAST, None, (object,)),
        'visual-updated': ((GObject.SIGNAL_RUN_LAST, None, (object, object)))
        }

    # list of columns names and positions on the TreeModel
    columns = {'tooltip': 0, 'pixbuf': 1, 
        'artist_album': 2, 'show': 3, 
        'empty': 4, 'markup': 5, 'expander': 6}
        
    def __init__(self, album_manager):
        super(ArtistsModel, self).__init__()

        self.album_manager = album_manager
        self._iters = {}
        self._albumiters = {}
        self._artists = SortedCollection(
            key=lambda artist: getattr(artist, 'name'))

        self._tree_store = Gtk.TreeStore(str, GdkPixbuf.Pixbuf, object, 
            bool,  str, str, str)
            
        # sorting idle call
        self._sort_process = None

        # create the filtered store that's used with the view
        self._filtered_store = self._tree_store.filter_new()
        self._filtered_store.set_visible_column(ArtistsModel.columns['show'])
        
        self._tree_sort = Gtk.TreeModelSort(model=self._filtered_store)     
        #self._tree_sort.set_default_sort_func(lambda *unused: 0)       
        self._tree_sort.set_sort_func(0, self._compare, None)
        
        self._connect_signals()
        
    def _connect_signals(self):
        self.connect('update-path', self._on_update_path)
        self.album_manager.model.connect('filter-changed', self._on_album_filter_changed)
        
    def _on_album_filter_changed(self, *args):
        if len(self._iters) == 0:
            return
            
        artists = list(set(row[AlbumsModel.columns['album']].artist for row in self.album_manager.model.store))

        for artist in self._iters:
            self.show(artist, artist in artists)
        
    def _compare(self, model, row1, row2, user_data):
        
        if not model.iter_has_child(row1) or \
            not model.iter_has_child(row2):
                return 0
                
        sort_column = 0
        value1 = RB.search_fold(model.get_value(row1, sort_column))
        value2 = RB.search_fold(model.get_value(row2, sort_column))
        if value1 < value2:
            return -1
        elif value1 == value2:
            return 0
        else:
            return 1
 
    @property
    def store(self):
        #return self._filtered_store
        return self._tree_sort

    def add(self, artist):
        '''
        Add an artist to the model.

        :param artist: `Artist` to be added to the model.
        '''
        # generate necessary values
        values = self._generate_artist_values(artist)
        # insert the values
        pos = self._artists.insert(artist)
        tree_iter = self._tree_store.insert(None,pos, values)
        child_iter = self._tree_store.insert(tree_iter, pos, values) # dummy child row so that the expand is available
        # connect signals
        ids = (artist.connect('modified', self._artist_modified),
            artist.connect('cover-updated', self._cover_updated),
            artist.connect('emptied', self.remove))
        
        if not artist.name in self._iters:
            self._iters[artist.name] = {}
        self._iters[artist.name] = {'artist_album': artist,
            'iter': tree_iter, 'dummy_iter': child_iter, 'ids': ids}
        return tree_iter
    
    def _emit_signal(self, tree_iter, signal):
        # we get the filtered path and iter since that's what the outside world
        # interacts with
        tree_path = self._filtered_store.convert_child_path_to_path(
            self._tree_store.get_path(tree_iter))

        if tree_path:
            # if there's no path, the album doesn't show on the filtered model
            # so no one needs to know
            tree_iter = self._filtered_store.get_iter(tree_path)

            self.emit(signal, tree_path, tree_iter)
            
    def remove(self, *args):
        print ("artist remove")
        
    def _cover_updated(self, artist):
        tree_iter = self._iters[artist.name]['iter']

        if self._tree_store.iter_is_valid(tree_iter):
            # only update if the iter is valid
            pixbuf = artist.cover.pixbuf

            self._tree_store.set_value(tree_iter, self.columns['pixbuf'],
                pixbuf)

            self._emit_signal(tree_iter, 'visual-updated')
            
    def _artist_modified(self, *args):
        print ("artist modified")

    def _on_update_path(self, widget, treepath):
        '''
           called when update-path signal is called
        '''
        artist = self.get_from_path(treepath)
        albums = self.album_manager.model.get_all()
        self.add_album_to_artist(artist, albums)
        
    def add_album_to_artist(self, artist, albums):
        '''
        Add an album to the artist in the model.

        :param artist: `Artist` for the album to be added to (i.e. the parent)
        :param album: array of `Album` which are the children of the Artist
        
        '''
        # get the artist iter
        artist_iter = self._iters[artist.name]['iter']
        
        # now remove the dummy_iter - if this fails, we've removed this 
        # before and have no need to add albums

        if 'dummy_iter' in self._iters[artist.name]:
            self._iters[artist.name]['album'] = []
                    
        for album in albums:
            if artist.name == album.artist and not (album in self._albumiters):
                # now for all matching albums that were found lets add to the model

                # generate necessary values
                values = self._generate_album_values(album)
                # insert the values
                tree_iter = self._tree_store.append(artist_iter, values)
                self._albumiters[album] = {}
                self._albumiters[album]['iter'] = tree_iter
                self._iters[artist.name]['album'].append(tree_iter)
                    
                # connect signals
                ids = (album.connect('modified', self._album_modified),
                    album.connect('cover-updated', self._album_coverupdate),
                    album.connect('emptied', self._album_emptied))
                    
                self._albumiters[album]['ids'] = ids 

        if 'dummy_iter' in self._iters[artist.name]:
            self._tree_store.remove(self._iters[artist.name]['dummy_iter'])
            del self._iters[artist.name]['dummy_iter']
            
        self.sort() # ensure the added albums are sorted correctly

    def _album_modified(self, album):
        print ("album modified")
        print (album)
        if not (album in self._albumiters):
            print ("not found in albumiters")
            return
            
        tree_iter = self._albumiters[album]['iter']

        if self._tree_store.iter_is_valid(tree_iter):
            # only update if the iter is valid
            # generate and update values
            tooltip, pixbuf, album, show, blank, markup, empty = \
                self._generate_album_values(album)

            self._tree_store.set(tree_iter, self.columns['tooltip'], tooltip,
                self.columns['markup'], markup, self.columns['show'], show)
                
            self.sort() # ensure the added albums are sorted correctly
       
    def _album_emptied(self, album):
        '''
        Removes this album from the model.

        :param album: `Album` to be removed from the model.
        '''
        print ('album emptied')
        print (album)
        print (album.artist)
        if not (album in self._albumiters):
            print ("not found in albumiters")
            return

        artist = self.get(album.artist)
        album_iter = self._albumiters[album]['iter']

        self._iters[album.artist]['album'].remove(album_iter)
        self._tree_store.remove(album_iter)

        # disconnect signals
        for sig_id in self._albumiters[album]['ids']:
            album.disconnect(sig_id)

        del self._albumiters[album]

        # test if there are any more albums for this artist otherwise just cleanup
        if len(self._iters[album.artist]['album']) == 0:
            self.remove(artist)
            self._on_album_filter_changed(_)
            
    def _album_coverupdate(self, album):
        tooltip, pixbuf, album, show, blank, markup, empty = self._generate_album_values(album)
        self._tree_store.set_value(self._albumiters[album]['iter'], 
            self.columns['pixbuf'], pixbuf)
            
    def _generate_artist_values(self, artist):
        tooltip = artist.name
        pixbuf = artist.cover.pixbuf
        show = True

        return tooltip, pixbuf, artist, show, '', \
            GLib.markup_escape_text(tooltip), ''
    
    def _generate_album_values(self, album):
        tooltip = album.name
        pixbuf = album.cover.pixbuf.scale_simple(48,48,GdkPixbuf.InterpType.BILINEAR)
        show = True
        
        rating = album.rating
        if int(rating) > 0:
            rating = u'\u2605' * int(rating)
        else:
            rating = ''
        
        year = ' (' + str(album.real_year) +')'
            
        track_count = album.track_count
        if track_count == 1:
            detail = rb3compat.unicodedecode(_(' with 1 track'), 'UTF-8')
        else:
            detail = rb3compat.unicodedecode(_(' with %d tracks') % 
                        track_count, 'UTF-8')
                        
        duration = album.duration / 60
        
        if duration == 1:
            detail += rb3compat.unicodedecode(_(' and a duration of 1 minute'), 'UTF-8')
        else:
            detail += rb3compat.unicodedecode(_(' and a duration of %d minutes') % 
                        duration, 'UTF-8')
            
        tooltip = rb3compat.unicodestr(tooltip, 'utf-8')
        tooltip = rb3compat.unicodeencode(tooltip, 'utf-8')
        import cgi
        
        formatted = '<b><i>' + \
            cgi.escape(rb3compat.unicodedecode(tooltip, 'utf-8')) + \
            '</i></b>' + \
            year + \
            ' ' + rating + \
            '\n<small>' + \
            GLib.markup_escape_text(detail) + \
            '</small>'

        return tooltip, pixbuf, album, show, '', formatted, ''

    def remove(self, artist):
        '''
        Removes this artist from the model.

        :param artist: `Artist` to be removed from the model.
        '''
        self._artists.remove(artist)
        self._tree_store.remove(self._iters[artist.name]['iter'])

        del self._iters[artist.name]

    def contains(self, artist_name):
        '''
        Indicates if the model contains a specific artist.

        :param artist_name: `str` name of the artist.
        '''
        return artist_name in self._iters

    def get(self, artist_name):
        '''
        Returns the requested Artist.

        :param artist_name: `str` name of the artist.
        '''
        return self._iters[artist_name]['artist_album']
        
    def get_albums(self, artist_name):
        '''
        Returns the displayed albums for the requested artist

        :param artist_name: `str` name of the artist.
        '''
        
        albums = []
        
        artist_iter = self._iters[artist_name]['iter']
        next_iter = self._tree_store.iter_children(artist_iter)
        
        while next_iter != None:
            albums.append(self._tree_store[next_iter][self.columns['artist_album']])
            next_iter = self._tree_store.iter_next(next_iter)
        #if 'album' in self._iters[artist_name]:
        #    for album_iter in self._iters[artist_name]['album']:
        #        path = self._tree_store.get_path(album_iter)
        #        if path:
        #        tree_path = self._filtered_store.convert_child_path_to_path(
        #            )
        #        albums.append(self.get_from_path(tree_path))
            
        return albums

    def get_all(self):
        '''
        Returns a collection of all the artists in this model.
        '''
        return self._artists
        
    def get_from_path(self, path):
        '''
        Returns the Artist or Album referenced by a `Gtk.TreeModelSort` path.

        :param path: `Gtk.TreePath` referencing the artist.
        '''
        return self.store[path][self.columns['artist_album']]

    def get_path(self, artist):
        print (artist.name)
        print (self._iters[artist.name]['iter'])
        return self._tree_store.get_path(
                self._iters[artist.name]['iter'])
                
    def get_from_ext_db_key(self, key):
        '''
        Returns the requested artist.

        :param key: ext_db_key
        '''
        # get the album name and artist
        name = key.get_field('artist')
        
        # first check if there's a direct match
        artist = self.get(name) if self.contains(name) else None
        return artist

    def show(self, artist_name, show):
        '''
        filters/unfilters an artist, making it visible to the publicly available model's
        `Gtk.TreeModel`

        :param artist: str containing the name of the artist to show or hide.
        :param show: `bool` indcating whether to show(True) or hide(False) the
            artist.
        '''
        artist_iter = self._iters[artist_name]['iter']

        if self._tree_store.iter_is_valid(artist_iter):
            self._tree_store.set_value(artist_iter, self.columns['show'], show)
            
            
    def sort(self):
        
        albums = SortedCollection(key=lambda album: getattr(album, 'name'))
        
        gs = GSetting()
        source_settings = gs.get_setting(gs.Path.PLUGIN)
        key = source_settings[gs.PluginKey.SORT_BY_ARTIST]
        order = source_settings[gs.PluginKey.SORT_ORDER_ARTIST]
        
        sort_keys = {
            'name_artist': ('album_sort', 'album_sort'),
            'year_artist': ('real_year', 'calc_year_sort'),
            'rating_artist': ('rating', 'album_sort')
            }

        props = sort_keys[key]
        
        def key_function(album):
            keys = [getattr(album, prop) for prop in props]
            return keys
            
        # remember the current sort then remove the sort order
        # because sorting will only work in unsorted lists
        sortSettings = self.store.get_sort_column_id()

        self.store.set_sort_column_id(-1, Gtk.SortType.ASCENDING)
        
        for artist in self._iters:
            albums.clear()
            albums.key = key_function
            
            if 'album' in self._iters[artist] and len(self._iters[artist]['album']) > 1:
                # we only need to sort an artists albums if there is more than one album
                
                # sort all the artists albums
                for album_iter in self._iters[artist]['album']:
                    albums.insert(self._tree_store[album_iter][self.columns['artist_album']])

                if not order:
                    albums = reversed(albums)
        
                # now we iterate through the sorted artist albums.  Look and swap iters
                # according to where they are in the tree store
            
                artist_iter = self._iters[artist]['iter']
                next_iter = self._tree_store.iter_children(artist_iter)
                
                for album in albums:
                    if self._tree_store[next_iter][self.columns['artist_album']] != album:
                        self._tree_store.swap(next_iter, self._albumiters[album]['iter'])
                        next_iter = self._albumiters[album]['iter']
                    next_iter = self._tree_store.iter_next(next_iter)
        
        # now we have finished sorting, reapply the sort
        self.store.set_sort_column_id(*sortSettings)
class ArtistsModel(GObject.Object):
    '''
    Model that contains artists, keeps them sorted, filtered and provides an
    external `Gtk.TreeModel` interface to use as part of a Gtk interface.

    The `Gtk.TreeModel` haves the following structure:
    column 0 -> string containing the artist name
    column 1 -> pixbuf of the artist's cover.
    column 2 -> instance of the artist or album itself.
    column 3 -> boolean that indicates if the row should be shown
    column 4 -> blank text column to pad the view correctly
    column 5 -> markup containing formatted text
    column 6 -> blank text for the expander column
    '''
    # signals
    __gsignals__ = {
        'update-path': (GObject.SIGNAL_RUN_LAST, None, (object,)),
        'visual-updated': ((GObject.SIGNAL_RUN_LAST, None, (object, object)))
    }

    # list of columns names and positions on the TreeModel
    columns = {'tooltip': 0, 'pixbuf': 1,
               'artist_album': 2, 'show': 3,
               'empty': 4, 'markup': 5, 'expander': 6}

    def __init__(self, album_manager):
        super(ArtistsModel, self).__init__()

        self.album_manager = album_manager
        self._iters = {}
        self._albumiters = {}
        self._artists = SortedCollection(
            key=lambda artist: getattr(artist, 'name'))

        self._tree_store = Gtk.TreeStore(str, GdkPixbuf.Pixbuf, object,
                                         bool, str, str, str)

        # sorting idle call
        self._sort_process = None

        # create the filtered store that's used with the view
        self._filtered_store = self._tree_store.filter_new()
        self._filtered_store.set_visible_column(ArtistsModel.columns['show'])

        self._tree_sort = Gtk.TreeModelSort(model=self._filtered_store)
        # self._tree_sort.set_default_sort_func(lambda *unused: 0)
        self._tree_sort.set_sort_func(0, self._compare, None)

        self._connect_signals()

    def _connect_signals(self):
        self.connect('update-path', self._on_update_path)
        self.album_manager.model.connect('filter-changed', self._on_album_filter_changed)

    def _on_album_filter_changed(self, *args):
        if len(self._iters) == 0:
            return

        artists = list(set(row[AlbumsModel.columns['album']].artist for row in self.album_manager.model.store))

        for artist in self._iters:
            self.show(artist, artist in artists)

    def _compare(self, model, row1, row2, user_data):

        if not model.iter_has_child(row1) or \
                not model.iter_has_child(row2):
            return 0

        sort_column = 0
        value1 = RB.search_fold(model.get_value(row1, sort_column))
        value2 = RB.search_fold(model.get_value(row2, sort_column))
        if value1 < value2:
            return -1
        elif value1 == value2:
            return 0
        else:
            return 1

    @property
    def store(self):
        # return self._filtered_store
        return self._tree_sort

    def add(self, artist):
        '''
        Add an artist to the model.

        :param artist: `Artist` to be added to the model.
        '''
        # generate necessary values
        values = self._generate_artist_values(artist)
        # insert the values
        pos = self._artists.insert(artist)
        tree_iter = self._tree_store.insert(None, pos, values)
        child_iter = self._tree_store.insert(tree_iter, pos, values)  # dummy child row so that the expand is available
        # connect signals
        ids = (artist.connect('modified', self._artist_modified),
               artist.connect('cover-updated', self._cover_updated),
               artist.connect('emptied', self.remove))

        if not artist.name in self._iters:
            self._iters[artist.name] = {}
        self._iters[artist.name] = {'artist_album': artist,
                                    'iter': tree_iter, 'dummy_iter': child_iter, 'ids': ids}
        return tree_iter

    def _emit_signal(self, tree_iter, signal):
        # we get the filtered path and iter since that's what the outside world
        # interacts with
        tree_path = self._filtered_store.convert_child_path_to_path(
            self._tree_store.get_path(tree_iter))

        if tree_path:
            # if there's no path, the album doesn't show on the filtered model
            # so no one needs to know
            tree_iter = self._filtered_store.get_iter(tree_path)

            self.emit(signal, tree_path, tree_iter)

    def remove(self, *args):
        print("artist remove")

    def _cover_updated(self, artist):
        tree_iter = self._iters[artist.name]['iter']

        if self._tree_store.iter_is_valid(tree_iter):
            # only update if the iter is valid
            pixbuf = artist.cover.pixbuf

            self._tree_store.set_value(tree_iter, self.columns['pixbuf'],
                                       pixbuf)

            self._emit_signal(tree_iter, 'visual-updated')

    def _artist_modified(self, *args):
        print("artist modified")

    def _on_update_path(self, widget, treepath):
        '''
           called when update-path signal is called
        '''
        artist = self.get_from_path(treepath)
        albums = self.album_manager.model.get_all()
        self.add_album_to_artist(artist, albums)

    def add_album_to_artist(self, artist, albums):
        '''
        Add an album to the artist in the model.

        :param artist: `Artist` for the album to be added to (i.e. the parent)
        :param album: array of `Album` which are the children of the Artist
        
        '''
        # get the artist iter
        artist_iter = self._iters[artist.name]['iter']

        # now remove the dummy_iter - if this fails, we've removed this 
        # before and have no need to add albums

        if 'dummy_iter' in self._iters[artist.name]:
            self._iters[artist.name]['album'] = []

        for album in albums:
            if artist.name == album.artist and not (album in self._albumiters):
                # now for all matching albums that were found lets add to the model

                # generate necessary values
                values = self._generate_album_values(album)
                # insert the values
                tree_iter = self._tree_store.append(artist_iter, values)
                self._albumiters[album] = {}
                self._albumiters[album]['iter'] = tree_iter
                self._iters[artist.name]['album'].append(tree_iter)

                # connect signals
                ids = (album.connect('modified', self._album_modified),
                       album.connect('cover-updated', self._album_coverupdate),
                       album.connect('emptied', self._album_emptied))

                self._albumiters[album]['ids'] = ids

        if 'dummy_iter' in self._iters[artist.name]:
            self._tree_store.remove(self._iters[artist.name]['dummy_iter'])
            del self._iters[artist.name]['dummy_iter']

        self.sort()  # ensure the added albums are sorted correctly

    def _album_modified(self, album):
        print("album modified")
        print(album)
        if not (album in self._albumiters):
            print("not found in albumiters")
            return

        tree_iter = self._albumiters[album]['iter']

        if self._tree_store.iter_is_valid(tree_iter):
            # only update if the iter is valid
            # generate and update values
            tooltip, pixbuf, album, show, blank, markup, empty = \
                self._generate_album_values(album)

            self._tree_store.set(tree_iter, self.columns['tooltip'], tooltip,
                                 self.columns['markup'], markup, self.columns['show'], show)

            self.sort()  # ensure the added albums are sorted correctly

    def _album_emptied(self, album):
        '''
        Removes this album from the model.

        :param album: `Album` to be removed from the model.
        '''
        print('album emptied')
        print(album)
        print(album.artist)
        if not (album in self._albumiters):
            print("not found in albumiters")
            return

        artist = self.get(album.artist)
        album_iter = self._albumiters[album]['iter']

        self._iters[album.artist]['album'].remove(album_iter)
        self._tree_store.remove(album_iter)

        # disconnect signals
        for sig_id in self._albumiters[album]['ids']:
            album.disconnect(sig_id)

        del self._albumiters[album]

        # test if there are any more albums for this artist otherwise just cleanup
        if len(self._iters[album.artist]['album']) == 0:
            self.remove(artist)
            self._on_album_filter_changed(_)

    def _album_coverupdate(self, album):
        tooltip, pixbuf, album, show, blank, markup, empty = self._generate_album_values(album)
        self._tree_store.set_value(self._albumiters[album]['iter'],
                                   self.columns['pixbuf'], pixbuf)

    def _generate_artist_values(self, artist):
        tooltip = artist.name
        pixbuf = artist.cover.pixbuf
        show = True

        return tooltip, pixbuf, artist, show, '', \
               GLib.markup_escape_text(tooltip), ''

    def _generate_album_values(self, album):
        tooltip = album.name
        pixbuf = album.cover.pixbuf.scale_simple(48, 48, GdkPixbuf.InterpType.BILINEAR)
        show = True

        rating = album.rating
        if int(rating) > 0:
            rating = u'\u2605' * int(rating)
        else:
            rating = ''

        year = ' (' + str(album.real_year) + ')'

        track_count = album.track_count
        if track_count == 1:
            detail = rb3compat.unicodedecode(_(' with 1 track'), 'UTF-8')
        else:
            detail = rb3compat.unicodedecode(_(' with %d tracks') %
                                             track_count, 'UTF-8')

        duration = album.duration / 60

        if duration == 1:
            detail += rb3compat.unicodedecode(_(' and a duration of 1 minute'), 'UTF-8')
        else:
            detail += rb3compat.unicodedecode(_(' and a duration of %d minutes') %
                                              duration, 'UTF-8')

        tooltip = rb3compat.unicodestr(tooltip, 'utf-8')
        tooltip = rb3compat.unicodeencode(tooltip, 'utf-8')
        import cgi

        formatted = '<b><i>' + \
                    cgi.escape(rb3compat.unicodedecode(tooltip, 'utf-8')) + \
                    '</i></b>' + \
                    year + \
                    ' ' + rating + \
                    '\n<small>' + \
                    GLib.markup_escape_text(detail) + \
                    '</small>'

        return tooltip, pixbuf, album, show, '', formatted, ''

    def remove(self, artist):
        '''
        Removes this artist from the model.

        :param artist: `Artist` to be removed from the model.
        '''
        self._artists.remove(artist)
        self._tree_store.remove(self._iters[artist.name]['iter'])

        del self._iters[artist.name]

    def contains(self, artist_name):
        '''
        Indicates if the model contains a specific artist.

        :param artist_name: `str` name of the artist.
        '''
        return artist_name in self._iters

    def get(self, artist_name):
        '''
        Returns the requested Artist.

        :param artist_name: `str` name of the artist.
        '''
        return self._iters[artist_name]['artist_album']

    def get_albums(self, artist_name):
        '''
        Returns the displayed albums for the requested artist

        :param artist_name: `str` name of the artist.
        '''

        albums = []

        artist_iter = self._iters[artist_name]['iter']
        next_iter = self._tree_store.iter_children(artist_iter)

        while next_iter != None:
            albums.append(self._tree_store[next_iter][self.columns['artist_album']])
            next_iter = self._tree_store.iter_next(next_iter)
        # if 'album' in self._iters[artist_name]:
        #    for album_iter in self._iters[artist_name]['album']:
        #        path = self._tree_store.get_path(album_iter)
        #        if path:
        #        tree_path = self._filtered_store.convert_child_path_to_path(
        #            )
        #        albums.append(self.get_from_path(tree_path))

        return albums

    def get_all(self):
        '''
        Returns a collection of all the artists in this model.
        '''
        return self._artists

    def get_from_path(self, path):
        '''
        Returns the Artist or Album referenced by a `Gtk.TreeModelSort` path.

        :param path: `Gtk.TreePath` referencing the artist.
        '''
        return self.store[path][self.columns['artist_album']]

    def get_path(self, artist):
        print(artist.name)
        print(self._iters[artist.name]['iter'])
        return self._tree_store.get_path(
            self._iters[artist.name]['iter'])

    def get_from_ext_db_key(self, key):
        '''
        Returns the requested artist.

        :param key: ext_db_key
        '''
        # get the album name and artist
        name = key.get_field('artist')

        # first check if there's a direct match
        artist = self.get(name) if self.contains(name) else None
        return artist

    def show(self, artist_name, show):
        '''
        filters/unfilters an artist, making it visible to the publicly available model's
        `Gtk.TreeModel`

        :param artist: str containing the name of the artist to show or hide.
        :param show: `bool` indcating whether to show(True) or hide(False) the
            artist.
        '''
        artist_iter = self._iters[artist_name]['iter']

        if self._tree_store.iter_is_valid(artist_iter):
            self._tree_store.set_value(artist_iter, self.columns['show'], show)


    def sort(self):

        albums = SortedCollection(key=lambda album: getattr(album, 'name'))

        gs = GSetting()
        source_settings = gs.get_setting(gs.Path.PLUGIN)
        key = source_settings[gs.PluginKey.SORT_BY_ARTIST]
        order = source_settings[gs.PluginKey.SORT_ORDER_ARTIST]

        sort_keys = {
            'name_artist': ('album_sort', 'album_sort'),
            'year_artist': ('real_year', 'calc_year_sort'),
            'rating_artist': ('rating', 'album_sort')
        }

        props = sort_keys[key]

        def key_function(album):
            keys = [getattr(album, prop) for prop in props]
            return keys

        # remember the current sort then remove the sort order
        # because sorting will only work in unsorted lists
        sortSettings = self.store.get_sort_column_id()

        self.store.set_sort_column_id(-1, Gtk.SortType.ASCENDING)

        for artist in self._iters:
            albums.clear()
            albums.key = key_function

            if 'album' in self._iters[artist] and len(self._iters[artist]['album']) > 1:
                # we only need to sort an artists albums if there is more than one album

                # sort all the artists albums
                for album_iter in self._iters[artist]['album']:
                    albums.insert(self._tree_store[album_iter][self.columns['artist_album']])

                if not order:
                    albums = reversed(albums)

                # now we iterate through the sorted artist albums.  Look and swap iters
                # according to where they are in the tree store

                artist_iter = self._iters[artist]['iter']
                next_iter = self._tree_store.iter_children(artist_iter)

                for album in albums:
                    if self._tree_store[next_iter][self.columns['artist_album']] != album:
                        self._tree_store.swap(next_iter, self._albumiters[album]['iter'])
                        next_iter = self._albumiters[album]['iter']
                    next_iter = self._tree_store.iter_next(next_iter)

        # now we have finished sorting, reapply the sort
        if sortSettings[0]:
            self.store.set_sort_column_id(*sortSettings)