reformat code according to pycharm

Esse commit está contido em:
fossfreedom
2014-05-24 14:52:09 +01:00
commit a5a3d6b85c
22 arquivos alterados com 2184 adições e 2096 exclusões
+176 -176
Ver Arquivo
@@ -55,6 +55,7 @@ ALBUM_LOAD_CHUNK = 50
# default chunk of albums to process when loading covers
COVER_LOAD_CHUNK = 5
class Cover(GObject.Object):
'''
Cover of an Album. It may be initialized either by a file path to the image
@@ -73,8 +74,8 @@ class Cover(GObject.Object):
def __init__(self, size, image):
super(Cover, self).__init__()
assert isinstance( image, str ), "image should be a string"
assert isinstance(image, str), "image should be a string"
self.original = image
self._create_pixbuf(size)
@@ -90,7 +91,7 @@ class Cover(GObject.Object):
def _create_pixbuf(self, size):
self.pixbuf = create_pixbuf_from_file_at_size(
self.original, size, size)
self.size = size
@@ -114,7 +115,6 @@ class Shadow(Cover):
class ShadowedCover(Cover):
def __init__(self, shadow, image):
super(ShadowedCover, self).__init__(shadow.cover_size, image)
@@ -142,11 +142,11 @@ class ShadowedCover(Cover):
# draw cover
Gdk.cairo_set_source_pixbuf(context, self.pixbuf, self._shadow.width,
self._shadow.width)
self._shadow.width)
context.paint()
self.pixbuf = Gdk.pixbuf_get_from_surface(surface, 0, 0,
self._shadow.size, self._shadow.size)
self._shadow.size, self._shadow.size)
class Track(GObject.Object):
@@ -162,10 +162,10 @@ class Track(GObject.Object):
__gsignals__ = {
'modified': (GObject.SIGNAL_RUN_LAST, None, ()),
'deleted': (GObject.SIGNAL_RUN_LAST, None, ())
}
}
__hash__ = GObject.__hash__
def __init__(self, entry, db=None):
super(Track, self).__init__()
@@ -174,7 +174,7 @@ class Track(GObject.Object):
def __eq__(self, other):
return rb.entry_equal(self.entry, other.entry)
@property
def title(self):
return self.entry.get_string(RB.RhythmDBPropType.TITLE)
@@ -214,7 +214,7 @@ class Track(GObject.Object):
@property
def location(self):
return self.entry.get_string(RB.RhythmDBPropType.LOCATION)
@property
def composer(self):
return self.entry.get_string(RB.RhythmDBPropType.COMPOSER)
@@ -231,8 +231,8 @@ class Track(GObject.Object):
def album_artist_sort(self):
sort = self.entry.get_string(
RB.RhythmDBPropType.ALBUM_ARTIST_SORTNAME_FOLDED) or \
self.entry.get_string(RB.RhythmDBPropType.ALBUM_ARTIST_FOLDED) or \
self.entry.get_string(RB.RhythmDBPropType.ARTIST_FOLDED)
self.entry.get_string(RB.RhythmDBPropType.ALBUM_ARTIST_FOLDED) or \
self.entry.get_string(RB.RhythmDBPropType.ARTIST_FOLDED)
return NaturalString(sort)
@@ -240,7 +240,7 @@ class Track(GObject.Object):
def album_sort(self):
sort = self.entry.get_string(
RB.RhythmDBPropType.ALBUM_SORTNAME_FOLDED) or \
self.entry.get_string(RB.RhythmDBPropType.ALBUM_FOLDED)
self.entry.get_string(RB.RhythmDBPropType.ALBUM_FOLDED)
return NaturalString(sort)
@@ -269,10 +269,10 @@ class Album(GObject.Object):
'modified': (GObject.SIGNAL_RUN_FIRST, None, ()),
'emptied': (GObject.SIGNAL_RUN_LAST, None, ()),
'cover-updated': (GObject.SIGNAL_RUN_LAST, None, ())
}
}
__hash__ = GObject.__hash__
def __init__(self, name, artist, cover):
super(Album, self).__init__()
@@ -324,12 +324,12 @@ class Album(GObject.Object):
[track.title for track in self._tracks]))
return self._titles
@property
def composers(self):
if not self._composers:
composers = [track.composer for track in self._tracks if track.composer]
if composers:
if composers:
self._composers = ' '.join(set(composers))
return self._composers
@@ -345,7 +345,7 @@ class Album(GObject.Object):
self._year = 0
return self._year
@property
def real_year(self):
'''
@@ -356,16 +356,16 @@ class Album(GObject.Object):
if calc_year == 0:
calc_year = date.today().year
else:
calc_year = datetime.fromordinal(calc_year).year
calc_year = datetime.fromordinal(calc_year).year
return calc_year
@property
def calc_year_sort(self):
'''
returns a str combinationi of real_year + album name
'''
return str(self.real_year) + self.name
@property
@@ -374,12 +374,12 @@ class Album(GObject.Object):
self._genres = set([track.genre for track in self._tracks])
return self._genres
@property
def rating(self):
if not self._rating:
ratings = [track.rating for track in self._tracks
if track.rating and track.rating != 0]
if track.rating and track.rating != 0]
if len(ratings) > 0:
self._rating = sum(ratings) / len(self._tracks)
@@ -393,7 +393,7 @@ class Album(GObject.Object):
track.rating = new_rating
self._rating = None
self.emit('modified')
@property
def track_count(self):
return len(self._tracks)
@@ -416,7 +416,7 @@ class Album(GObject.Object):
self._cover = new_cover
self._cover_resized_id = self._cover.connect('resized',
lambda *args: self.emit('cover-updated'))
lambda *args: self.emit('cover-updated'))
self.emit('cover-updated')
@@ -435,7 +435,7 @@ class Album(GObject.Object):
else:
# otherwise, only return the entries over the threshold
tracks = [track for track in self._tracks
if track.rating >= rating_threshold]
if track.rating >= rating_threshold]
return sorted(tracks, key=lambda track: (track.disc_number, track.track_number))
@@ -447,26 +447,26 @@ class Album(GObject.Object):
'''
self._tracks.append(track)
ids = (track.connect('modified', self._track_modified),
track.connect('deleted', self._track_deleted))
track.connect('deleted', self._track_deleted))
self._signals_id[track] = ids
self.emit('modified')
def _track_modified(self, track):
print ("_track_modified")
print("_track_modified")
if track.album != self.name:
self._track_deleted(track)
else:
self.emit('modified')
def _track_deleted(self, track):
print ("_track_deleted")
print("_track_deleted")
self._tracks.remove(track)
#list(map(track.disconnect, self._signals_id[track]))
for signal_id in self._signals_id[track]:
track.disconnect(signal_id)
del self._signals_id[track]
if len(self._tracks) == 0:
@@ -497,15 +497,14 @@ class Album(GObject.Object):
def __eq__(self, other):
return other and self.name == other.name and \
self.artist == other.artist
self.artist == other.artist
def __ne__(self, other):
return not other or\
self.name + self.artist != other.name + other.artist
return not other or \
self.name + self.artist != other.name + other.artist
class AlbumFilters(object):
@classmethod
def nay_filter(cls, *args):
def filt(*args):
@@ -524,7 +523,7 @@ class AlbumFilters(object):
words = RB.search_fold(searchtext).split()
params = list(map(RB.search_fold, [album.name, album.artist,
album.artists, album.track_titles, album.composers]))
album.artists, album.track_titles, album.composers]))
matches = []
for word in words:
@@ -560,7 +559,7 @@ class AlbumFilters(object):
return RB.search_fold(searchtext) in RB.search_fold(album.artists)
return filt
@classmethod
def similar_artist_filter(cls, searchtext=None):
def filt(album):
@@ -572,7 +571,7 @@ class AlbumFilters(object):
words = RB.search_fold(searchtext).split()
params = list(map(RB.search_fold, [album.artist,
album.artists]))
album.artists]))
matches = []
for word in words:
@@ -588,7 +587,7 @@ class AlbumFilters(object):
return False not in matches
return filt
@classmethod
def album_name_filter(cls, searchtext=None):
def filt(album):
@@ -605,18 +604,18 @@ class AlbumFilters(object):
if not searchtext:
return True
return RB.search_fold(searchtext) in RB.search_fold(
return RB.search_fold(searchtext) in RB.search_fold(
album.track_titles)
return filt
@classmethod
def composer_filter(cls, searchtext=None):
def filt(album):
if not searchtext:
return True
return RB.search_fold(searchtext) in RB.search_fold(
return RB.search_fold(searchtext) in RB.search_fold(
album.composers)
return filt
@@ -628,7 +627,7 @@ class AlbumFilters(object):
return True
genres = RB.search_fold(' '.join(album.genres))
return RB.search_fold(searchtext) in genres
return RB.search_fold(searchtext) in genres
return filt
@@ -657,6 +656,7 @@ class AlbumFilters(object):
or -1 for all albums older than our standard range which is 1930
or an actual decade for 1930 to 2020
'''
def filt(album):
if not searchdecade:
return True
@@ -691,7 +691,6 @@ AlbumFilters.keys = {
'decade': AlbumFilters.decade_filter
}
sort_keys = {
'name': ('album_sort', 'album_sort'),
'artist': ('album_artist_sort', 'album_artist_sort'),
@@ -720,7 +719,7 @@ class AlbumsModel(GObject.Object):
'visual-updated': ((GObject.SIGNAL_RUN_LAST, None, (object, object))),
'filter-changed': ((GObject.SIGNAL_RUN_FIRST, None, ())),
'album-added': ((GObject.SIGNAL_RUN_LAST, None, (object,)))
}
}
# list of columns names and positions on the TreeModel
columns = {'tooltip': 0, 'pixbuf': 1, 'album': 2, 'markup': 3, 'show': 4}
@@ -731,10 +730,10 @@ class AlbumsModel(GObject.Object):
self._iters = {}
self._albums = SortedCollection(
key=lambda album: getattr(album, 'name'))
self._sortkey = {'type':'name', 'order':True}
self._sortkey = {'type': 'name', 'order': True}
self._tree_store = Gtk.ListStore(str, GdkPixbuf.Pixbuf, object, str,
bool)
bool)
# filters
self._filters = {}
@@ -757,7 +756,7 @@ class AlbumsModel(GObject.Object):
markup = self.emit('generate-markup', album)
self._tree_store.set(tree_iter, self.columns['markup'],
markup)
markup)
self._emit_signal(tree_iter, 'visual-updated')
def error(exception):
@@ -766,7 +765,7 @@ class AlbumsModel(GObject.Object):
return ALBUM_LOAD_CHUNK, process, None, error, None
def _album_modified(self, album):
print ("_album_modified")
print("_album_modified")
tree_iter = self._iters[album.name][album.artist]['iter']
if self._tree_store.iter_is_valid(tree_iter):
@@ -776,7 +775,7 @@ class AlbumsModel(GObject.Object):
self._generate_values(album)
self._tree_store.set(tree_iter, self.columns['tooltip'], tooltip,
self.columns['markup'], markup, self.columns['show'], hidden)
self.columns['markup'], markup, self.columns['show'], hidden)
# reorder the album
new_pos = self._albums.reorder(album)
@@ -794,8 +793,8 @@ class AlbumsModel(GObject.Object):
self._tree_store.move_before(tree_iter, old_iter)
# inform that the album is updated
print ("album modified")
print (album)
print("album modified")
print(album)
self._emit_signal(tree_iter, 'album-updated')
def _cover_updated(self, album):
@@ -806,7 +805,7 @@ class AlbumsModel(GObject.Object):
pixbuf = album.cover.pixbuf
self._tree_store.set_value(tree_iter, self.columns['pixbuf'],
pixbuf)
pixbuf)
self._emit_signal(tree_iter, 'visual-updated')
@@ -836,12 +835,12 @@ class AlbumsModel(GObject.Object):
tree_iter = self._tree_store.insert(self._albums.insert(album), values)
# connect signals
ids = (album.connect('modified', self._album_modified),
album.connect('cover-updated', self._cover_updated),
album.connect('emptied', self.remove))
album.connect('cover-updated', self._cover_updated),
album.connect('emptied', self.remove))
if not album.name in self._iters:
self._iters[album.name] = {}
self._iters[album.name][album.artist] = {'album': album,
'iter': tree_iter, 'ids': ids}
'iter': tree_iter, 'ids': ids}
self.emit('album-added', album)
return tree_iter
@@ -859,8 +858,8 @@ class AlbumsModel(GObject.Object):
:param album: `Album` to be removed from the model.
'''
print ("album model remove")
print (album)
print("album model remove")
print(album)
self._albums.remove(album)
self._tree_store.remove(self._iters[album.name][album.artist]['iter'])
@@ -886,18 +885,18 @@ class AlbumsModel(GObject.Object):
:param album_name: `str` name of the album.
'''
return self._iters[album_name][album_artist]['album']
def get_from_dbentry(self, entry):
'''
Returns the album containing the track corresponding to rhythmdbentry
:param entry: `RhythmDBEntry`
'''
album_artist = entry.get_string(RB.RhythmDBPropType.ALBUM_ARTIST)
album_artist = album_artist if album_artist else entry.get_string(RB.RhythmDBPropType.ARTIST)
album_name = entry.get_string(RB.RhythmDBPropType.ALBUM)
return self._iters[album_name][album_artist]['album']
def get_all(self):
@@ -942,9 +941,9 @@ class AlbumsModel(GObject.Object):
return self._filtered_store.convert_child_path_to_path(
self._tree_store.get_path(
self._iters[album.name][album.artist]['iter']))
def find_first_visible(self, filter_key, filter_arg, start=None,
backwards=False):
backwards=False):
album_filter = AlbumFilters.keys[filter_key](filter_arg)
albums = reversed(self._albums) if backwards else self._albums
@@ -993,24 +992,24 @@ class AlbumsModel(GObject.Object):
'''
Changes the sorting strategy for the model.
'''
gs = GSetting()
source_settings = gs.get_setting(gs.Path.PLUGIN)
key = source_settings[gs.PluginKey.SORT_BY]
order = source_settings[gs.PluginKey.SORT_ORDER]
print ("current")
print (self._sortkey)
print ("registry")
print (key)
print (order)
print("current")
print(self._sortkey)
print("registry")
print(key)
print(order)
if key == self._sortkey['type']:
key = None
else:
self._sortkey['type'] = key
if order != self._sortkey['order']:
reverse = True
self._sortkey['order'] = order
@@ -1020,17 +1019,17 @@ class AlbumsModel(GObject.Object):
def key_function(album):
keys = [getattr(album, prop) for prop in props]
return keys
if not key and not reverse:
print ("nothing to sort")
print("nothing to sort")
return
print (key)
print (reverse)
print(key)
print(reverse)
if key:
props = sort_keys[key]
self._albums.key = key_function
if reverse:
self._albums = reversed(self._albums)
@@ -1089,15 +1088,15 @@ class AlbumsModel(GObject.Object):
def do_filter_changed(self):
pos = 0
for show_result in list(map(self._album_filter, self._albums)):
self.show( self._albums[pos], show_result )
self.show(self._albums[pos], show_result)
pos = pos + 1
def _album_filter(self, album):
for f in list(self._filters.values()):
if not f(album):
return False
for f in list(self._filters.values()):
if not f(album):
return False
return True
return True
def recreate_text(self):
'''
@@ -1105,6 +1104,7 @@ class AlbumsModel(GObject.Object):
'''
self._recreate_text(iter(self._albums))
class AlbumLoader(GObject.Object):
'''
Loads and updates Rhythmbox's tracks and albums, updating the model
@@ -1116,7 +1116,7 @@ class AlbumLoader(GObject.Object):
__gsignals__ = {
'albums-load-finished': (GObject.SIGNAL_RUN_LAST, None, (object,)),
'model-load-finished': (GObject.SIGNAL_RUN_LAST, None, ())
}
}
def __init__(self, album_manager):
super(AlbumLoader, self).__init__()
@@ -1129,11 +1129,11 @@ class AlbumLoader(GObject.Object):
def _connect_signals(self):
# connect signals for updating the albums
self.entry_changed_id = self._album_manager.db.connect('entry-changed',
self._entry_changed_callback)
self._entry_changed_callback)
self.entry_added_id = self._album_manager.db.connect('entry-added',
self._entry_added_callback)
self._entry_added_callback)
self.entry_deleted_id = self._album_manager.db.connect('entry-deleted',
self._entry_deleted_callback)
self._entry_deleted_callback)
@idle_iterator
def _load_albums(self):
@@ -1143,7 +1143,7 @@ class AlbumLoader(GObject.Object):
# allocate the track
track = Track(entry, self._album_manager.db)
self._tracks[track.location] = track
album_name = track.album
album_artist = track.album_artist
album_artist = album_artist if album_artist else track.artist
@@ -1155,11 +1155,11 @@ class AlbumLoader(GObject.Object):
album = data['albums'][album_name][album_artist]
else:
album = Album(album_name, album_artist,
self._album_manager.cover_man.unknown_cover)
self._album_manager.cover_man.unknown_cover)
data['albums'][album_name][album_artist] = album
album.add_track(track)
def after(data):
# update the progress
data['progress'] += ALBUM_LOAD_CHUNK
@@ -1209,36 +1209,36 @@ class AlbumLoader(GObject.Object):
# this now works correctly
def analyse_change(change):
print (change.prop)
print(change.prop)
if change.prop is RB.RhythmDBPropType.ALBUM \
or change.prop is RB.RhythmDBPropType.ALBUM_ARTIST \
or change.prop is RB.RhythmDBPropType.ARTIST \
or change.prop is RhythmDBPropType.ALBUM_SORTNAME \
or change.prop is RhythmDBPropType.ALBUM_ARTIST_SORTNAME:
or change.prop is RB.RhythmDBPropType.ALBUM_ARTIST \
or change.prop is RB.RhythmDBPropType.ARTIST \
or change.prop is RhythmDBPropType.ALBUM_SORTNAME \
or change.prop is RhythmDBPropType.ALBUM_ARTIST_SORTNAME:
# called when the album of a entry is modified
track.emit('deleted')
track.emit('modified')
print ("change prop album or artist")
print("change prop album or artist")
self._allocate_track(track)
elif change.prop is RB.RhythmDBPropType.HIDDEN:
# called when an entry gets hidden (e.g.:the sound file is
# removed.
if changes.new:
print ("change prop new")
print("change prop new")
track.emit('deleted')
else:
print ("change prop dunno")
print("change prop dunno")
self._allocate_track(track)
# look at all the changes and update the albums accordingly
try:
track = self._tracks[Track(entry).location]
if rb3compat.is_rb3():
#RB3 has a simple rhythmdbentrychange array to deal with so we
#just need to loop each element of the array
#RB3 has a simple rhythmdbentrychange array to deal with so we
#just need to loop each element of the array
for change in changes:
analyse_change(change)
else:
@@ -1247,15 +1247,15 @@ class AlbumLoader(GObject.Object):
while changes.n_values != 0:
change = changes.values
analyse_change(change)
# removes the last change from the GValueArray
changes.remove(0)
except:
# we have a problem houston ... RB2.98 and 2.99 cant cope
# lets just assume something has just changed
track = self._tracks[Track(entry).location]
print ("except")
print("except")
track.emit('modified')
print("CoverArtBrowser DEBUG - end entry_changed_callback")
@@ -1289,15 +1289,15 @@ class AlbumLoader(GObject.Object):
album_artist = album_artist if album_artist else track.artist
if self._album_manager.model.contains(album_name, album_artist):
print ("allocate track - contains")
print("allocate track - contains")
album = self._album_manager.model.get(album_name, album_artist)
print (album)
print(album)
album.add_track(track)
else:
print ("allocate track - does not contain")
print("allocate track - does not contain")
album = Album(album_name, album_artist,
self._album_manager.cover_man.unknown_cover)
print (album)
self._album_manager.cover_man.unknown_cover)
print(album)
album.add_track(track)
self._album_manager.cover_man.load_cover(album)
self._album_manager.model.add(album)
@@ -1310,7 +1310,7 @@ class AlbumLoader(GObject.Object):
print("CoverArtBrowser DEBUG - load_albums")
self._load_albums(iter(query_model), albums={}, model=query_model,
total=len(query_model), progress=0.)
total=len(query_model), progress=0.)
print("CoverArtBrowser DEBUG - load_albums finished")
@@ -1324,7 +1324,6 @@ class AlbumLoader(GObject.Object):
class CoverRequester(GObject.Object):
def __init__(self, cover_db):
super(CoverRequester, self).__init__()
@@ -1383,7 +1382,7 @@ class CoverRequester(GObject.Object):
# add a timeout to the request
Gdk.threads_add_timeout_seconds(GLib.PRIORITY_DEFAULT_IDLE, 40,
self._next, self._queue_id)
self._next, self._queue_id)
else:
# if there're no more elements, clean the state of the requester
self._running = False
@@ -1419,8 +1418,8 @@ class CoverRequester(GObject.Object):
def stop(self):
''' Clears the queue, forcing the requester to stop. '''
del self._queue[:]
class CoverManager(GObject.Object):
'''
Manager that takes care of cover loading and updating.
@@ -1433,7 +1432,7 @@ class CoverManager(GObject.Object):
# signals
__gsignals__ = {
'load-finished': (GObject.SIGNAL_RUN_LAST, None, ())
}
}
# properties
has_finished_loading = False
@@ -1446,12 +1445,12 @@ class CoverManager(GObject.Object):
self._manager = manager
self._requester = CoverRequester(self.cover_db)
self.unknown_cover = None #to be defined by inherited class
self.album_manager = None #to be defined by inherited class
self.unknown_cover = None #to be defined by inherited class
self.album_manager = None #to be defined by inherited class
# connect the signal to update cover arts when added
self.req_id = self.cover_db.connect('added',
self.coverart_added_callback)
self.coverart_added_callback)
self.connect('load-finished', self._on_load_finished)
def _on_load_finished(self, *args):
@@ -1505,7 +1504,7 @@ class CoverManager(GObject.Object):
# create a key and look for the art location
key = coverobject.create_ext_db_key()
art_location = self.cover_db.lookup(key)
# try to create a cover
if art_location:
coverobject.cover = self.create_cover(art_location)
@@ -1536,16 +1535,16 @@ class CoverManager(GObject.Object):
if not check_lastfm(self.force_lastfm_check):
# display error message and quit
dialog = Gtk.MessageDialog(None,
Gtk.DialogFlags.MODAL,
Gtk.MessageType.INFO,
Gtk.ButtonsType.OK,
_("Enable LastFM plugin and log in first"))
Gtk.DialogFlags.MODAL,
Gtk.MessageType.INFO,
Gtk.ButtonsType.OK,
_("Enable LastFM plugin and log in first"))
dialog.run()
dialog.destroy()
return
if coverobjects is None:
self._requester.replace_queue(
list(self._manager.model.get_all()), callback)
@@ -1557,10 +1556,10 @@ class CoverManager(GObject.Object):
Cancel the current cover request, if there is one running.
'''
self._requester.stop()
def update_pixbuf_cover(self, coverobject, pixbuf):
pass
def update_cover(self, coverobject, pixbuf=None, uri=None):
'''
Updates the cover database, inserting the pixbuf as the cover art for
@@ -1599,7 +1598,7 @@ class CoverManager(GObject.Object):
self.update_cover(coverobject, cover)
except:
print("The URI doesn't point to an image or " + \
"the image couldn't be opened.")
"the image couldn't be opened.")
async = rb.Loader()
async.get_url(uri, cover_update, coverobject)
@@ -1609,11 +1608,11 @@ class AlbumCoverManager(CoverManager):
# properties
add_shadow = GObject.property(type=bool, default=False)
shadow_image = GObject.property(type=str, default="above")
def __init__(self, plugin, album_manager):
self.cover_db = RB.ExtDB(name='album-art')
super(AlbumCoverManager, self).__init__(plugin, album_manager)
self.album_manager = album_manager
self._connect_properties()
self._connect_signals(plugin)
@@ -1625,29 +1624,29 @@ class AlbumCoverManager(CoverManager):
self.connect('notify::cover-size', self._on_cover_size_changed)
self.connect('notify::add-shadow', self._on_add_shadow_changed, plugin)
self.connect('notify::shadow-image', self._on_add_shadow_changed,
plugin)
plugin)
def _connect_properties(self):
gs = GSetting()
setting = gs.get_setting(gs.Path.PLUGIN)
setting.bind(gs.PluginKey.COVER_SIZE, self, 'cover_size',
Gio.SettingsBindFlags.GET)
Gio.SettingsBindFlags.GET)
setting.bind(gs.PluginKey.ADD_SHADOW, self, 'add_shadow',
Gio.SettingsBindFlags.GET)
Gio.SettingsBindFlags.GET)
setting.bind(gs.PluginKey.SHADOW_IMAGE, self, 'shadow_image',
Gio.SettingsBindFlags.GET)
Gio.SettingsBindFlags.GET)
def create_unknown_cover(self, plugin):
# create the unknown cover
self._shadow = Shadow(self.cover_size,
rb.find_plugin_file(plugin, 'img/album-shadow-%s.png' %
self.shadow_image))
rb.find_plugin_file(plugin, 'img/album-shadow-%s.png' %
self.shadow_image))
self.unknown_cover = self.create_cover(
rb.find_plugin_file(plugin, 'img/rhythmbox-missing-artwork.svg'))
super(AlbumCoverManager,self).create_unknown_cover(plugin)
super(AlbumCoverManager, self).create_unknown_cover(plugin)
def create_cover(self, image):
if self.add_shadow:
cover = ShadowedCover(self._shadow, image)
@@ -1655,14 +1654,14 @@ class AlbumCoverManager(CoverManager):
cover = Cover(self.cover_size, image)
return cover
def _on_add_shadow_changed(self, obj, prop, plugin):
# update the unknown_cover
self.create_unknown_cover(plugin)
# recreate all the covers
self.load_covers()
def _on_cover_size_changed(self, *args):
'''
Updates the showing albums cover size.
@@ -1680,22 +1679,22 @@ class AlbumCoverManager(CoverManager):
def update_item_width(self):
self.album_manager.current_view.resize_icon(self.cover_size)
def update_pixbuf_cover(self, coverobject, pixbuf):
# if it's a pixbuf, assign it to all the artist for the album
key = RB.ExtDBKey.create_storage('album', coverobject.name)
key.add_field('artist', coverobject.artist)
self.cover_db.store(key, RB.ExtDBSourceType.USER_EXPLICIT,
pixbuf)
for artist in coverobject.artists.split(', '):
key = RB.ExtDBKey.create_storage('album', coverobject.name)
key.add_field('artist', coverobject.artist)
key.add_field('artist', artist)
self.cover_db.store(key, RB.ExtDBSourceType.USER_EXPLICIT,
pixbuf)
pixbuf)
for artist in coverobject.artists.split(', '):
key = RB.ExtDBKey.create_storage('album', coverobject.name)
key.add_field('artist', artist)
self.cover_db.store(key, RB.ExtDBSourceType.USER_EXPLICIT,
pixbuf)
@idle_iterator
def _resize_covers(self):
def process(coverobject, data):
@@ -1728,7 +1727,7 @@ class TextManager(GObject.Object):
display_text_ellipsize_enabled = GObject.property(type=bool, default=False)
display_text_ellipsize_length = GObject.property(type=int, default=0)
display_font_size = GObject.property(type=int, default=0)
def __init__(self, album_manager):
super(TextManager, self).__init__()
@@ -1745,16 +1744,16 @@ class TextManager(GObject.Object):
'''
# connect signals for the loader properties
self.connect('notify::display-text-ellipsize-enabled',
self._on_notify_display_text_ellipsize)
self._on_notify_display_text_ellipsize)
self.connect('notify::display-text-ellipsize-length',
self._on_notify_display_text_ellipsize)
self._on_notify_display_text_ellipsize)
self.connect('notify::display-font-size',
self._on_notify_display_text_ellipsize)
self._on_notify_display_text_ellipsize)
self._album_manager.model.connect('generate-tooltip',
self._generate_tooltip)
self._generate_tooltip)
self._album_manager.model.connect('generate-markup',
self._generate_markup_text)
self._generate_markup_text)
def _connect_properties(self):
'''
@@ -1764,27 +1763,27 @@ class TextManager(GObject.Object):
setting = gs.get_setting(gs.Path.PLUGIN)
setting.bind(gs.PluginKey.DISPLAY_TEXT_ELLIPSIZE, self,
'display_text_ellipsize_enabled', Gio.SettingsBindFlags.GET)
'display_text_ellipsize_enabled', Gio.SettingsBindFlags.GET)
setting.bind(gs.PluginKey.DISPLAY_TEXT_ELLIPSIZE_LENGTH, self,
'display_text_ellipsize_length',
Gio.SettingsBindFlags.GET)
'display_text_ellipsize_length',
Gio.SettingsBindFlags.GET)
setting.bind(gs.PluginKey.DISPLAY_FONT_SIZE, self, 'display_font_size',
Gio.SettingsBindFlags.GET)
Gio.SettingsBindFlags.GET)
def _on_notify_display_text_ellipsize(self, *args):
'''
Callback called when one of the properties related with the ellipsize
option is changed.
'''
self._album_manager.model.recreate_text()
def _generate_tooltip(self, model, album):
'''
Utility function that creates the tooltip for this album to set into
the model.
'''
return cgi.escape(rb3compat.unicodeencode(_('%s by %s'), 'utf-8') % (album.name,
album.artists))
album.artists))
def _generate_markup_text(self, model, album):
'''
@@ -1815,6 +1814,7 @@ class TextManager(GObject.Object):
markup = "<span font='%d'><b>%s</b>\n<i>%s</i></span>"
return markup % (self.display_font_size, name, artist)
class AlbumManager(GObject.Object):
'''
Main construction that glues together the different managers, the loader
@@ -1828,12 +1828,12 @@ class AlbumManager(GObject.Object):
# properties
progress = GObject.property(type=float, default=0)
# signals
__gsignals__ = {
'sort': (GObject.SIGNAL_RUN_LAST, None, (object,))
}
}
def __init__(self, plugin, current_view):
super(AlbumManager, self).__init__()
@@ -1847,6 +1847,7 @@ class AlbumManager(GObject.Object):
self.loader = AlbumLoader(self)
self.cover_man = AlbumCoverManager(plugin, self)
from coverart_artistview import ArtistManager
self.artist_man = ArtistManager(plugin, self, plugin.shell)
self.text_man = TextManager(self)
self._show_policy = current_view.show_policy.initialise(self)
@@ -1860,11 +1861,10 @@ class AlbumManager(GObject.Object):
'''
# connect signal to the loader so it shows the albums when it finishes
self._load_finished_id = self.loader.connect('model-load-finished',
self._load_finished_callback)
self._load_finished_callback)
self.connect('sort', self._sort_album)
def _sort_album(self, widget, param):
toolbar_type = param
if not toolbar_type or toolbar_type == "album":
+339 -335
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+262 -255
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+57 -54
Ver Arquivo
@@ -41,10 +41,12 @@ from coverart_toolbar import TopToolbar
import coverart_rb3compat as rb3compat
class CoverArtBrowserEntryType(RB.RhythmDBEntryType):
'''
Entry type for our source.
'''
def __init__(self):
'''
Initializes the entry type.
@@ -90,44 +92,44 @@ class CoverArtBrowserPlugin(GObject.Object, Peas.Activatable):
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
entry_type.category = RB.RhythmDBEntryCategory.NORMAL
group = RB.DisplayPageGroup.get_by_id('library')
# load plugin icon
theme = Gtk.IconTheme.get_default()
rb.append_plugin_source_path(theme, '/icons')
# lets assume that python3 versions of RB only has the new icon attribute in the source
if rb3compat.PYVER >=3:
iconfile = Gio.File.new_for_path(
rb.find_plugin_file(self, 'img/covermgr_rb3.png'))
self.source = CoverArtBrowserSource(
shell=self.shell,
name=_("CoverArt"),
entry_type=entry_type,
plugin=self,
icon=Gio.FileIcon.new(iconfile),
query_model=self.shell.props.library_source.props.base_query_model)
else:
what, width, height = Gtk.icon_size_lookup(Gtk.IconSize.LARGE_TOOLBAR)
pxbf = GdkPixbuf.Pixbuf.new_from_file_at_size(
rb.find_plugin_file(self, 'img/covermgr.png'), width, height)
if rb3compat.PYVER >= 3:
iconfile = Gio.File.new_for_path(
rb.find_plugin_file(self, 'img/covermgr_rb3.png'))
self.source = CoverArtBrowserSource(
shell=self.shell,
name=_("CoverArt"),
entry_type=entry_type,
plugin=self,
icon=Gio.FileIcon.new(iconfile),
query_model=self.shell.props.library_source.props.base_query_model)
else:
what, width, height = Gtk.icon_size_lookup(Gtk.IconSize.LARGE_TOOLBAR)
pxbf = GdkPixbuf.Pixbuf.new_from_file_at_size(
rb.find_plugin_file(self, 'img/covermgr.png'), width, height)
self.source = CoverArtBrowserSource(
shell=self.shell,
name=_("CoverArt"), entry_type=entry_type,
plugin=self, pixbuf=pxbf,
query_model=self.shell.props.library_source.props.base_query_model)
self.source = CoverArtBrowserSource(
shell=self.shell,
name=_("CoverArt"), entry_type=entry_type,
plugin=self, pixbuf=pxbf,
query_model=self.shell.props.library_source.props.base_query_model)
self.shell.register_entry_type_for_source(self.source, entry_type)
self.shell.append_display_page(self.source, group)
self.source.props.query_model.connect('complete', self.load_complete)
if rb3compat.PYVER >=3:
if rb3compat.PYVER >= 3:
self._externalmenu = ExternalPluginMenu(self)
else:
self._externalmenu = None
cl.switch_locale(cl.Locale.RB)
print("CoverArtBrowser DEBUG - end do_activate")
@@ -145,7 +147,7 @@ class CoverArtBrowserPlugin(GObject.Object, Peas.Activatable):
del self.source
print("CoverArtBrowser DEBUG - end do_deactivate")
def load_complete(self, *args, **kwargs):
'''
Called by Rhythmbox when it has completed loading all data
@@ -157,64 +159,65 @@ class CoverArtBrowserPlugin(GObject.Object, Peas.Activatable):
if setting[gs.PluginKey.AUTOSTART]:
GLib.idle_add(self.shell.props.display_page_tree.select,
self.source)
self.source)
def _translation_helper(self):
'''
a method just to help out with translation strings
it is not meant to be called by itself
'''
# define .plugin text strings used for translation
plugin = _('CoverArt Browser')
desc = _('Browse and play your albums through their covers')
#. TRANSLATORS: This is the icon-grid view that the user sees
tile = _('Tiles')
#. TRANSLATORS: This is the cover-flow view the user sees - they can swipe album covers from side-to-side
artist = _('Flow')
#. TRANSLATORS: percentage size that the image will be expanded
scale = _('Scale by %:')
class ExternalPluginMenu(GObject.Object):
class ExternalPluginMenu(GObject.Object):
toolbar_pos = GObject.property(type=str, default=TopToolbar.name)
def __init__(self, plugin):
super(ExternalPluginMenu, self).__init__()
self.plugin = plugin
self.shell = plugin.shell
self.source = plugin.source
self.app_id = None
from coverart_browser_source import Views
self._views = Views(self.shell)
self._connect_properties()
self._connect_signals()
self._create_menu()
def _connect_signals(self):
self.connect('notify::toolbar-pos', self._on_notify_toolbar_pos)
self.shell.props.display_page_tree.connect(
"selected", self.on_page_change
)
)
def _connect_properties(self):
gs = GSetting()
setting = gs.get_setting(gs.Path.PLUGIN)
setting.bind(gs.PluginKey.TOOLBAR_POS, self, 'toolbar_pos',
Gio.SettingsBindFlags.GET)
Gio.SettingsBindFlags.GET)
def _on_notify_toolbar_pos(self, *args):
if self.toolbar_pos == TopToolbar.name:
self._create_menu()
else:
self.cleanup()
def cleanup(self):
if self.app_id:
app = Gio.Application.get_default()
@@ -225,46 +228,46 @@ class ExternalPluginMenu(GObject.Object):
def _create_menu(self):
app = Gio.Application.get_default()
self.app_id = 'coverart-browser'
self.locations = ['library-toolbar', 'queue-toolbar']
action_name = 'coverart-browser-views'
self.action = Gio.SimpleAction.new_stateful(
action_name, GLib.VariantType.new('s'),
self._views.get_action_name(ListView.name)
)
)
self.action.connect("activate", self.view_change_cb)
app.add_action(self.action)
menu_item = Gio.MenuItem()
section = Gio.Menu()
menu = Gio.Menu()
toolbar_item = Gio.MenuItem()
for view_name in self._views.get_view_names():
menu_item.set_label(self._views.get_menu_name(view_name))
menu_item.set_action_and_target_value(
'app.' + action_name, self._views.get_action_name(view_name)
)
)
section.append_item(menu_item)
menu.append_section(None, section)
cl = CoverLocale()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
toolbar_item.set_label(_('Views'))
cl.switch_locale(cl.Locale.RB)
toolbar_item.set_submenu(menu)
for location in self.locations:
app.add_plugin_menu_item(location, self.app_id, toolbar_item)
def on_page_change(self, display_page_tree, page):
'''
Called when the display page changes. Grabs query models and sets the
active view.
'''
if page == self.shell.props.library_source:
self.action.set_state(self._views.get_action_name(ListView.name))
elif page == self.shell.props.queue_source:
@@ -282,11 +285,11 @@ class ExternalPluginMenu(GObject.Object):
setting = gs.get_setting(gs.Path.PLUGIN)
setting[gs.PluginKey.VIEW_NAME] = view_name
GLib.idle_add(self.shell.props.display_page_tree.select,
self.source)
self.source)
elif view_name == ListView.name:
GLib.idle_add(self.shell.props.display_page_tree.select,
self.shell.props.library_source)
self.shell.props.library_source)
elif view_name == QueueView.name:
GLib.idle_add(self.shell.props.display_page_tree.select,
self.shell.props.queue_source)
self.shell.props.queue_source)
+85 -84
Ver Arquivo
@@ -33,6 +33,7 @@ from stars import ReactiveStar
from stars import StarSize
import coverart_rb3compat as rb3compat
def webkit_support():
'''
function that returns True/False if webkit technology is supported
@@ -41,6 +42,7 @@ def webkit_support():
settings = gs.get_setting(gs.Path.PLUGIN)
return settings[gs.PluginKey.WEBKIT]
class CoverLocale:
'''
This class manages the locale
@@ -238,7 +240,7 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
GObject.Object.__init__(self)
gs = GSetting()
self.settings = gs.get_setting(gs.Path.PLUGIN)
self._first_run = True
def do_create_configure_widget(self):
@@ -246,39 +248,39 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
Creates the plugin's preferences dialog
'''
return self._create_display_contents(self)
def display_preferences_dialog(self, plugin):
if self._first_run:
self._first_run = False
cl = CoverLocale()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
self._dialog = Gtk.Dialog(modal=True, destroy_with_parent=True)
self._dialog.add_button(Gtk.STOCK_OK, Gtk.ResponseType.OK)
self._dialog.set_title(_('Browser Preferences'))
self._dialog.set_title(_('Browser Preferences'))
content_area = self._dialog.get_content_area()
content_area.pack_start(self._create_display_contents(plugin), True, True, 0)
helpbutton = self._dialog.add_button(Gtk.STOCK_HELP, Gtk.ResponseType.HELP)
helpbutton.connect('clicked', self._display_help)
self._dialog.show_all()
while True:
response = self._dialog.run()
if response != Gtk.ResponseType.HELP:
break
self._dialog.hide()
def _display_help(self, *args):
peas = Peas.Engine.get_default()
uri = peas.get_plugin_info('coverart_browser').get_help_uri()
webbrowser.open(uri)
def _create_display_contents(self, plugin):
# create the ui
cl = CoverLocale()
@@ -286,10 +288,10 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
builder = Gtk.Builder()
builder.set_translation_domain(cl.Locale.LOCALE_DOMAIN)
builder.add_from_file(rb.find_plugin_file(plugin,
'ui/coverart_browser_prefs.ui'))
'ui/coverart_browser_prefs.ui'))
self.launchpad_button = builder.get_object('show_launchpad')
self.launchpad_label = builder.get_object('launchpad_label')
builder.connect_signals(self)
#. TRANSLATORS: Do not translate this string.
@@ -299,25 +301,25 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
self.launchpad_label.set_text(translators)
else:
self.launchpad_button.set_visible(False)
gs = GSetting()
# bind the toggles to the settings
toggle_statusbar = builder.get_object('custom_statusbar_checkbox')
self.settings.bind(gs.PluginKey.CUSTOM_STATUSBAR,
toggle_statusbar, 'active', Gio.SettingsBindFlags.DEFAULT)
toggle_statusbar, 'active', Gio.SettingsBindFlags.DEFAULT)
toggle_bottom = builder.get_object('display_bottom_checkbox')
self.settings.bind(gs.PluginKey.DISPLAY_BOTTOM, toggle_bottom,
'active', Gio.SettingsBindFlags.DEFAULT)
'active', Gio.SettingsBindFlags.DEFAULT)
toggle_text = builder.get_object('display_text_checkbox')
self.settings.bind(gs.PluginKey.DISPLAY_TEXT, toggle_text, 'active',
Gio.SettingsBindFlags.DEFAULT)
Gio.SettingsBindFlags.DEFAULT)
box_text = builder.get_object('display_text_box')
self.settings.bind(gs.PluginKey.DISPLAY_TEXT, box_text, 'sensitive',
Gio.SettingsBindFlags.GET)
Gio.SettingsBindFlags.GET)
self.display_text_pos = self.settings[gs.PluginKey.DISPLAY_TEXT_POS]
self.display_text_under_radiobutton = builder.get_object('display_text_under_radiobutton')
self.display_text_within_radiobutton = builder.get_object('display_text_within_radiobutton')
@@ -327,40 +329,39 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
else:
self.display_text_within_radiobutton.set_active(True)
random_scale = builder.get_object('random_adjustment')
self.settings.bind(gs.PluginKey.RANDOM, random_scale, 'value',
Gio.SettingsBindFlags.DEFAULT)
Gio.SettingsBindFlags.DEFAULT)
toggle_text_ellipsize = builder.get_object(
'display_text_ellipsize_checkbox')
self.settings.bind(gs.PluginKey.DISPLAY_TEXT_ELLIPSIZE,
toggle_text_ellipsize, 'active', Gio.SettingsBindFlags.DEFAULT)
toggle_text_ellipsize, 'active', Gio.SettingsBindFlags.DEFAULT)
box_text_ellipsize_length = builder.get_object(
'display_text_ellipsize_length_box')
self.settings.bind(gs.PluginKey.DISPLAY_TEXT_ELLIPSIZE,
box_text_ellipsize_length, 'sensitive', Gio.SettingsBindFlags.GET)
box_text_ellipsize_length, 'sensitive', Gio.SettingsBindFlags.GET)
spinner_text_ellipsize_length = builder.get_object(
'display_text_ellipsize_length_spin')
self.settings.bind(gs.PluginKey.DISPLAY_TEXT_ELLIPSIZE_LENGTH,
spinner_text_ellipsize_length, 'value',
Gio.SettingsBindFlags.DEFAULT)
spinner_text_ellipsize_length, 'value',
Gio.SettingsBindFlags.DEFAULT)
spinner_font_size = builder.get_object(
'display_font_spin')
self.settings.bind(gs.PluginKey.DISPLAY_FONT_SIZE,
spinner_font_size, 'value',
Gio.SettingsBindFlags.DEFAULT)
spinner_font_size, 'value',
Gio.SettingsBindFlags.DEFAULT)
cover_size_scale = builder.get_object('cover_size_adjustment')
self.settings.bind(gs.PluginKey.COVER_SIZE, cover_size_scale, 'value',
Gio.SettingsBindFlags.DEFAULT)
Gio.SettingsBindFlags.DEFAULT)
add_shadow = builder.get_object('add_shadow_checkbox')
self.settings.bind(gs.PluginKey.ADD_SHADOW, add_shadow, 'active',
Gio.SettingsBindFlags.DEFAULT)
Gio.SettingsBindFlags.DEFAULT)
rated_box = builder.get_object('rated_box')
self.stars = ReactiveStar(size=StarSize.BIG)
@@ -375,53 +376,53 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
autostart = builder.get_object('autostart_checkbox')
self.settings.bind(gs.PluginKey.AUTOSTART,
autostart, 'active', Gio.SettingsBindFlags.DEFAULT)
autostart, 'active', Gio.SettingsBindFlags.DEFAULT)
toolbar_pos_combo = builder.get_object('show_in_combobox')
renderer = Gtk.CellRendererText()
toolbar_pos_combo.pack_start(renderer, True)
toolbar_pos_combo.add_attribute(renderer, 'text', 1)
self.settings.bind(gs.PluginKey.TOOLBAR_POS, toolbar_pos_combo,
'active-id', Gio.SettingsBindFlags.DEFAULT)
'active-id', Gio.SettingsBindFlags.DEFAULT)
light_source_combo = builder.get_object('light_source_combobox')
renderer = Gtk.CellRendererText()
light_source_combo.pack_start(renderer, True)
light_source_combo.add_attribute(renderer, 'text', 1)
self.settings.bind(gs.PluginKey.SHADOW_IMAGE, light_source_combo,
'active-id', Gio.SettingsBindFlags.DEFAULT)
'active-id', Gio.SettingsBindFlags.DEFAULT)
combo_liststore = builder.get_object('combo_liststore')
from coverart_utils import Theme
for theme in Theme(self).themes:
combo_liststore.append([theme, theme])
theme_combo = builder.get_object('theme_combobox')
renderer = Gtk.CellRendererText()
theme_combo.pack_start(renderer, True)
theme_combo.add_attribute(renderer, 'text', 1)
self.settings.bind(gs.PluginKey.THEME, theme_combo,
'active-id', Gio.SettingsBindFlags.DEFAULT)
'active-id', Gio.SettingsBindFlags.DEFAULT)
button_relief = builder.get_object('button_relief_checkbox')
self.settings.bind(gs.PluginKey.BUTTON_RELIEF, button_relief, 'active',
Gio.SettingsBindFlags.DEFAULT)
Gio.SettingsBindFlags.DEFAULT)
# create user data files
cachedir = RB.user_cache_dir() + "/coverart_browser/usericons"
if not os.path.exists(cachedir):
os.makedirs(cachedir)
popup = cachedir + "/popups.xml"
temp = RB.find_user_data_file('plugins/coverart_browser/img/usericons/popups.xml')
# lets see if there is a legacy file - if necessary copy it to the cache dir
if os.path.isfile(temp) and not os.path.isfile(popup):
shutil.copyfile(temp, popup)
if not os.path.isfile(popup):
template = rb.find_plugin_file(plugin, 'template/popups.xml')
folder = os.path.split(popup)[0]
@@ -441,13 +442,13 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
self._iters = {}
for key in list(self._sheet.keys()):
store_iter = self.alt_liststore.append([key, self._sheet[key]])
self._iters[(key,self.GENRE_POPUP)] = store_iter
self._iters[(key, self.GENRE_POPUP)] = store_iter
for key, value in self._sheet.genre_alternate.items():
if key.genre_type == GenreConfiguredSpriteSheet.GENRE_USER:
store_iter = self.alt_user_liststore.append([key.name,
self._sheet[self._sheet.genre_alternate[key]],
self._sheet.genre_alternate[key]])
self._sheet[self._sheet.genre_alternate[key]],
self._sheet.genre_alternate[key]])
self._iters[(key.name, self.GENRE_LIST)] = store_iter
self.amend_mode = False
@@ -463,15 +464,15 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
padding_scale = builder.get_object('padding_adjustment')
self.settings.bind(gs.PluginKey.ICON_PADDING, padding_scale, 'value',
Gio.SettingsBindFlags.DEFAULT)
Gio.SettingsBindFlags.DEFAULT)
spacing_scale = builder.get_object('spacing_adjustment')
self.settings.bind(gs.PluginKey.ICON_SPACING, spacing_scale, 'value',
Gio.SettingsBindFlags.DEFAULT)
Gio.SettingsBindFlags.DEFAULT)
icon_automatic = builder.get_object('icon_automatic_checkbox')
self.settings.bind(gs.PluginKey.ICON_AUTOMATIC,
icon_automatic, 'active', Gio.SettingsBindFlags.DEFAULT)
icon_automatic, 'active', Gio.SettingsBindFlags.DEFAULT)
#flow tab
flow_combo = builder.get_object('flow_combobox')
@@ -479,27 +480,27 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
flow_combo.pack_start(renderer, True)
flow_combo.add_attribute(renderer, 'text', 1)
self.settings.bind(gs.PluginKey.FLOW_APPEARANCE, flow_combo,
'active-id', Gio.SettingsBindFlags.DEFAULT)
'active-id', Gio.SettingsBindFlags.DEFAULT)
flow_hide = builder.get_object('hide_caption_checkbox')
self.settings.bind(gs.PluginKey.FLOW_HIDE_CAPTION,
flow_hide, 'active', Gio.SettingsBindFlags.DEFAULT)
flow_hide, 'active', Gio.SettingsBindFlags.DEFAULT)
flow_scale = builder.get_object('cover_scale_adjustment')
self.settings.bind(gs.PluginKey.FLOW_SCALE, flow_scale, 'value',
Gio.SettingsBindFlags.DEFAULT)
Gio.SettingsBindFlags.DEFAULT)
flow_width = builder.get_object('cover_width_adjustment')
self.settings.bind(gs.PluginKey.FLOW_WIDTH, flow_width, 'value',
Gio.SettingsBindFlags.DEFAULT)
Gio.SettingsBindFlags.DEFAULT)
flow_max = builder.get_object('flow_max_adjustment')
self.settings.bind(gs.PluginKey.FLOW_MAX, flow_max, 'value',
Gio.SettingsBindFlags.DEFAULT)
Gio.SettingsBindFlags.DEFAULT)
flow_automatic = builder.get_object('automatic_checkbox')
self.settings.bind(gs.PluginKey.FLOW_AUTOMATIC,
flow_automatic, 'active', Gio.SettingsBindFlags.DEFAULT)
flow_automatic, 'active', Gio.SettingsBindFlags.DEFAULT)
self.background_colour = self.settings[gs.PluginKey.FLOW_BACKGROUND_COLOUR]
self.white_radiobutton = builder.get_object('white_radiobutton')
@@ -509,13 +510,13 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
self.white_radiobutton.set_active(True)
else:
self.black_radiobutton.set_active(True)
# return the dialog
return builder.get_object('main_notebook')
def on_flow_combobox_changed(self, combobox):
current_val = combobox.get_model()[combobox.get_active()][0]
gs=GSetting()
gs = GSetting()
if self.settings[gs.PluginKey.FLOW_APPEARANCE] != current_val:
if current_val == 'flow-vert':
default_size = 150
@@ -523,10 +524,10 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
default_size = 600
self.settings[gs.PluginKey.FLOW_WIDTH] = default_size
if current_val == 'carousel':
self.settings[gs.PluginKey.FLOW_HIDE_CAPTION] = True
def on_background_radio_toggled(self, button):
if button.get_active():
gs = GSetting()
@@ -534,7 +535,7 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
self.settings[gs.PluginKey.FLOW_BACKGROUND_COLOUR] = 'W'
else:
self.settings[gs.PluginKey.FLOW_BACKGROUND_COLOUR] = 'B'
def on_display_text_pos_radio_toggled(self, button):
if button.get_active():
gs = GSetting()
@@ -543,7 +544,7 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
else:
self.settings[gs.PluginKey.DISPLAY_TEXT_POS] = False
self.settings[gs.PluginKey.ADD_SHADOW] = False
def on_add_shadow_checkbox_toggled(self, button):
if button.get_active():
#gs = GSetting()
@@ -559,32 +560,32 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
'''
action when genre edit area is saved
'''
entry_value = self.genre_entry.get_text()
entry_value = self.genre_entry.get_text()
treeiter = self.genre_combobox.get_active_iter()
icon_value = self.alt_liststore[treeiter][0]
# model 0 is the icon name, model 1 is the pixbuf
if self.amend_mode:
key = self._sheet.amend_genre_info(self.current_genre,
entry_value, icon_value)
entry_value, icon_value)
self.alt_user_liststore[self._iters[(self.current_genre,
self.GENRE_LIST)]][1]=self._sheet[self._sheet.genre_alternate[key]]
self.GENRE_LIST)]][1] = self._sheet[self._sheet.genre_alternate[key]]
self.alt_user_liststore[self._iters[(self.current_genre,
self.GENRE_LIST)]][0]=key.name
self.GENRE_LIST)]][0] = key.name
store_iter = self._iters[(self.current_genre, self.GENRE_LIST)]
del self._iters[(self.current_genre, self.GENRE_LIST)]
self._iters[(key.name, self.GENRE_LIST)] = store_iter
else:
self.amend_mode = True
key = self._sheet.amend_genre_info('',
entry_value, icon_value)
entry_value, icon_value)
self.current_genre = key.name
store_iter = self.alt_user_liststore.append([key.name,
self._sheet[self._sheet.genre_alternate[key]],
self._sheet.genre_alternate[key]])
self._sheet[self._sheet.genre_alternate[key]],
self._sheet.genre_alternate[key]])
self._iters[(key.name, self.GENRE_LIST)] = store_iter
selection = self.genre_view.get_selection()
selection.select_iter(store_iter)
@@ -592,23 +593,23 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
self.save_button.set_sensitive(False)
self._toggle_new_genre_state()
def on_genre_filechooserbutton_file_set(self, filechooser):
'''
action when genre new icon button is pressed
'''
key = self._sheet.add_genre_icon( self.filechooserdialog.get_filename() )
key = self._sheet.add_genre_icon(self.filechooserdialog.get_filename())
store_iter = self.alt_liststore.append([key.name, self._sheet[key.name]])
self._iters[(key.name,self.GENRE_POPUP)] = store_iter
self._iters[(key.name, self.GENRE_POPUP)] = store_iter
gs = GSetting()
last_genre_folder = self.filechooserdialog.get_current_folder()
print (last_genre_folder)
print (self.filechooserdialog.get_filename())
print(last_genre_folder)
print(self.filechooserdialog.get_filename())
if last_genre_folder:
self.settings[gs.PluginKey.LAST_GENRE_FOLDER] = last_genre_folder
def on_genre_view_selection_changed(self, view):
'''
action when user selects a row in the list of genres
@@ -620,7 +621,7 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
if index != '':
self.genre_combobox.set_active_iter(self._iters[(index, self.GENRE_POPUP)])
self.amend_mode = True
self.current_genre=rb3compat.unicodestr(model[genre_iter][0], 'utf-8')
self.current_genre = rb3compat.unicodestr(model[genre_iter][0], 'utf-8')
else:
self.genre_entry.set_text('')
self.genre_combobox.set_active_iter(None)
@@ -634,7 +635,7 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
self.blank_iter = None
except:
self.blank_iter = None
def on_add_button_clicked(self, button):
'''
action when a new genre is added to the table
@@ -645,8 +646,8 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
self.blank_iter = self.alt_user_liststore.append(['', None, ''])
selection = self.genre_view.get_selection()
selection.select_iter(self.blank_iter)
def on_delete_button_clicked(self, button):
'''
action when a genre is to be deleted
@@ -655,7 +656,7 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
model, genre_iter = selection.get_selected()
if genre_iter:
index = rb3compat.unicodestr(model[genre_iter][0],'utf-8')
index = rb3compat.unicodestr(model[genre_iter][0], 'utf-8')
model.remove(genre_iter)
if index:
@@ -663,7 +664,7 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
self._sheet.delete_genre(index)
self._toggle_new_genre_state()
def set_save_sensitivity(self, _):
'''
action to toggle the state of the save button depending
@@ -703,8 +704,8 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
else:
test = True
self.settings[gs.PluginKey.NEW_GENRE_ICON]=test
self.settings[gs.PluginKey.NEW_GENRE_ICON] = test
def on_show_launchpad_toggled(self, button):
self.launchpad_label.set_visible(button.get_active())
+187 -178
Ver Arquivo
@@ -61,7 +61,7 @@ class CoverArtBrowserSource(RB.Source):
rating_threshold = GObject.property(type=float, default=0)
artist_paned_pos = GObject.property(type=str)
min_paned_pos = 40
# unique instance of the source
instance = None
@@ -83,7 +83,7 @@ class CoverArtBrowserSource(RB.Source):
self.favourites = False
self.follow_song = False
self.task_progress = None
def _connect_properties(self):
'''
Connects the source properties to the saved preferences.
@@ -107,14 +107,14 @@ class CoverArtBrowserSource(RB.Source):
show the selected album info.
Also, it makes sure to show the progress on the album loading
'''
try:
# this will only work for RB3.0 and later
if not self.task_progress:
self.task_progress = RB.TaskProgressSimple.new()
except:
pass
try:
progress = self.album_manager.progress
progress_text = _('Loading...') if progress < 1 else ''
@@ -123,9 +123,9 @@ class CoverArtBrowserSource(RB.Source):
if progress < 1:
if self.props.shell.props.task_list.get_model().n_items() == 0:
self.props.shell.props.task_list.add_task(self.task_progress)
self.task_progress.props.task_progress = progress
self.task_progress.props.task_label=progress_text
self.task_progress.props.task_label = progress_text
else:
self.task_progress.props.task_outcome = RB.TaskOutcome.COMPLETE
except:
@@ -173,7 +173,7 @@ class CoverArtBrowserSource(RB.Source):
self.actiongroup = ActionGroup(self.shell, 'coverplaylist_submenu')
self._browser_preferences = None
self._search_preferences = None
# indicate that the source was activated before
self.hasActivated = True
@@ -198,7 +198,7 @@ class CoverArtBrowserSource(RB.Source):
ui = Gtk.Builder()
ui.set_translation_domain(cl.Locale.LOCALE_DOMAIN)
ui.add_from_file(rb.find_plugin_file(self.plugin,
'ui/coverart_browser.ui'))
'ui/coverart_browser.ui'))
ui.connect_signals(self)
# load the page and put it in the source
@@ -208,9 +208,9 @@ class CoverArtBrowserSource(RB.Source):
# get widgets for main icon-view
self.status_label = ui.get_object('status_label')
window = ui.get_object('scrolled_window')
self.viewmgr = ViewManager(self, window)
# get widgets for the artist paned
self.artist_paned = ui.get_object('vertical_paned')
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 50, self._change_artist_paned_pos, self.viewmgr.view_name)
@@ -221,22 +221,22 @@ class CoverArtBrowserSource(RB.Source):
# define menu's
self.popup_menu = Menu(self.plugin, self.shell)
self.popup_menu.load_from_file('ui/coverart_browser_pop_rb2.ui',
'ui/coverart_browser_pop_rb3.ui')
'ui/coverart_browser_pop_rb3.ui')
self._external_plugins = None
signals = \
{ 'play_album_menu_item' : self.play_album_menu_item_callback,
'queue_album_menu_item' : self.queue_album_menu_item_callback,
'add_to_playing_menu_item' : self.add_to_playing_menu_item_callback,
'new_playlist' : self.add_playlist_menu_item_callback,
'cover_search_menu_item' : self.cover_search_menu_item_callback,
'export_embed_menu_item' : self.export_embed_menu_item_callback,
'show_properties_menu_item' : self.show_properties_menu_item_callback,
'play_similar_artist_menu_item' : self.play_similar_artist_menu_item_callback}
{'play_album_menu_item': self.play_album_menu_item_callback,
'queue_album_menu_item': self.queue_album_menu_item_callback,
'add_to_playing_menu_item': self.add_to_playing_menu_item_callback,
'new_playlist': self.add_playlist_menu_item_callback,
'cover_search_menu_item': self.cover_search_menu_item_callback,
'export_embed_menu_item': self.export_embed_menu_item_callback,
'show_properties_menu_item': self.show_properties_menu_item_callback,
'play_similar_artist_menu_item': self.play_similar_artist_menu_item_callback}
self.popup_menu.connect_signals(signals)
self.popup_menu.connect('pre-popup', self.pre_popup_menu_callback)
self.status_label = ui.get_object('status_label')
self.request_status_box = ui.get_object('request_status_box')
self.request_spinner = ui.get_object('request_spinner')
@@ -248,24 +248,24 @@ class CoverArtBrowserSource(RB.Source):
#setup Track Pane
setting = self.gs.get_setting(self.gs.Path.PLUGIN)
setting.bind(self.gs.PluginKey.PANED_POSITION,
self.paned, 'collapsible-y', Gio.SettingsBindFlags.DEFAULT)
self.paned, 'collapsible-y', Gio.SettingsBindFlags.DEFAULT)
setting.bind(self.gs.PluginKey.DISPLAY_BOTTOM,
self.paned.get_child2(), 'visible', Gio.SettingsBindFlags.DEFAULT)
self.paned.get_child2(), 'visible', Gio.SettingsBindFlags.DEFAULT)
self.entryviewpane = EntryViewPane(self.shell,
self.plugin,
self,
self.entry_view_grid,
self.viewmgr)
#---- set up info pane -----#
info_stack = ui.get_object('info_stack')
info_button_box = ui.get_object('info_button_box')
artist_info_paned = ui.get_object('vertical_info_paned')
self.artist_info = ArtistInfoPane(info_button_box,
info_stack,
artist_info_paned,
self)
info_stack,
artist_info_paned,
self)
# quick search
self.quick_search = ui.get_object('quick_search_entry')
@@ -278,7 +278,7 @@ class CoverArtBrowserSource(RB.Source):
by the user. It also creates and configure some custom widgets.
'''
print("CoverArtBrowser DEBUG - _setup_source")
cl = CoverLocale()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
@@ -287,7 +287,7 @@ class CoverArtBrowserSource(RB.Source):
# create an album manager
self.album_manager = AlbumManager(self.plugin, self.viewmgr.current_view)
self.viewmgr.current_view.initialise(self)
# setup cover search pane
self.entryviewpane.setup_source()
@@ -306,24 +306,24 @@ class CoverArtBrowserSource(RB.Source):
self.quick_search_controller.connect_quick_search(self.quick_search)
# set sensitivity of export menu item for iconview
self.popup_menu.set_sensitive('export_embed_menu_item',
CoverArtExport(self.plugin,
self.shell, self.album_manager).is_search_plugin_enabled())
self.popup_menu.set_sensitive('export_embed_menu_item',
CoverArtExport(self.plugin,
self.shell, self.album_manager).is_search_plugin_enabled())
# setup the statusbar component
self.statusbar = Statusbar(self)
# initialise the toolbar manager
self.toolbar_manager = ToolbarManager(self.plugin, self.page,
self.viewmgr)
self.viewmgr)
self.viewmgr.current_view.emit('update-toolbar')
cl.switch_locale(cl.Locale.RB)
# setup the artist paned
artist_pview = None
for view in self.shell.props.library_source.get_property_views():
print (view.props.title)
print (_("Artist"))
print(view.props.title)
print(_("Artist"))
if view.props.title == _("Artist"):
artist_pview = view
break
@@ -333,11 +333,11 @@ class CoverArtBrowserSource(RB.Source):
self.artist_treeview.set_model(artist_pview.get_model())
setting = self.gs.get_setting(self.gs.Path.PLUGIN)
setting.bind(self.gs.PluginKey.ARTIST_PANED_POSITION,
self, 'artist-paned-pos', Gio.SettingsBindFlags.DEFAULT)
self, 'artist-paned-pos', Gio.SettingsBindFlags.DEFAULT)
self.artist_paned.connect('button-release-event',
self.artist_paned_button_release_callback)
self.artist_paned.connect('button-release-event',
self.artist_paned_button_release_callback)
# intercept JumpToPlaying Song action so that we can scroll to the playing album
appshell = rb3compat.ApplicationShell(self.shell)
action = appshell.lookup_action("", "jump-to-playing", "win")
@@ -347,9 +347,9 @@ class CoverArtBrowserSource(RB.Source):
# should be automatically selected
self.shell.props.shell_player.connect('playing-song-changed', self.playing_song_callback)
self.echonest_similar_playlist = None
print("CoverArtBrowser DEBUG - end _setup_source")
def playing_song_callback(self, *args):
@@ -363,7 +363,7 @@ class CoverArtBrowserSource(RB.Source):
Callback when the popup menu is about to be displayed
'''
state,sensitive = self.shell.props.shell_player.get_playing()
state, sensitive = self.shell.props.shell_player.get_playing()
if not state:
sensitive = False
@@ -373,12 +373,12 @@ class CoverArtBrowserSource(RB.Source):
if not self._external_plugins:
# initialise external plugin menu support
self._external_plugins = \
CreateExternalPluginMenu("ca_covers_view",
8, self.popup_menu)
CreateExternalPluginMenu("ca_covers_view",
8, self.popup_menu)
self._external_plugins.create_menu('popup_menu', True)
self.playlist_menu_item_callback()
def jump_to_playing(self, *args):
'''
Callback when the JumpToPlaying action is invoked
@@ -389,14 +389,14 @@ class CoverArtBrowserSource(RB.Source):
# if the source page that was played from is not the plugin then
# nothing to do
return
album = None
entry = self.shell.props.shell_player.get_playing_entry()
if entry:
album = self.album_manager.model.get_from_dbentry(entry)
self.viewmgr.current_view.scroll_to_album(album)
def artist_paned_button_release_callback(self, *args):
@@ -404,45 +404,45 @@ class CoverArtBrowserSource(RB.Source):
Callback when the artist paned handle is released from its mouse click.
'''
child_width = self._get_child_width()
paned_positions = eval(self.artist_paned_pos)
found = None
for viewpos in paned_positions:
if self.viewmgr.view_name in viewpos:
found = viewpos
break
if not found:
return
paned_positions.remove(found)
if child_width <= self.min_paned_pos:
child_width = 0
self.artist_paned.set_position(child_width)
paned_positions.append(self.viewmgr.view_name + ":" + str(child_width))
self.artist_paned_pos = repr(paned_positions)
def on_view_changed(self, widget, view_name):
self._change_artist_paned_pos(view_name)
def _change_artist_paned_pos(self, view_name):
paned_positions = eval(self.artist_paned_pos)
print (paned_positions)
print(paned_positions)
found = None
for viewpos in paned_positions:
if view_name in viewpos:
found = viewpos
break
print (found)
print(found)
if not found:
return
child_width = int(found.split(":")[1])
print (child_width)
print(child_width)
# odd case - if the pane is not visible but the position is zero
# then the paned position on visible=true is some large arbitary value
# hence - set it to be 1 px larger than the real value, then set it back
@@ -450,11 +450,11 @@ class CoverArtBrowserSource(RB.Source):
self.artist_paned.set_position(child_width + 1)
self.artist_paned.set_visible(True)
self.artist_paned.set_position(child_width)
def _get_child_width(self):
child = self.artist_paned.get_child1()
return child.get_allocated_width()
def on_artist_treeview_selection_changed(self, view):
model, artist_iter = view.get_selected()
if artist_iter:
@@ -469,7 +469,7 @@ class CoverArtBrowserSource(RB.Source):
self.album_manager.model.replace_filter('quick_artist', artist)
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
def _apply_settings(self):
'''
Applies all the settings related to the source and connects those that
@@ -480,8 +480,8 @@ class CoverArtBrowserSource(RB.Source):
# connect some signals to the loader to keep the source informed
self.album_mod_id = self.album_manager.model.connect('album-updated',
self.on_album_updated)
self.on_album_updated)
self.notify_prog_id = self.album_manager.connect(
'notify::progress', lambda *args: self.notify_status_changed())
@@ -495,8 +495,8 @@ class CoverArtBrowserSource(RB.Source):
print("CoverArtBrowser DEBUG - load_finished_callback")
#if not self.request_status_box.get_visible():
# it should only be enabled if no cover request is going on
#self.source_menu_search_all_item.set_sensitive(True)
# it should only be enabled if no cover request is going on
#self.source_menu_search_all_item.set_sensitive(True)
# enable sorting on the entryview
self.entry_view.set_columns_clickable(True)
@@ -529,19 +529,20 @@ class CoverArtBrowserSource(RB.Source):
Callback called when the play similar artist option is selected from
the cover view popup. It plays similar artists music.
'''
def play_similar_artist_menu_item_callback(self, *args):
if not self.echonest_similar_playlist:
self.echonest_similar_playlist = \
EchoNestPlaylist( self.shell,
self.shell.props.queue_source)
EchoNestPlaylist(self.shell,
self.shell.props.queue_source)
selected_albums = self.viewmgr.current_view.get_selected_objects()
album = selected_albums[0]
tracks = album.get_tracks()
entry = tracks[0].entry
self.echonest_similar_playlist.start(entry, reinitialise=True)
def show_properties_menu_item_callback(self, *args):
'''
Callback called when the show album properties option is selected from
@@ -594,7 +595,7 @@ class CoverArtBrowserSource(RB.Source):
selected_albums = self.viewmgr.current_view.get_selected_objects()
threshold = self.rating_threshold if favourites else 0
total = 0
total = 0
for album in selected_albums:
# Retrieve and sort the entries of the album
tracks = album.get_tracks(threshold)
@@ -605,10 +606,11 @@ class CoverArtBrowserSource(RB.Source):
if total == 0 and threshold:
dialog = Gtk.MessageDialog(None,
Gtk.DialogFlags.MODAL,
Gtk.MessageType.INFO,
Gtk.ButtonsType.OK,
_("No tracks have been added because no tracks meet the favourite rating threshold"))
Gtk.DialogFlags.MODAL,
Gtk.MessageType.INFO,
Gtk.ButtonsType.OK,
_(
"No tracks have been added because no tracks meet the favourite rating threshold"))
dialog.run()
dialog.destroy()
@@ -652,7 +654,7 @@ class CoverArtBrowserSource(RB.Source):
self.favourites)
def playlist_fillmenu(self, popup_menu, menubar, section_name,
actiongroup, func, favourite=False):
actiongroup, func, favourite=False):
print("CoverArtBrowser DEBUG - playlist_fillmenu")
playlist_manager = self.shell.props.playlist_manager
@@ -665,32 +667,32 @@ class CoverArtBrowserSource(RB.Source):
if playlists_entries:
for playlist in playlists_entries:
if playlist.props.is_local and \
isinstance(playlist, RB.StaticPlaylistSource):
isinstance(playlist, RB.StaticPlaylistSource):
args = (playlist, favourite)
args=(playlist, favourite)
# take the name of the playlist, strip out non-english characters and reduce the string
# to just a-to-z characters i.e. this will make the action_name valid in RB3
ascii_name = unicodedata.normalize('NFKD', \
rb3compat.unicodestr(playlist.props.name, 'utf-8')).encode('ascii','ignore')
rb3compat.unicodestr(playlist.props.name, 'utf-8')).encode(
'ascii', 'ignore')
ascii_name = ascii_name.decode(encoding='UTF-8')
ascii_name = re.sub(r'[^a-zA-Z]', '', ascii_name)
action = actiongroup.add_action(func=func,
action_name=ascii_name,
playlist=playlist,favourite=favourite,
label=playlist.props.name)
popup_menu.add_menu_item( menubar, section_name,
action )
action_name=ascii_name,
playlist=playlist, favourite=favourite,
label=playlist.props.name)
popup_menu.add_menu_item(menubar, section_name,
action)
def add_to_static_playlist_menu_item_callback(self, action, param, args):
print('''CoverArtBrowser DEBUG -
add_to_static_playlist_menu_item_callback''')
playlist=args['playlist']
playlist = args['playlist']
favourite = args['favourite']
self.queue_selected_album(playlist, favourite)
def add_playlist_menu_item_callback(self, *args):
@@ -710,27 +712,27 @@ class CoverArtBrowserSource(RB.Source):
selected_albums = []
gs = GSetting()
settings = gs.get_setting(gs.Path.PLUGIN)
settings = gs.get_setting(gs.Path.PLUGIN)
to_queue = settings[gs.PluginKey.RANDOM]
if num_albums <= to_queue:
dialog = Gtk.MessageDialog(None,
Gtk.DialogFlags.MODAL,
Gtk.MessageType.INFO,
Gtk.ButtonsType.OK,
_("The number of albums to randomly play is less than that displayed."))
Gtk.DialogFlags.MODAL,
Gtk.MessageType.INFO,
Gtk.ButtonsType.OK,
_("The number of albums to randomly play is less than that displayed."))
dialog.run()
dialog.destroy()
return
album_col = self.album_manager.model.columns['album']
chosen = {}
# now loop through finding unique random albums
# i.e. ensure we dont queue the same album twice
for loop in range(0, to_queue):
while True:
pos = random.randint(0, num_albums - 1)
@@ -752,14 +754,15 @@ class CoverArtBrowserSource(RB.Source):
if total == 0 and threshold:
dialog = Gtk.MessageDialog(None,
Gtk.DialogFlags.MODAL,
Gtk.MessageType.INFO,
Gtk.ButtonsType.OK,
_("No tracks have been added because no tracks meet the favourite rating threshold"))
Gtk.DialogFlags.MODAL,
Gtk.MessageType.INFO,
Gtk.ButtonsType.OK,
_(
"No tracks have been added because no tracks meet the favourite rating threshold"))
dialog.run()
dialog.destroy()
self.props.query_model = query_model
# Start the music
@@ -781,7 +784,7 @@ class CoverArtBrowserSource(RB.Source):
self.request_status_box.show_all()
self.album_manager.cover_man.search_covers(selected_albums,
self.update_request_status_bar)
self.update_request_status_bar)
print("CoverArtBrowser DEBUG - end cover_search_menu_item_callback()")
@@ -795,7 +798,7 @@ class CoverArtBrowserSource(RB.Source):
selected_albums = self.viewmgr.current_view.get_selected_objects()
CoverArtExport(self.plugin,
self.shell, self.album_manager).embed_albums(selected_albums)
self.shell, self.album_manager).embed_albums(selected_albums)
print("CoverArtBrowser DEBUG - export_embed_menu_item_callback()")
@@ -837,15 +840,15 @@ class CoverArtBrowserSource(RB.Source):
then if necessary scroll the view to the last selected album
params is "album" or a tuple of "album" and "force_expand" boolean
'''
if isinstance(params, tuple):
album, force = params
else:
album = params
force = PanedCollapsible.Paned.DEFAULT
if (album and self.click_count == 1 \
and self.last_selected_album is album) or force != PanedCollapsible.Paned.DEFAULT:
and self.last_selected_album is album) or force != PanedCollapsible.Paned.DEFAULT:
# check if it's a second or third click on the album and expand
# or collapse the entry view accordingly
self.paned.expand(force)
@@ -866,7 +869,7 @@ class CoverArtBrowserSource(RB.Source):
self.statusbar.emit('display-status', self.viewmgr.current_view)
def propertiesbutton_callback(self, choice):
if choice == 'download':
self.request_status_box.show_all()
self._cover_search_manager = self.viewmgr.current_view.get_default_manager()
@@ -884,12 +887,13 @@ class CoverArtBrowserSource(RB.Source):
elif choice == 'browser prefs':
if not self._browser_preferences:
self._browser_preferences = Preferences()
self._browser_preferences.display_preferences_dialog(self.plugin)
elif choice == 'search prefs':
try:
if not self._search_preferences:
if not self._search_preferences:
from gi.repository import Peas
peas = Peas.Engine.get_default()
plugin_info = peas.get_plugin_info('coverart_search_providers')
module_name = plugin_info.get_module_name()
@@ -901,15 +905,16 @@ class CoverArtBrowserSource(RB.Source):
self._search_preferences.display_preferences_dialog(self._search_preferences)
except:
dialog = Gtk.MessageDialog(None,
Gtk.DialogFlags.MODAL,
Gtk.MessageType.INFO,
Gtk.ButtonsType.OK,
_("Please install and activate the latest version of the Coverart Search Providers plugin"))
Gtk.DialogFlags.MODAL,
Gtk.MessageType.INFO,
Gtk.ButtonsType.OK,
_(
"Please install and activate the latest version of the Coverart Search Providers plugin"))
dialog.run()
dialog.destroy()
else:
assert 1==2, ("unknown choice %s", choice)
assert 1 == 2, ("unknown choice %s", choice)
@classmethod
def get_instance(cls, **kwargs):
@@ -921,12 +926,13 @@ class CoverArtBrowserSource(RB.Source):
return cls.instance
class Statusbar(GObject.Object):
# signals
__gsignals__ = {
'display-status': (GObject.SIGNAL_RUN_LAST, None, (object,))
}
custom_statusbar_enabled = GObject.property(type=bool, default=False)
def __init__(self, source):
@@ -946,11 +952,11 @@ class Statusbar(GObject.Object):
settings = gs.get_setting(gs.Path.PLUGIN)
settings.bind(gs.PluginKey.CUSTOM_STATUSBAR, self,
'custom_statusbar_enabled', Gio.SettingsBindFlags.GET)
'custom_statusbar_enabled', Gio.SettingsBindFlags.GET)
def _connect_signals(self, source):
self.connect('notify::custom-statusbar-enabled',
self._custom_statusbar_enabled_changed)
self._custom_statusbar_enabled_changed)
self.connect('display-status', self._update)
def _custom_statusbar_enabled_changed(self, *args):
@@ -980,30 +986,31 @@ class Statusbar(GObject.Object):
# 'interesting stuff' about the album
if len(albums) == 1:
#. TRANSLATORS - for example "abba's greatest hits by ABBA"
self.status = rb3compat.unicodedecode(_('%s by %s') %
(album.name, album.artist), 'UTF-8')
self.status = rb3compat.unicodedecode(_('%s by %s') %
(album.name, album.artist), 'UTF-8')
else:
#. TRANSLATORS - the number of albums that have been selected/highlighted
self.status = rb3compat.unicodedecode(_('%d selected albums') %
(len(albums)), 'UTF-8')
self.status = rb3compat.unicodedecode(_('%d selected albums') %
(len(albums)), 'UTF-8')
if track_count == 1:
self.status += rb3compat.unicodedecode(_(' with 1 track'), 'UTF-8')
else:
self.status += rb3compat.unicodedecode(_(' with %d tracks') %
track_count, 'UTF-8')
self.status += rb3compat.unicodedecode(_(' with %d tracks') %
track_count, 'UTF-8')
if duration == 1:
self.status += rb3compat.unicodedecode(_(' and a duration of 1 minute'), 'UTF-8')
else:
self.status += rb3compat.unicodedecode(_(' and a duration of %d minutes') %
duration, 'UTF-8')
self.status += rb3compat.unicodedecode(_(' and a duration of %d minutes') %
duration, 'UTF-8')
def _update(self, widget, current_view):
albums = current_view.get_selected_objects()
self._generate_status(albums)
self.current_statusbar.update(self.status)
class SourceStatusBar(object):
def __init__(self, source):
self._source = source
@@ -1031,7 +1038,8 @@ class CustomStatusBar(object):
def update(self, status):
self._label.set_text(status)
class Views:
'''
This class describes the different views available
@@ -1061,40 +1069,40 @@ class Views:
queue_name = shell.props.queue_source.props.name
self._values = OrderedDict()
cl = CoverLocale()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
self._values[CoverIconView.name] = [_('Tiles'),
GLib.Variant.new_string('coverart-browser-tile')]
cl = CoverLocale()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
self._values[CoverIconView.name] = [_('Tiles'),
GLib.Variant.new_string('coverart-browser-tile')]
if webkit_support():
self._values[CoverFlowView.name] = [_('Flow'),
GLib.Variant.new_string('coverart-browser-coverflow')]
self._values[ArtistView.name] = [_('Artist'),
GLib.Variant.new_string('coverart-browser-artist')]
self._values[ListView.name] = [library_name,
GLib.Variant.new_string('coverart-browser-list')]
self._values[CoverFlowView.name] = [_('Flow'),
GLib.Variant.new_string('coverart-browser-coverflow')]
self._values[ArtistView.name] = [_('Artist'),
GLib.Variant.new_string('coverart-browser-artist')]
self._values[ListView.name] = [library_name,
GLib.Variant.new_string('coverart-browser-list')]
self._values[QueueView.name] = [queue_name,
GLib.Variant.new_string('coverart-browser-queue')]
cl.switch_locale(cl.Locale.RB)
print (self._values)
GLib.Variant.new_string('coverart-browser-queue')]
cl.switch_locale(cl.Locale.RB)
print(self._values)
def get_view_names(self):
return list(self._values.keys())
def get_view_name_for_action(self, action_name):
for view_name in self.get_view_names():
if self.get_action_name(view_name) == action_name:
return view_name
return None
def get_menu_name(self, view_name):
return self._values[view_name][0]
def get_action_name(self, view_name):
return self._values[view_name][1]
def __init__(self, plugin):
""" Create singleton instance """
# Check whether we already have an instance
@@ -1118,33 +1126,33 @@ class ViewManager(GObject.Object):
# signals
__gsignals__ = {
'new-view': (GObject.SIGNAL_RUN_LAST, None, (str,))
}
}
# properties
view_name = GObject.property(type=str, default=CoverIconView.name)
def __init__(self, source, window):
super(ViewManager, self).__init__()
self.source = source
self.window = window
# initialize views
self._views = {}
ui = Gtk.Builder()
ui.add_from_file(rb.find_plugin_file(source.plugin,
'ui/coverart_iconview.ui'))
'ui/coverart_iconview.ui'))
self._views[CoverIconView.name] = ui.get_object('covers_view')
self._views[CoverFlowView.name] = CoverFlowView()
self._views[ListView.name] = ListView()
self._views[QueueView.name] = QueueView()
ui.add_from_file(rb.find_plugin_file(source.plugin,
'ui/coverart_artistview.ui'))
'ui/coverart_artistview.ui'))
self._views[ArtistView.name] = ui.get_object('artist_view')
self._lastview = None
self.controller = ViewController(source.shell, self)
# connect signal and properties
self._connect_signals()
self._connect_properties()
@@ -1152,22 +1160,22 @@ class ViewManager(GObject.Object):
if self.current_view.use_plugin_window:
window.add(self.current_view.view)
window.show_all()
@property
def current_view(self):
return self._views[self.view_name]
def get_view(self, view_name):
return self._views[view_name]
def _connect_signals(self):
self.connect('notify::view-name', self.on_notify_view_name)
def _connect_properties(self):
gs = GSetting()
setting = gs.get_setting(gs.Path.PLUGIN)
setting.bind(gs.PluginKey.VIEW_NAME, self, 'view_name',
Gio.SettingsBindFlags.DEFAULT)
Gio.SettingsBindFlags.DEFAULT)
def on_notify_view_name(self, *args):
if self._lastview and self.view_name != self._lastview:
@@ -1175,42 +1183,42 @@ class ViewManager(GObject.Object):
current_album = None
if len(selected) > 0:
current_album = self._views[self._lastview].get_selected_objects()[0]
if self._views[self.view_name].use_plugin_window:
child = self.window.get_child()
if child:
self.window.remove(child)
self.window.add(self._views[self.view_name].view)
self.window.show_all()
self.click_count = 0
self._views[self._lastview].panedposition = self.source.paned.get_expansion_status()
self._views[self.view_name].switch_to_view(self.source, current_album)
self._views[self.view_name].emit('update-toolbar')
self._views[self.view_name].get_default_manager().emit('sort', None)
if self._views[self.view_name].use_plugin_window:
self.source.paned.expand(self._views[self.view_name].panedposition)
self.current_view.set_popup_menu(self.source.popup_menu)
self.source.album_manager.current_view = self.current_view
if self._views[self.view_name].use_plugin_window:
# we only ever save plugin views not external views
saved_view = self.view_name
else:
saved_view = self._lastview
self._lastview = self.view_name
gs = GSetting()
setting = gs.get_setting(gs.Path.PLUGIN)
setting[gs.PluginKey.VIEW_NAME] = saved_view
self.emit('new-view', self.view_name)
def get_view_icon_name(self, view_name):
return self._views[view_name].get_view_icon_name()
@@ -1226,5 +1234,6 @@ class ViewManager(GObject.Object):
colour = '#0000FF'
return colour
GObject.type_register(CoverArtBrowserSource)
+106 -109
Ver Arquivo
@@ -37,14 +37,15 @@ import rb
import coverart_rb3compat as rb3compat
from collections import namedtuple
MenuNodeT = namedtuple('MenuNode', 'label menutype typevalue')
def MenuNode(label, menutype=None, typevalue=None):
return MenuNodeT(label, menutype, typevalue)
class OptionsController(GObject.Object):
class OptionsController(GObject.Object):
# properties
options = GObject.property(type=object, default=None)
current_key = GObject.property(type=str, default=None)
@@ -86,10 +87,10 @@ class OptionsController(GObject.Object):
'''
if sheet:
del sheet
return ConfiguredSpriteSheet(plugin, typestr, get_stock_size())
def create_button_image( self, plugin, image, icon_name):
def create_button_image(self, plugin, image, icon_name):
'''
helper function to create a button image
'''
@@ -97,13 +98,13 @@ class OptionsController(GObject.Object):
del image
path = 'img/' + Theme(self.plugin).current + '/'
return create_pixbuf_from_file_at_size(
rb.find_plugin_file(self.plugin, path + icon_name),
*get_stock_size())
class PlaylistPopupController(OptionsController):
class PlaylistPopupController(OptionsController):
def __init__(self, plugin, album_model):
super(PlaylistPopupController, self).__init__()
@@ -123,7 +124,7 @@ class PlaylistPopupController(OptionsController):
self._spritesheet = None
self._update_options(shell)
# get the playlist model so we can monitor changes
if rb3compat.is_rb3(shell):
playlist_model = shell.props.display_page_model
@@ -137,8 +138,8 @@ class PlaylistPopupController(OptionsController):
playlist_model.connect('row-changed', self._update_options, shell)
def update_images(self, *args):
self._spritesheet = self.create_spritesheet( self.plugin,
self._spritesheet, 'playlist')
self._spritesheet = self.create_spritesheet(self.plugin,
self._spritesheet, 'playlist')
if args[-1]:
self.update_image = True
@@ -146,10 +147,10 @@ class PlaylistPopupController(OptionsController):
def _update_options(self, *args):
shell = args[-1]
self.update_images(False)
playlist_manager = shell.props.playlist_manager
still_exists = self.current_key == self._library_name or\
self.current_key == self._queue_name
still_exists = self.current_key == self._library_name or \
self.current_key == self._queue_name
# retrieve the options
values = OrderedDict()
@@ -171,7 +172,7 @@ class PlaylistPopupController(OptionsController):
self.values = values
self.options = list(values.keys())
self.current_key = self.current_key if still_exists else\
self.current_key = self.current_key if still_exists else \
self._library_name
def do_action(self):
@@ -181,7 +182,7 @@ class PlaylistPopupController(OptionsController):
self._album_model.remove_filter('model')
else:
self._album_model.replace_filter('model',
playlist.get_query_model())
playlist.get_query_model())
def get_current_image(self):
playlist = self.values[self.current_key]
@@ -215,7 +216,7 @@ class GenrePopupController(OptionsController):
# create a new property model for the genres
genres_model = RB.RhythmDBPropertyModel.new(shell.props.db,
RB.RhythmDBPropType.GENRE)
RB.RhythmDBPropType.GENRE)
query = shell.props.library_source.props.base_query_model
genres_model.props.query_model = query
@@ -229,21 +230,21 @@ class GenrePopupController(OptionsController):
self._connect_properties()
self._connect_signals(query, genres_model)
# generate initial popup
self._update_options(genres_model)
def update_images(self, *args):
if self._spritesheet:
del self._spritesheet
self._spritesheet = GenreConfiguredSpriteSheet(self.plugin,
'genre', get_stock_size())
self._default_image = self.create_button_image( self.plugin,
self._default_image, 'default_genre.png')
self._unrecognised_image = self.create_button_image( self.plugin,
self._unrecognised_image, 'unrecognised_genre.png')
'genre', get_stock_size())
self._default_image = self.create_button_image(self.plugin,
self._default_image, 'default_genre.png')
self._unrecognised_image = self.create_button_image(self.plugin,
self._unrecognised_image, 'unrecognised_genre.png')
if args[-1]:
self.update_image = True
@@ -259,13 +260,13 @@ class GenrePopupController(OptionsController):
setting = gs.get_setting(gs.Path.PLUGIN)
setting.bind(gs.PluginKey.NEW_GENRE_ICON, self, 'new_genre_icon',
Gio.SettingsBindFlags.GET)
Gio.SettingsBindFlags.GET)
def _update_options(self, *args):
genres_model = args[-1]
self.update_images(False)
still_exists = False
# retrieve the options
@@ -284,7 +285,7 @@ class GenrePopupController(OptionsController):
self.options = options
self.current_key = self.current_key if still_exists else\
self.current_key = self.current_key if still_exists else \
self._initial_genre
def do_action(self):
@@ -306,7 +307,7 @@ class GenrePopupController(OptionsController):
image = self._find_alternates(test_genre)
if image == self._unrecognised_image and \
test_genre in self._spritesheet:
test_genre in self._spritesheet:
image = self._spritesheet[test_genre]
return image
@@ -326,15 +327,15 @@ class GenrePopupController(OptionsController):
# in a mixture of cases, both unicode (normalized or not) and str
# and as usual python cannot mix and match these types.
test_genre = RB.search_fold(test_genre)
ret, sprite = self._match_genres(test_genre, self._spritesheet.GENRE_USER)
if ret:
return sprite
for genre in sorted(self._spritesheet.locale_names,
key=lambda b: (-len(b), b)):
key=lambda b: (-len(b), b)):
if RB.search_fold(genre) in test_genre:
return self._spritesheet[self._spritesheet.locale_names[genre]]
@@ -346,23 +347,23 @@ class GenrePopupController(OptionsController):
ret, sprite = self._match_genres(test_genre, self._spritesheet.GENRE_SYSTEM)
if ret:
return sprite
# check if any of the default genres are a substring
# of test_genre - check in reverse order so that we
# test largest strings first (prevents spurious matches with
# short strings)
for genre in sorted(self._spritesheet.names,
key=lambda b: (-len(b), b)):
key=lambda b: (-len(b), b)):
if RB.search_fold(genre) in test_genre:
return self._spritesheet[genre]
# if no matches then default to unrecognised image
return self._unrecognised_image
def _match_genres(self, test_genre, genre_type):
case_search = CaseInsensitiveDict(
dict((k.name, v) for k, v in self._spritesheet.genre_alternate.items()
if k.genre_type==genre_type))
if k.genre_type == genre_type))
if test_genre in case_search:
return (True, self._spritesheet[case_search[test_genre]])
@@ -378,23 +379,22 @@ class GenrePopupController(OptionsController):
class SortPopupController(OptionsController):
def __init__(self, plugin, viewmgr):
super(SortPopupController, self).__init__()
self._viewmgr=viewmgr
self._viewmgr = viewmgr
self.plugin = plugin
# sorts dictionary
cl = CoverLocale()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
self.values = OrderedDict([(_('Sort by album name'), 'name'),
(_('Sort by album artist'), 'artist'),
(_('Sort by year'), 'year'),
(_('Sort by rating'), 'rating')])
(_('Sort by album artist'), 'artist'),
(_('Sort by year'), 'year'),
(_('Sort by rating'), 'rating')])
self.options = list(self.values.keys())
# get the current sort key and initialise the superclass
gs = GSetting()
source_settings = gs.get_setting(gs.Path.PLUGIN)
@@ -402,17 +402,17 @@ class SortPopupController(OptionsController):
self._spritesheet = None
self.update_images(False)
self.current_key = list(self.values.keys())[
list(self.values.values()).index(value)]
def update_images(self, *args):
self._spritesheet = self.create_spritesheet( self.plugin,
self._spritesheet, 'sort')
self._spritesheet = self.create_spritesheet(self.plugin,
self._spritesheet, 'sort')
if args[-1]:
self.update_image = True
def do_action(self):
sort = self.values[self.current_key]
@@ -425,9 +425,9 @@ class SortPopupController(OptionsController):
def get_current_image(self):
sort = self.values[self.current_key]
return self._spritesheet[sort]
class ArtistSortPopupController(OptionsController):
class ArtistSortPopupController(OptionsController):
def __init__(self, plugin, viewmgr):
super(ArtistSortPopupController, self).__init__()
@@ -438,35 +438,35 @@ class ArtistSortPopupController(OptionsController):
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
self.values = OrderedDict([(_('Sort by album name'), 'name_artist'),
(_('Sort by year'), 'year_artist'),
(_('Sort by rating'), 'rating_artist')])
(_('Sort by year'), 'year_artist'),
(_('Sort by rating'), 'rating_artist')])
self.options = list(self.values.keys())
# get the current sort key and initialise the superclass
gs = GSetting()
source_settings = gs.get_setting(gs.Path.PLUGIN)
value = source_settings[gs.PluginKey.SORT_BY_ARTIST]
if value not in list(self.values.values()):
print ("here")
print("here")
value = 'name_artist'
source_settings[gs.PluginKey.SORT_BY_ARTIST]=value
source_settings[gs.PluginKey.SORT_BY_ARTIST] = value
self._spritesheet = None
self.update_images(False)
self.current_key = list(self.values.keys())[
list(self.values.values()).index(value)]
print (self.current_key)
print(self.current_key)
def update_images(self, *args):
self._spritesheet = self.create_spritesheet( self.plugin,
self._spritesheet, 'sort_artist')
self._spritesheet = self.create_spritesheet(self.plugin,
self._spritesheet, 'sort_artist')
if args[-1]:
self.update_image = True
def do_action(self):
sort = self.values[self.current_key]
@@ -499,18 +499,18 @@ class PropertiesMenuController(OptionsController):
self.values[MenuNode(_('Download all covers'))] = 'download'
self.values[MenuNode(_('Play random album'))] = 'random'
self.values[MenuNode(_('Follow playing song'), 'check',
(True if self.follow else False))] = 'follow'
(True if self.follow else False))] = 'follow'
self.values[MenuNode('separator1', 'separator')] = ''
self.values[MenuNode(_('Use favourites only'), 'check',
(True if self.favourites else False))] = 'favourite'
(True if self.favourites else False))] = 'favourite'
self.values[MenuNode('separator2', 'separator')] = ''
self.values[MenuNode(_('Browser Preferences'))] = 'browser prefs'
self.values[MenuNode(_('Search Preferences'))] = 'search prefs'
self.options = list(self.values.keys())
self.update_images(False)
if self.favourites:
self._source.propertiesbutton_callback('favourite')
@@ -518,7 +518,7 @@ class PropertiesMenuController(OptionsController):
self._source.propertiesbutton_callback('follow')
self.current_key = None
def _connect_properties(self):
gs = GSetting()
setting = gs.get_setting(gs.Path.PLUGIN)
@@ -532,23 +532,23 @@ class PropertiesMenuController(OptionsController):
self,
'follow',
Gio.SettingsBindFlags.DEFAULT)
def _change_key(self, dict, old, new):
for i in range(len(dict)):
k,v = dict.popitem(False)
k, v = dict.popitem(False)
dict[new if old == k else k] = v
def update_images(self, *args):
self._image = self.create_button_image( self.plugin,
None, 'properties.png')
self._image = self.create_button_image(self.plugin,
None, 'properties.png')
if args[-1]:
self.update_image = True
def do_action(self):
if self.current_key:
key = [node for node in self.values if node.label == self.current_key]
if self.current_key == _('Use favourites only'):
self.favourites = not self.favourites
@@ -564,8 +564,8 @@ class PropertiesMenuController(OptionsController):
def get_current_description(self):
return _('Properties')
class DecadePopupController(OptionsController):
class DecadePopupController(OptionsController):
def __init__(self, plugin, album_model):
super(DecadePopupController, self).__init__()
@@ -573,7 +573,7 @@ class DecadePopupController(OptionsController):
self.plugin = plugin
self._spritesheet = None
# decade options
cl = CoverLocale()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
@@ -613,13 +613,13 @@ class DecadePopupController(OptionsController):
# define a initial decade an set the initial key
self._initial_decade = self.options[0]
self.update_images(False)
self.current_key = self._initial_decade
def update_images(self, *args):
self._spritesheet = self.create_spritesheet( self.plugin,
self._spritesheet, 'decade')
self._spritesheet = self.create_spritesheet(self.plugin,
self._spritesheet, 'decade')
if args[-1]:
self.update_image = True
@@ -628,7 +628,7 @@ class DecadePopupController(OptionsController):
self._album_model.remove_filter('decade')
else:
self._album_model.replace_filter('decade',
self.values[self.current_key][0])
self.values[self.current_key][0])
def get_current_image(self):
decade = self.values[self.current_key][1]
@@ -639,22 +639,21 @@ class DecadePopupController(OptionsController):
class SortOrderToggleController(OptionsController):
toolbar_type = "album"
def __init__(self, plugin, viewmgr):
super(SortOrderToggleController, self).__init__()
self._viewmgr = viewmgr
self.plugin = plugin
# options
self.values = OrderedDict([(_('Sort in descending order'), False),
(_('Sort in ascending order'), True)])
(_('Sort in ascending order'), True)])
self.options = list(self.values.keys())
self._images = []
# set the current key
self.gs = GSetting()
self.settings = self.gs.get_setting(self.gs.Path.PLUGIN)
@@ -663,44 +662,43 @@ class SortOrderToggleController(OptionsController):
self.current_key = list(self.values.keys())[
list(self.values.values()).index(sort_order)]
self.update_images(False)
def get_key(self):
return self.gs.PluginKey.SORT_ORDER
def update_images(self, *args):
# initialize images
if len(self._images) > 0:
del self._images[:]
self._images.append(self.create_button_image( self.plugin,
None, 'arrow_down.png'))
self._images.append(self.create_button_image( self.plugin,
None, 'arrow_up.png'))
self._images.append(self.create_button_image(self.plugin,
None, 'arrow_down.png'))
self._images.append(self.create_button_image(self.plugin,
None, 'arrow_up.png'))
if args[-1]:
self.update_image = True
def do_action(self):
sort_order = self.values[self.current_key]
self.settings[self.key] = sort_order
self._viewmgr.current_view.get_default_manager().emit('sort', self.toolbar_type)
def get_current_image(self):
return self._images[self.get_current_key_index()]
class ArtistSortOrderToggleController(SortOrderToggleController):
toolbar_type = "artist"
def __init__(self, plugin, model):
super(ArtistSortOrderToggleController, self).__init__(plugin, model)
def get_key(self):
return self.gs.PluginKey.SORT_ORDER_ARTIST
class AlbumSearchEntryController(OptionsController):
# properties
search_text = GObject.property(type=str, default='')
@@ -750,7 +748,7 @@ class AlbumSearchEntryController(OptionsController):
if search_text:
self._album_model.replace_filter(self._filter_type,
search_text)
search_text)
elif not force:
self._album_model.remove_filter(self._filter_type)
@@ -783,12 +781,10 @@ class AlbumSearchEntryController(OptionsController):
self._typing = True
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 100,
self._search_typing)
self._search_typing)
class AlbumQuickSearchController(object):
def __init__(self, album_manager):
self._album_manager = album_manager
@@ -799,7 +795,7 @@ class AlbumQuickSearchController(object):
def _on_quick_search(self, quick_search, search_text, *args):
album = self._album_manager.model.find_first_visible('album_name',
search_text)
search_text)
if album:
path = self._album_manager.model.get_path(album)
@@ -824,24 +820,25 @@ class AlbumQuickSearchController(object):
def _on_hide(self, quick_search, *args):
self._album_manager.current_view.grab_focus()
class ViewController(OptionsController):
class ViewController(OptionsController):
def __init__(self, shell, viewmgr):
super(ViewController, self).__init__()
self._viewmgr = viewmgr
from coverart_browser_source import Views
views = Views(shell)
self.values = OrderedDict()
for view_name in views.get_view_names():
self.values[views.get_menu_name(view_name)] = view_name
print (view_name)
print(view_name)
self.options = list(self.values.keys())
viewmgr.connect('new-view', self.on_notify_view_name)
def on_notify_view_name(self, *args):
for key in self.options:
if self.values[key] == self._viewmgr.view_name:
+64 -61
Ver Arquivo
@@ -34,6 +34,7 @@ from os.path import expanduser
from xml.sax.saxutils import escape
from collections import namedtuple
class FlowShowingPolicy(GObject.Object):
'''
Policy that mostly takes care of how and when things should be showed on
@@ -55,6 +56,7 @@ class FlowShowingPolicy(GObject.Object):
self._album_manager = album_manager
self._model = album_manager.model
class CoverFlowView(AbstractView):
__gtype_name__ = "CoverFlowView"
@@ -69,59 +71,60 @@ class CoverFlowView(AbstractView):
flow_appearance = GObject.property(type=str, default='coverflow')
flow_max = GObject.property(type=int, default=100)
panedposition = PanedCollapsible.Paned.EXPAND
def __init__(self):
super(CoverFlowView, self).__init__()
self.show_policy = FlowShowingPolicy(self)
if webkit_support():
from gi.repository import WebKit
self.view = WebKit.WebView()
else:
self.view = None
self._last_album = None
self._has_initialised = False
self._filter_changed_inprogress = False
self._on_first_use = True
def _connect_properties(self):
gs = GSetting()
settings = gs.get_setting(gs.Path.PLUGIN)
settings.bind(gs.PluginKey.FLOW_APPEARANCE, self,
'flow_appearance', Gio.SettingsBindFlags.GET)
'flow_appearance', Gio.SettingsBindFlags.GET)
settings.bind(gs.PluginKey.FLOW_HIDE_CAPTION, self,
'flow_hide', Gio.SettingsBindFlags.GET)
'flow_hide', Gio.SettingsBindFlags.GET)
settings.bind(gs.PluginKey.FLOW_SCALE, self,
'flow_scale', Gio.SettingsBindFlags.GET)
'flow_scale', Gio.SettingsBindFlags.GET)
settings.bind(gs.PluginKey.FLOW_AUTOMATIC, self,
'flow_automatic', Gio.SettingsBindFlags.GET)
'flow_automatic', Gio.SettingsBindFlags.GET)
settings.bind(gs.PluginKey.FLOW_BACKGROUND_COLOUR, self,
'flow_background', Gio.SettingsBindFlags.GET)
'flow_background', Gio.SettingsBindFlags.GET)
settings.bind(gs.PluginKey.FLOW_WIDTH, self,
'flow_width', Gio.SettingsBindFlags.GET)
'flow_width', Gio.SettingsBindFlags.GET)
settings.bind(gs.PluginKey.FLOW_MAX, self,
'flow_max', Gio.SettingsBindFlags.GET)
'flow_max', Gio.SettingsBindFlags.GET)
def _connect_signals(self, source):
self.connect('notify::flow-background',
self.filter_changed)
self.filter_changed)
self.connect('notify::flow-scale',
self.filter_changed)
self.filter_changed)
self.connect('notify::flow-hide',
self.filter_changed)
self.filter_changed)
self.connect('notify::flow-width',
self.filter_changed)
self.filter_changed)
self.connect('notify::flow-appearance',
self.filter_changed)
self.filter_changed)
self.connect('notify::flow-max',
self.filter_changed)
self.filter_changed)
def filter_changed(self, *args):
# we can get several filter_changed calls per second
# lets simplify the processing & potential flickering when the
# call to this method has slowed stopped
self._filter_changed_event = True
if self._filter_changed_inprogress:
@@ -136,16 +139,16 @@ class CoverFlowView(AbstractView):
else:
self._filter_changed_event = False
return True
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 250, filter_events, None)
def _filter_changed(self, *args):
path = rb.find_plugin_file(self.plugin, 'coverflow/index.html')
f = open(path)
string = f.read()
f.close()
if self.flow_background == 'W':
background_colour = 'white'
if len(self.album_manager.model.store) <= self.flow_max:
@@ -161,13 +164,13 @@ class CoverFlowView(AbstractView):
string = string.replace('#BACKGROUND_COLOUR', background_colour)
string = string.replace('#FOREGROUND_COLOUR', foreground_colour)
string = string.replace('#FACTOR', str(float(self.flow_scale)/100))
string = string.replace('#FACTOR', str(float(self.flow_scale) / 100))
if self.flow_hide:
if self.flow_hide:
caption = ""
else:
caption = '<div class="globalCaption"></div>'
string = string.replace('#GLOBAL_CAPTION', caption)
addon = background_colour
@@ -187,46 +190,46 @@ class CoverFlowView(AbstractView):
identifier = "'start'"
else:
identifier = str(identifier)
string = string.replace('#START', identifier)
#TRANSLATORS: for example 'Number of covers limited to 150'
display_message = _("Number of covers limited to %d") % self.flow_max
string = string.replace('#MAXCOVERS',
'<p>' + display_message + '</p>')
'<p>' + display_message + '</p>')
items = self.flow.initialise(self.album_manager.model, self.flow_max)
string = string.replace('#ITEMS', items)
base = os.path.dirname(path) + "/"
base = os.path.dirname(path) + "/"
Gdk.threads_enter()
print (string)
print(string)
self.view.load_string(string, "text/html", "UTF-8", "file://" + base)
Gdk.threads_leave()
if self._on_first_use:
self._on_first_use = False
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 250,
self.source.show_hide_pane, (self.last_album, PanedCollapsible.Paned.EXPAND))
self.source.show_hide_pane, (self.last_album, PanedCollapsible.Paned.EXPAND))
def get_view_icon_name(self):
return "flowview.png"
def initialise(self, source):
if self._has_initialised:
return
self._has_initialised = True
super(CoverFlowView,self).initialise(source)
super(CoverFlowView, self).initialise(source)
self.album_manager = source.album_manager
self.ext_menu_pos = 6
self._connect_properties()
self._connect_signals(source)
# lets check that all covers have finished loading before
# initialising the flowcontrol and other signals
if not self.album_manager.cover_man.has_finished_loading:
@@ -243,7 +246,7 @@ class CoverFlowView(AbstractView):
self.album_manager.model.connect('album-updated', self.filter_changed)
self.album_manager.model.connect('visual-updated', self.filter_changed)
self.album_manager.model.connect('filter-changed', self.filter_changed)
self.filter_changed()
@property
@@ -270,12 +273,12 @@ class CoverFlowView(AbstractView):
# to expand the entry view
if self.flow_automatic:
self.source.click_count += 1
self.last_album = album
if self.source.click_count == 1:
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 250,
self.source.show_hide_pane, album)
self.source.show_hide_pane, album)
def item_activated_callback(self, album):
'''
@@ -291,8 +294,8 @@ class CoverFlowView(AbstractView):
Callback called when something is dropped onto the flow view - hopefully a webpath
to a picture
'''
print ("item_drop_callback %s" % webpath)
print ("dropped on album %s" % album)
print("item_drop_callback %s" % webpath)
print("dropped on album %s" % album)
self.album_manager.cover_man.update_cover(album, uri=webpath)
def get_selected_objects(self):
@@ -309,22 +312,22 @@ class CoverFlowView(AbstractView):
def switch_to_view(self, source, album):
self.initialise(source)
self.show_policy.initialise(source.album_manager)
self.last_album = album
self.scroll_to_album(self.last_album)
def grab_focus(self):
self.view.grab_focus()
def scroll_to_album(self, album):
self.flow.scroll_to_album(album, self.view)
class FlowControl(object):
def __init__(self, callback_view):
self.callback_view = callback_view
self.album_identifier = {}
def get_identifier(self, album):
index = -1
for row in self.album_identifier:
@@ -355,11 +358,11 @@ class FlowControl(object):
obj['identifier'] = str(index)
webview.execute_script("update_album('%s')" % json.dumps(obj))
def receive_message_signal(self, webview, param):
# this will be key to passing stuff back and forth - need
# to develop some-sort of message protocol to distinguish "events"
title = webview.get_title()
if (not title) or (title == '"clear"'):
return
@@ -368,7 +371,7 @@ class FlowControl(object):
try:
signal = args["signal"]
except:
print ("unhandled: %s " % title)
print("unhandled: %s " % title)
return
if signal == 'clickactive':
@@ -380,9 +383,9 @@ class FlowControl(object):
self.callback_view.item_activated_callback(self.album_identifier[int(args['param'][0])])
elif signal == 'dropactive':
self.callback_view.item_drop_callback(self.album_identifier[int(args['param'][0])],
args['param'][1])
args['param'][1])
else:
print ("unhandled signal: %s" % signal)
print("unhandled signal: %s" % signal)
def scroll_to_album(self, album, webview):
for row in self.album_identifier:
@@ -396,14 +399,14 @@ class FlowControl(object):
index = 0
items = ""
self.album_identifier = {}
def html_elements(fullfilename, title, caption, identifier):
return '<div class="item"><img class="content" src="' +\
escape(fullfilename) + '" title="' +\
escape(title) + '" identifier="' +\
identifier + '"/> <div class="caption">' +\
escape(caption) + '</div> </div>'
return '<div class="item"><img class="content" src="' + \
escape(fullfilename) + '" title="' + \
escape(title) + '" identifier="' + \
identifier + '"/> <div class="caption">' + \
escape(caption) + '</div> </div>'
for row in model.store:
@@ -415,7 +418,7 @@ class FlowControl(object):
self.album_identifier[index] = row[album_col]
items += html_elements(
fullfilename = cover,
fullfilename=cover,
caption=row[album_col].name,
title=row[album_col].artist,
identifier=str(index))
+142 -140
Ver Arquivo
@@ -32,132 +32,134 @@ from coverart_browser_prefs import GSetting
from coverart_album import AlbumsModel
from coverart_widgets import AbstractView
from coverart_widgets import PanedCollapsible
import coverart_rb3compat as rb3compat
import coverart_rb3compat as rb3compat
import rb
PLAY_SIZE_X = 30
PLAY_SIZE_Y = 30
class CellRendererThumb(Gtk.CellRendererPixbuf):
markup=GObject.property(type=str, default="")
markup = GObject.property(type=str, default="")
def __init__(self, font_description, cell_area_source):
super(CellRendererThumb, self).__init__()
self.font_description = font_description
self.cell_area_source = cell_area_source
ypad = 0
def do_render(self, cr, widget,
background_area,
cell_area,
flags):
def do_render(self, cr, widget,
background_area,
cell_area,
flags):
x_offset = cell_area.x + 1
y_offset = cell_area.y + 1
wi = 0
he = 0
#IMAGE
pixbuf = self.props.pixbuf.scale_simple(cell_area.width-2, cell_area.height-2,
GdkPixbuf.InterpType.NEAREST)
pixbuf = self.props.pixbuf.scale_simple(cell_area.width - 2, cell_area.height - 2,
GdkPixbuf.InterpType.NEAREST)
Gdk.cairo_set_source_pixbuf(cr, pixbuf, x_offset, y_offset)
cr.paint()
alpha = 0.40
if((flags & Gtk.CellRendererState.PRELIT) == Gtk.CellRendererState.PRELIT):
if ((flags & Gtk.CellRendererState.PRELIT) == Gtk.CellRendererState.PRELIT):
alpha -= 0.15
if hasattr(Gtk.IconView, "get_cell_rect") and self.cell_area_source.hover_pixbuf:
# this only works on Gtk+3.6 and later
Gdk.cairo_set_source_pixbuf(cr,
self.cell_area_source.hover_pixbuf, x_offset, y_offset)
Gdk.cairo_set_source_pixbuf(cr,
self.cell_area_source.hover_pixbuf, x_offset, y_offset)
cr.paint()
#if((flags & Gtk.CellRendererState.SELECTED) == Gtk.CellRendererState.SELECTED or \
# (flags & Gtk.CellRendererState.FOCUSED) == Gtk.CellRendererState.FOCUSED):
# alpha -= 0.15
if not(self.cell_area_source.display_text and self.cell_area_source.display_text_pos==False):
if not (self.cell_area_source.display_text and self.cell_area_source.display_text_pos == False):
return
#PANGO LAYOUT
layout_width = cell_area.width - 2
layout_width = cell_area.width - 2
pango_layout = PangoCairo.create_layout(cr)
pango_layout.set_markup(self.markup , -1)
pango_layout.set_markup(self.markup, -1)
pango_layout.set_alignment(Pango.Alignment.CENTER)
pango_layout.set_font_description(self.font_description)
pango_layout.set_width( int(layout_width * Pango.SCALE))
pango_layout.set_width(int(layout_width * Pango.SCALE))
pango_layout.set_wrap(Pango.WrapMode.WORD_CHAR)
wi,he = pango_layout.get_pixel_size()
wi, he = pango_layout.get_pixel_size()
rect_offset = y_offset + (int((2.0 * self.cell_area_source.cover_size) / 3.0))
rect_height = int(self.cell_area_source.cover_size / 3.0)
was_to_large = False;
if(he > rect_height):
if (he > rect_height):
was_to_large = True
pango_layout.set_ellipsize(Pango.EllipsizeMode.END)
pango_layout.set_height( int((self.cell_area_source.cover_size / 3.0) * Pango.SCALE))
pango_layout.set_height(int((self.cell_area_source.cover_size / 3.0) * Pango.SCALE))
wi, he = pango_layout.get_pixel_size()
#RECTANGLE
cr.set_source_rgba(0.0, 0.0, 0.0, alpha)
cr.set_line_width(0)
cr.rectangle(x_offset,
cr.rectangle(x_offset,
rect_offset,
cell_area.width - 1,
rect_height - 1)
cr.fill()
#DRAW FONT
cr.set_source_rgba(1.0, 1.0, 1.0, 1.0)
cr.move_to(x_offset,
y_offset
+ 2.0 * self.cell_area_source.cover_size / 3.0
+ (((self.cell_area_source.cover_size/3.0) - he) / 2.0)
)
y_offset
+ 2.0 * self.cell_area_source.cover_size / 3.0
+ (((self.cell_area_source.cover_size / 3.0) - he) / 2.0)
)
PangoCairo.show_layout(cr, pango_layout)
class AlbumArtCellArea(Gtk.CellAreaBox):
font_family = GObject.property(type=str, default="Sans")
font_size = GObject.property(type=int, default=10)
cover_size = GObject.property(type=int, default=0)
display_text_pos = GObject.property(type=bool, default=False)
display_text = GObject.property(type=bool, default=False)
hover_pixbuf = GObject.property(type=object, default=None)
def __init__(self, ):
super(AlbumArtCellArea, self).__init__()
self.font_description = Pango.FontDescription.new()
self.font_description.set_family(self.font_family)
self.font_description.set_size(int(self.font_size * Pango.SCALE))
self._connect_properties()
#Add own cellrenderer
renderer_thumb = CellRendererThumb(self.font_description, self)
self.pack_start(renderer_thumb, False, False, False)
self.attribute_connect(renderer_thumb, "pixbuf", AlbumsModel.columns['pixbuf'])
self.attribute_connect(renderer_thumb, "pixbuf", AlbumsModel.columns['pixbuf'])
self.attribute_connect(renderer_thumb, "markup", AlbumsModel.columns['markup'])
self.props.spacing = 2
def _connect_properties(self):
gs = GSetting()
setting = gs.get_setting(gs.Path.PLUGIN)
setting.bind(gs.PluginKey.COVER_SIZE, self, 'cover-size',
Gio.SettingsBindFlags.GET)
Gio.SettingsBindFlags.GET)
setting.bind(gs.PluginKey.DISPLAY_TEXT_POS, self, 'display-text-pos',
Gio.SettingsBindFlags.GET)
Gio.SettingsBindFlags.GET)
setting.bind(gs.PluginKey.DISPLAY_TEXT, self, 'display-text',
Gio.SettingsBindFlags.GET)
Gio.SettingsBindFlags.GET)
class AlbumShowingPolicy(GObject.Object):
'''
Policy that mostly takes care of how and when things should be showed on
@@ -182,7 +184,7 @@ class AlbumShowingPolicy(GObject.Object):
def _connect_signals(self):
self._cover_view.props.vadjustment.connect('value-changed',
self._viewport_changed)
self._viewport_changed)
self._model.connect('album-updated', self._album_updated)
self._model.connect('visual-updated', self._album_updated)
@@ -215,6 +217,7 @@ class AlbumShowingPolicy(GObject.Object):
# if our path is on the viewport, emit the signal to update it
self._cover_view.queue_draw()
class CoverIconView(EnhancedIconView, AbstractView):
__gtype_name__ = "CoverIconView"
@@ -226,11 +229,11 @@ class CoverIconView(EnhancedIconView, AbstractView):
display_text_pos = GObject.property(type=bool, default=False)
name = 'coverview'
panedposition = PanedCollapsible.Paned.COLLAPSE
__gsignals__ = {
'update-toolbar': (GObject.SIGNAL_RUN_LAST, None, ())
}
}
def __init__(self, *args, **kwargs):
if not rb3compat.compare_pygobject_version("3.9"):
@@ -238,8 +241,8 @@ class CoverIconView(EnhancedIconView, AbstractView):
else:
# this works in trusty but not in earlier versions - define in the super above
super(CoverIconView, self).__init__(*args, **kwargs)
self.props.cell_area = AlbumArtCellArea()
self.props.cell_area = AlbumArtCellArea()
self.gs = GSetting()
# custom text renderer
self._text_renderer = None
@@ -249,19 +252,19 @@ class CoverIconView(EnhancedIconView, AbstractView):
self._last_play_path = None
self._recheck_in_progress = False
self._current_hover_path = None
def initialise(self, source):
if self._has_initialised:
return
self._has_initialised = True
self.view_name = "covers_view"
super(CoverIconView,self).initialise(source)
super(CoverIconView, self).initialise(source)
self.shell = source.shell
self.album_manager = source.album_manager
# setup iconview drag&drop support
# first drag and drop on the coverart view to receive coverart
self.enable_model_drag_dest([], Gdk.DragAction.COPY)
@@ -269,7 +272,7 @@ class CoverIconView(EnhancedIconView, AbstractView):
self.drag_dest_add_text_targets()
self.connect('drag-drop', self.on_drag_drop)
self.connect('drag-data-received',
self.on_drag_data_received)
self.on_drag_data_received)
self.source.paned.connect("expanded", self.bottom_expander_expanded_callback)
# lastly support drag-drop from coverart to devices/nautilus etc
@@ -278,34 +281,34 @@ class CoverIconView(EnhancedIconView, AbstractView):
[], Gdk.DragAction.COPY)
#targets = Gtk.TargetList.new([Gtk.TargetEntry.new("application/x-rhythmbox-entry", 0, 0),
# Gtk.TargetEntry.new("text/uri-list", 0, 1) ])
targets = Gtk.TargetList.new([Gtk.TargetEntry.new("text/uri-list", 0, 0) ])
targets = Gtk.TargetList.new([Gtk.TargetEntry.new("text/uri-list", 0, 0)])
# N.B. values taken from rhythmbox v2.97 widgets/rb_entry_view.c
targets.add_uri_targets(1)
self.drag_source_set_target_list(targets)
self.connect("drag-data-get", self.on_drag_data_get)
# set the model to the view
#self.set_pixbuf_column(AlbumsModel.columns['pixbuf'])
self.set_model(self.album_manager.model.store)
# setup view to monitor mouse movements
self.add_events(Gdk.EventMask.POINTER_MOTION_MASK)
self.hover_pixbufs = {
'button_play':None,
'button_play_hover':None,
'button_playpause':None,
'button_playpause_hover':None,
'button_queue':None,
'button_queue_hover':None }
'button_play': None,
'button_play_hover': None,
'button_playpause': None,
'button_playpause_hover': None,
'button_queue': None,
'button_queue_hover': None}
for pixbuf_type in self.hover_pixbufs:
filename = 'img/' + pixbuf_type + '.png'
filename = rb.find_plugin_file(self.plugin, filename)
self.hover_pixbufs[pixbuf_type] = GdkPixbuf.Pixbuf.new_from_file_at_size(filename,
PLAY_SIZE_X, PLAY_SIZE_Y)
self.hover_pixbufs[pixbuf_type] = GdkPixbuf.Pixbuf.new_from_file_at_size(filename,
PLAY_SIZE_X, PLAY_SIZE_Y)
self._connect_properties()
self._connect_signals()
@@ -327,32 +330,32 @@ class CoverIconView(EnhancedIconView, AbstractView):
Gio.SettingsBindFlags.GET)
setting.bind(self.gs.PluginKey.DISPLAY_TEXT, self,
'display_text_enabled', Gio.SettingsBindFlags.GET)
'display_text_enabled', Gio.SettingsBindFlags.GET)
setting.bind(self.gs.PluginKey.ICON_AUTOMATIC, self,
'icon_automatic', Gio.SettingsBindFlags.GET)
setting.bind(self.gs.PluginKey.DISPLAY_TEXT_POS, self,
'display-text-pos', Gio.SettingsBindFlags.GET)
'icon_automatic', Gio.SettingsBindFlags.GET)
setting.bind(self.gs.PluginKey.DISPLAY_TEXT_POS, self,
'display-text-pos', Gio.SettingsBindFlags.GET)
def _connect_signals(self):
self.connect("item-clicked", self.item_clicked_callback)
self.connect("selection-changed", self.selectionchanged_callback)
self.connect("item-activated", self.item_activated_callback)
self.connect("item-activated", self.item_activated_callback)
self.connect('notify::icon-spacing',
self.on_notify_icon_spacing)
self.on_notify_icon_spacing)
self.connect('notify::icon-padding',
self.on_notify_icon_padding)
self.on_notify_icon_padding)
self.connect('notify::display-text-enabled',
self._activate_markup)
self._activate_markup)
self.connect('notify::display-text-pos',
self._activate_markup)
self._activate_markup)
self.connect("motion-notify-event", self.on_pointer_motion)
def get_view_icon_name(self):
return "iconview.png"
def resize_icon(self, cover_size):
def resize_icon(self, cover_size):
'''
Callback called when to resize the icon
[common to all views]
@@ -383,7 +386,7 @@ class CoverIconView(EnhancedIconView, AbstractView):
return result
def on_drag_data_received(self, widget, drag_context, x, y, data, info,
time):
time):
'''
Callback called when the drag source has prepared the data (pixbuf)
for us to use.
@@ -446,62 +449,61 @@ class CoverIconView(EnhancedIconView, AbstractView):
widget.stop_emission_by_name('drag-begin')
else:
widget.stop_emission('drag-begin')
def _cover_play_hotspot(self, path, in_vacinity=False):
if path and hasattr(self, "get_cell_rect"):
# get_cell_rect only exists in Gtk+3.6 and later
valid, rect = self.get_cell_rect(path, None) # rect of widget coords
cursor_x, cursor_y = self.get_pointer() # returns widget coords
valid, rect = self.get_cell_rect(path, None) # rect of widget coords
cursor_x, cursor_y = self.get_pointer() # returns widget coords
c_x = cursor_x - rect.x
c_y = cursor_y - rect.y
sizing = (rect.width / 2) if in_vacinity else 0
if c_x < (PLAY_SIZE_X + sizing) and \
c_y < (PLAY_SIZE_Y + sizing) and \
c_x > (self.icon_padding + self.icon_spacing) and \
c_y > (self.icon_padding + self.icon_spacing):
sizing = (rect.width / 2) if in_vacinity else 0
if c_x < (PLAY_SIZE_X + sizing) and \
c_y < (PLAY_SIZE_Y + sizing) and \
c_x > (self.icon_padding + self.icon_spacing) and \
c_y > (self.icon_padding + self.icon_spacing):
return True
return False
def on_pointer_motion(self, widget, event):
self._calculate_hotspot(event)
def _calculate_hotspot(self, event):
path = self.get_path_at_pos(event.x, event.y)
(_, playing) = self.shell.props.shell_player.get_playing()
if playing and not self._last_play_path:
entry = self.shell.props.shell_player.get_playing_entry()
album = self.album_manager.model.get_from_dbentry(entry)
self._last_play_path = self.album_manager.model.get_path(album)
if playing and self._last_play_path == path:
icon = 'button_playpause'
elif playing:
icon = 'button_queue'
else:
icon = 'button_play'
def recheck_hotspot(args):
path = args[0]
in_vacinity = args[1]
if self._cover_play_hotspot(path, in_vacinity):
current_path = self.get_path_at_pos(event.x, event.y)
if current_path == path:
self._current_hover_path = path
else:
self._current_hover_path = None
self._recheck_in_progress = False
self._calculate_hotspot(event)
self.queue_draw()
if self._cover_play_hotspot(path, in_vacinity=True):
exact_hotspot = self._cover_play_hotspot(path)
if path == self._current_hover_path:
@@ -512,39 +514,39 @@ class CoverIconView(EnhancedIconView, AbstractView):
if not self._recheck_in_progress:
self._recheck_in_progress = True
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 150,
recheck_hotspot, (path, False))
hover = None
recheck_hotspot, (path, False))
hover = None
else:
hover = None
if not self._recheck_in_progress:
self._recheck_in_progress = True
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 450,
recheck_hotspot, (path, True))
recheck_hotspot, (path, True))
else:
hover = None
self.props.cell_area.hover_pixbuf = hover
if hover and path:
valid, rect = self.get_cell_rect(path, None)
self.props.window.invalidate_rect(rect, True)
self.queue_draw()
def item_clicked_callback(self, iconview, event, path):
'''
Callback called when the user clicks somewhere on the cover_view.
Along with source "show_hide_pane", takes care of showing/hiding the bottom
pane after a second click on a selected album.
'''
# first test if we've clicked on the cover-play icon
if self._cover_play_hotspot(path):
(_, playing) = self.shell.props.shell_player.get_playing()
# first see if anything is playing...
if playing:
entry = self.shell.props.shell_player.get_playing_entry()
album = self.album_manager.model.get_from_dbentry(entry)
# if the current playing entry corresponds to the album
# we are hovering over then we are requesting to pause
if self.album_manager.model.get_from_path(path) == album:
@@ -552,33 +554,33 @@ class CoverIconView(EnhancedIconView, AbstractView):
self.shell.props.shell_player.pause()
self.on_pointer_motion(self, event)
return
# if we are not playing and the last thing played is what
# we are still hovering over then we must be requesting to play
if self._last_play_path and self._last_play_path == path:
self.shell.props.shell_player.play()
self.on_pointer_motion(self, event)
return
# otherwise, this must be a new album so we are asking just
# to play this new album ... just need a short interval
# for the selection event to kick in first
def delay(*args):
if playing: # if we are playing then queue up the next album
if playing: # if we are playing then queue up the next album
self.source.queue_selected_album(None, self.source.favourites)
else: # otherwise just play it
else: # otherwise just play it
self._last_play_path = path
self.source.play_selected_album(self.source.favourites)
self.props.cell_area.hover_pixbuf= \
self.hover_pixbufs['button_play_hover']
self.props.cell_area.hover_pixbuf = \
self.hover_pixbufs['button_play_hover']
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 250,
delay, None)
delay, None)
return
# to expand the entry view
ctrl = event.state & Gdk.ModifierType.CONTROL_MASK
shift = event.state & Gdk.ModifierType.SHIFT_MASK
@@ -587,10 +589,10 @@ class CoverIconView(EnhancedIconView, AbstractView):
self.source.click_count += 1 if not ctrl and not shift else 0
if self.source.click_count == 1:
album = self.album_manager.model.get_from_path(path)\
album = self.album_manager.model.get_from_path(path) \
if path else None
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 250,
self.source.show_hide_pane, album)
self.source.show_hide_pane, album)
def item_activated_callback(self, iconview, path):
'''
@@ -640,16 +642,16 @@ class CoverIconView(EnhancedIconView, AbstractView):
# set the renderer
self.pack_end(self._text_renderer, False)
self.add_attribute(self._text_renderer,
'markup', AlbumsModel.columns['markup'])
'markup', AlbumsModel.columns['markup'])
elif self._text_renderer:
# remove the cell renderer
self.props.cell_area.remove(self._text_renderer)
if self.display_text_enabled:
self.set_tooltip_column(-1) # turnoff tooltips
self.set_tooltip_column(-1) # turnoff tooltips
else:
self.set_tooltip_column(AlbumsModel.columns['tooltip'])
def bottom_expander_expanded_callback(self, paned, expand):
'''
Callback connected to expanded signal of the paned GtkExpander
@@ -663,17 +665,17 @@ class CoverIconView(EnhancedIconView, AbstractView):
self.source.last_selected_album)
self.scroll_to_path(path, False, 0, 0)
return False
Gdk.threads_add_idle(GObject.PRIORITY_DEFAULT_IDLE,
scroll_to_album, None)
scroll_to_album, None)
def switch_to_view(self, source, album):
self.initialise(source)
self.show_policy.initialise(source.album_manager)
self.scroll_to_album(album)
def grab_focus(self):
+139 -135
Ver Arquivo
@@ -39,10 +39,12 @@ from coverart_widgets import PixbufButton
MIN_IMAGE_SIZE = 100
class EntryViewPane(object):
'''
encapulates all of the Track Pane objects
'''
def __init__(self, shell, plugin, source, entry_view_grid, viewmgr):
self.gs = GSetting()
@@ -84,7 +86,7 @@ class EntryViewPane(object):
self.entry_view_grid.attach(self.stars, 1, 1, 1, 1)
stack_switcher = Gtk.StackSwitcher()
stack_switcher.set_stack(self.stack)
self.entry_view_grid.attach( stack_switcher, 0, 1, 1, 1)
self.entry_view_grid.attach(stack_switcher, 0, 1, 1, 1)
viewtoggle = PixbufButton()
viewtoggle.set_image(create_button_image(self.plugin, "entryview.png"))
viewtoggle.props.halign = Gtk.Align.END
@@ -101,8 +103,8 @@ class EntryViewPane(object):
self.entry_view_grid.show_all()
def entry_view_toggled(self, widget, initialised = False):
print ("DEBUG - entry_view_toggled")
def entry_view_toggled(self, widget, initialised=False):
print("DEBUG - entry_view_toggled")
if widget.get_active():
next_view = self.entry_view_full
show_coverart = False
@@ -167,13 +169,13 @@ class EntryViewPane(object):
def cover_search(self, album_artist, manager):
self.cover_search_pane.do_search(album_artist,
manager.cover_man.update_cover)
manager.cover_man.update_cover)
def update_selection(self, last_selected_album, click_count):
'''
Update the source view when an item gets selected.
'''
print ("DEBUG - update_with_selection")
print("DEBUG - update_with_selection")
selected = self.viewmgr.current_view.get_selected_objects()
# clear the entry view
@@ -223,18 +225,18 @@ class EntryViewPane(object):
# update the cover search pane with the first selected album
if cover_search_pane_visible:
self.cover_search_pane.do_search(selected[0],
self.source.album_manager.cover_man.update_cover)
self.source.album_manager.cover_man.update_cover)
return last_selected_album, click_count
class ResultsGrid(Gtk.Grid):
# signals
__gsignals__ = {
'update-cover': (GObject.SIGNAL_RUN_LAST, None, (GObject.Object,RB.RhythmDBEntry))
}
'update-cover': (GObject.SIGNAL_RUN_LAST, None, (GObject.Object, RB.RhythmDBEntry))
}
image_width = 0
def __init__(self, *args, **kwargs):
super(ResultsGrid, self).__init__(*args, **kwargs)
@@ -279,7 +281,7 @@ class ResultsGrid(Gtk.Grid):
def update_cover(self, widget, source, entry):
self.oldval = 0 # force a redraw
self.oldval = 0 # force a redraw
if entry:
album = source.album_manager.model.get_from_dbentry(entry)
self.pixbuf = GdkPixbuf.Pixbuf().new_from_file(album.cover.original)
@@ -293,23 +295,23 @@ class ResultsGrid(Gtk.Grid):
self.image1.queue_draw()
else:
self.image2.queue_draw()
def window_resize(self, widget):
alloc = self.get_allocation()
if alloc.height < 10:
return
if (alloc.width / 3) <= (MIN_IMAGE_SIZE+30) or \
(alloc.height) <= (MIN_IMAGE_SIZE+30):
if (alloc.width / 3) <= (MIN_IMAGE_SIZE + 30) or \
(alloc.height) <= (MIN_IMAGE_SIZE + 30):
self.frame.props.visible = False
else:
self.frame.props.visible = True
framealloc = self.frame.get_allocation()
minval = min(framealloc.width-30, framealloc.height-30)
minval = min(framealloc.width - 30, framealloc.height - 30)
if self.oldval == minval:
return
print ("resizing")
print("resizing")
self.oldval = minval
if self.pixbuf:
p = self.pixbuf.scale_simple(minval, minval, GdkPixbuf.InterpType.BILINEAR)
@@ -322,30 +324,29 @@ class ResultsGrid(Gtk.Grid):
else:
self.image1.set_from_pixbuf(p)
self.stack.set_visible_child_name("image1")
def change_view(self, entry_view, show_coverart):
print ("debug - change_view")
print("debug - change_view")
widget = self.get_child_at(0, 0)
if widget:
self.remove(widget)
if not show_coverart:
widget = self.get_child_at(6, 0)
if widget:
self.remove(widget)
entry_view.props.hexpand = True
entry_view.props.vexpand = True
self.attach(entry_view, 0, 0, 3, 1)
if show_coverart:
self.attach(self.frame, 6, 0, 1, 1)
self.show_all()
class BaseView(RB.EntryView):
class BaseView(RB.EntryView):
def __init__(self, shell, source):
'''
Initializes the entryview.
@@ -355,64 +356,64 @@ class BaseView(RB.EntryView):
self.plugin = self.source.props.plugin
super(RB.EntryView, self).__init__(db=shell.props.db,
shell_player=shell.props.shell_player, is_drag_source=True,
visible_columns=[])
shell_player=shell.props.shell_player, is_drag_source=True,
visible_columns=[])
cl = CoverLocale()
cl.switch_locale(cl.Locale.RB)
self.display_columns()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
self.define_menu()
# connect signals to the shell to know when the playing state changes
self.shell.props.shell_player.connect('playing-song-changed',
self.playing_song_changed)
self.playing_song_changed)
self.shell.props.shell_player.connect('playing-changed',
self.playing_changed)
self.playing_changed)
self.actiongroup = ActionGroup(self.shell, 'coverentryplaylist_submenu')
self.external_plugins = None
self.source_query_model = self.source.source_query_model # RB.RhythmDBQueryModel.new_empty(self.shell.props.db)
self.source_query_model = self.source.source_query_model # RB.RhythmDBQueryModel.new_empty(self.shell.props.db)
self.qm = RB.RhythmDBQueryModel.new_empty(self.shell.props.db)
self.set_model(self.qm)
# connect the sort-order to the library source sort
library_view = self.shell.props.library_source.get_entry_view()
library_view.connect('notify::sort-order',
self._on_library_sorting_changed)
self._on_library_sorting_changed)
self._on_library_sorting_changed(library_view,
library_view.props.sort_order)
library_view.props.sort_order)
# connect to the sort-order property
# connect to the sort-order property
self.connect('notify::sort-order', self._notify_sort_order,
library_view)
library_view)
self.echonest_similar_playlist = None
self.echonest_similar_genre_playlist = None
self.lastfm_similar_playlist = None
self.set_columns_clickable(False)
self.connect('selection-changed', self.selection_changed)
self.artists = ""
def __del__(self):
del self.action_group
del self.play_action
del self.queue_action
def define_menu(self):
pass
def display_columns(self):
pass
def selection_changed(self, entry_view):
entries = entry_view.get_selected_entries()
if entries and len(entries) > 0:
@@ -427,13 +428,13 @@ class BaseView(RB.EntryView):
(_, playing) = self.shell.props.shell_player.get_playing()
self.playing_changed(self.shell.props.shell_player, playing)
artists = album.artists.split(', ')
if self.artists == "":
self.artists = artists
else:
self.artists = list(set(self.artists + artists))
print("CoverArtBrowser DEBUG - add_album()")
def clear(self):
@@ -443,7 +444,7 @@ class BaseView(RB.EntryView):
self.qm.remove_entry(row[0])
self.artists = ""
print("CoverArtBrowser DEBUG - clear()")
def do_entry_activated(self, entry):
@@ -452,49 +453,49 @@ class BaseView(RB.EntryView):
self.play_track_menu_item_callback(entry)
print("CoverArtBrowser DEBUG - do_entry_activated()")
return True
def pre_popup_menu_callback(self, *args):
pass
def do_show_popup(self, over_entry):
if over_entry:
print("CoverArtBrowser DEBUG - do_show_popup()")
self.popup.popup(self.source,
'entryview_popup_menu', 0, Gtk.get_current_event_time())
'entryview_popup_menu', 0, Gtk.get_current_event_time())
return over_entry
def play_similar_artist_menu_item_callback(self, *args):
if not self.echonest_similar_playlist:
self.echonest_similar_playlist = \
EchoNestPlaylist( self.shell,
self.shell.props.queue_source)
EchoNestPlaylist(self.shell,
self.shell.props.queue_source)
selected = self.get_selected_entries()
entry = selected[0]
self.echonest_similar_playlist.start(entry, reinitialise=True)
def play_similar_genre_menu_item_callback(self, *args):
if not self.echonest_similar_genre_playlist:
self.echonest_similar_genre_playlist = \
EchoNestGenrePlaylist( self.shell,
self.shell.props.queue_source)
EchoNestGenrePlaylist(self.shell,
self.shell.props.queue_source)
selected = self.get_selected_entries()
entry = selected[0]
self.echonest_similar_genre_playlist.start(entry, reinitialise=True)
def play_similar_track_menu_item_callback(self, *args):
if not self.lastfm_similar_playlist:
self.lastfm_similar_playlist = \
LastFMTrackPlaylist( self.shell,
self.shell.props.queue_source)
LastFMTrackPlaylist(self.shell,
self.shell.props.queue_source)
selected = self.get_selected_entries()
entry = selected[0]
self.lastfm_similar_playlist.start(entry, reinitialise=True)
def play_track_menu_item_callback(self, *args):
print("CoverArtBrowser DEBUG - play_track_menu_item_callback()")
@@ -504,12 +505,12 @@ class BaseView(RB.EntryView):
selected = self.get_selected_entries()
entry = selected[0]
if len(selected) == 1:
self.source_query_model.copy_contents(self.qm)
else:
self.add_tracks_to_source(self.source_query_model)
self.source.props.query_model = self.source_query_model
# Start the music
@@ -536,7 +537,7 @@ class BaseView(RB.EntryView):
selected.reverse()
selected = sorted(selected,
key=lambda song: song.get_ulong(RB.RhythmDBPropType.TRACK_NUMBER))
key=lambda song: song.get_ulong(RB.RhythmDBPropType.TRACK_NUMBER))
for entry in selected:
source.add_entry(entry, -1)
@@ -551,7 +552,7 @@ class BaseView(RB.EntryView):
for entry in selected:
self.shell.props.db.entry_set(entry, RB.RhythmDBPropType.RATING,
rating)
rating)
self.shell.props.db.commit()
@@ -560,7 +561,7 @@ class BaseView(RB.EntryView):
info_dialog = RB.SongInfo(source=self.source, entry_view=self)
info_dialog.show_all()
print("CoverArtBrowser DEBUG - show_properties_menu_item_callback()")
def playing_song_changed(self, shell_player, entry):
@@ -599,8 +600,8 @@ class BaseView(RB.EntryView):
def add_to_static_playlist_menu_item_callback(self, action, param, args):
print("CoverArtBrowser DEBUG - " + \
"add_to_static_playlist_menu_item_callback")
"add_to_static_playlist_menu_item_callback")
playlist = args['playlist']
self.add_tracks_to_source(playlist)
@@ -615,68 +616,69 @@ class BaseView(RB.EntryView):
# update library source's view direction
library_view.set_sorting_type(self.props.sort_order)
class CoverArtCompactEntryView(BaseView):
__hash__ = GObject.__hash__
def __init__(self, shell, source):
'''
Initializes the entryview.
'''
super(CoverArtCompactEntryView, self).__init__(shell, source)
def display_columns(self):
self.col_map = OrderedDict([
('track-number', RB.EntryViewColumn.TRACK_NUMBER),
('title', RB.EntryViewColumn.TITLE),
('artist', RB.EntryViewColumn.ARTIST),
('rating', RB.EntryViewColumn.RATING),
('duration', RB.EntryViewColumn.DURATION)
])
('track-number', RB.EntryViewColumn.TRACK_NUMBER),
('title', RB.EntryViewColumn.TITLE),
('artist', RB.EntryViewColumn.ARTIST),
('rating', RB.EntryViewColumn.RATING),
('duration', RB.EntryViewColumn.DURATION)
])
for entry in self.col_map:
visible = False if entry == 'artist' else True
self.append_column(self.col_map[entry], visible)
def add_album(self, album):
super(CoverArtCompactEntryView, self).add_album(album)
if len(self.artists) > 1:
self.get_column(RB.EntryViewColumn.ARTIST).set_visible(True)
else:
self.get_column(RB.EntryViewColumn.ARTIST).set_visible(False)
def define_menu(self):
popup = Menu(self.plugin, self.shell)
popup.load_from_file('N/A',
'ui/coverart_entryview_compact_pop_rb3.ui')
signals = {
'ev_compact_play_track_menu_item' : self.play_track_menu_item_callback,
'ev_compact_queue_track_menu_item' : self.queue_track_menu_item_callback,
'ev_compact_add_to_playing_menu_item' : self.add_to_playing_menu_item_callback,
'ev_compact_new_playlist' : self.add_playlist_menu_item_callback,
'ev_compact_show_properties_menu_item' : self.show_properties_menu_item_callback,
'ev_compact_similar_track_menu_item' : self.play_similar_track_menu_item_callback,
'ev_compact_similar_artist_menu_item' : self.play_similar_artist_menu_item_callback,
'ev_compact_similar_genre_menu_item' : self.play_similar_genre_menu_item_callback }
'ev_compact_play_track_menu_item': self.play_track_menu_item_callback,
'ev_compact_queue_track_menu_item': self.queue_track_menu_item_callback,
'ev_compact_add_to_playing_menu_item': self.add_to_playing_menu_item_callback,
'ev_compact_new_playlist': self.add_playlist_menu_item_callback,
'ev_compact_show_properties_menu_item': self.show_properties_menu_item_callback,
'ev_compact_similar_track_menu_item': self.play_similar_track_menu_item_callback,
'ev_compact_similar_artist_menu_item': self.play_similar_artist_menu_item_callback,
'ev_compact_similar_genre_menu_item': self.play_similar_genre_menu_item_callback}
popup.connect_signals(signals)
popup.connect('pre-popup', self.pre_popup_menu_callback)
self.popup = popup
def playlist_menu_item_callback(self, *args):
print("CoverArtBrowser DEBUG - playlist_menu_item_callback")
self.source.playlist_fillmenu(self.popup, 'ev_compact_playlist_sub_menu_item', 'ev_compact_playlist_section',
self.actiongroup, self.add_to_static_playlist_menu_item_callback)
self.actiongroup, self.add_to_static_playlist_menu_item_callback)
def pre_popup_menu_callback(self, *args):
'''
Callback when the popup menu is about to be displayed
'''
state,sensitive = self.shell.props.shell_player.get_playing()
state, sensitive = self.shell.props.shell_player.get_playing()
if not state:
sensitive = False
@@ -684,52 +686,53 @@ class CoverArtCompactEntryView(BaseView):
if not self.external_plugins:
self.external_plugins = \
CreateExternalPluginMenu("ev_compact_entryview", 5, self.popup)
CreateExternalPluginMenu("ev_compact_entryview", 5, self.popup)
self.external_plugins.create_menu('entryview_compact_popup_menu')
self.playlist_menu_item_callback()
class CoverArtEntryView(BaseView):
__hash__ = GObject.__hash__
def __init__(self, shell, source):
'''
Initializes the entryview.
'''
super(CoverArtEntryView, self).__init__(shell, source)
def display_columns(self):
self.col_map = OrderedDict([
('track-number', RB.EntryViewColumn.TRACK_NUMBER),
('title', RB.EntryViewColumn.TITLE),
('genre', RB.EntryViewColumn.GENRE),
('artist', RB.EntryViewColumn.ARTIST),
('album', RB.EntryViewColumn.ALBUM),
('composer', RB.EntryViewColumn.COMPOSER),
('date', RB.EntryViewColumn.YEAR),
('duration', RB.EntryViewColumn.DURATION),
('bitrate', RB.EntryViewColumn.QUALITY),
('play-count', RB.EntryViewColumn.PLAY_COUNT),
('beats-per-minute', RB.EntryViewColumn.BPM),
('comment', RB.EntryViewColumn.COMMENT),
('location', RB.EntryViewColumn.LOCATION),
('rating', RB.EntryViewColumn.RATING),
('last-played', RB.EntryViewColumn.LAST_PLAYED),
('first-seen', RB.EntryViewColumn.FIRST_SEEN)
])
('track-number', RB.EntryViewColumn.TRACK_NUMBER),
('title', RB.EntryViewColumn.TITLE),
('genre', RB.EntryViewColumn.GENRE),
('artist', RB.EntryViewColumn.ARTIST),
('album', RB.EntryViewColumn.ALBUM),
('composer', RB.EntryViewColumn.COMPOSER),
('date', RB.EntryViewColumn.YEAR),
('duration', RB.EntryViewColumn.DURATION),
('bitrate', RB.EntryViewColumn.QUALITY),
('play-count', RB.EntryViewColumn.PLAY_COUNT),
('beats-per-minute', RB.EntryViewColumn.BPM),
('comment', RB.EntryViewColumn.COMMENT),
('location', RB.EntryViewColumn.LOCATION),
('rating', RB.EntryViewColumn.RATING),
('last-played', RB.EntryViewColumn.LAST_PLAYED),
('first-seen', RB.EntryViewColumn.FIRST_SEEN)
])
for entry in self.col_map:
visible = True if entry == 'title' else False
self.append_column(self.col_map[entry], visible)
# connect the visible-columns global setting to update our entryview
gs = GSetting()
rhythm_settings = gs.get_setting(gs.Path.RBSOURCE)
rhythm_settings.connect('changed::visible-columns',
self.on_visible_columns_changed)
self.on_visible_columns_changed)
self.on_visible_columns_changed(rhythm_settings, 'visible-columns')
def on_visible_columns_changed(self, settings, key):
print("CoverArtBrowser DEBUG - on_visible_columns_changed()")
# reset current columns
@@ -739,11 +742,11 @@ class CoverArtEntryView(BaseView):
if entry in settings[key]:
col.set_visible(True)
else:
if entry != 'title':
if entry != 'title':
col.set_visible(False)
print ("CoverArtBrowser DEBUG - end on_visible_columns_changed()")
print("CoverArtBrowser DEBUG - end on_visible_columns_changed()")
def define_menu(self):
popup = Menu(self.plugin, self.shell)
popup.load_from_file('N/A',
@@ -751,29 +754,29 @@ class CoverArtEntryView(BaseView):
signals = {
'ev_full_play_track_menu_item': self.play_track_menu_item_callback,
'ev_full_queue_track_menu_item': self.queue_track_menu_item_callback,
'ev_full_add_to_playing_menu_item' : self.add_to_playing_menu_item_callback,
'ev_full_add_to_playing_menu_item': self.add_to_playing_menu_item_callback,
'ev_full_new_playlist': self.add_playlist_menu_item_callback,
'ev_full_show_properties_menu_item': self.show_properties_menu_item_callback,
'ev_full_similar_track_menu_item': self.play_similar_track_menu_item_callback,
'ev_full_similar_artist_menu_item': self.play_similar_artist_menu_item_callback,
'ev_full_similar_genre_menu_item': self.play_similar_genre_menu_item_callback }
'ev_full_similar_genre_menu_item': self.play_similar_genre_menu_item_callback}
popup.connect_signals(signals)
popup.connect('pre-popup', self.pre_popup_menu_callback)
self.popup = popup
def playlist_menu_item_callback(self, *args):
print("CoverArtBrowser DEBUG - playlist_menu_item_callback")
self.source.playlist_fillmenu(self.popup, 'ev_full_playlist_sub_menu_item', 'ev_full_playlist_section',
self.actiongroup, self.add_to_static_playlist_menu_item_callback)
self.actiongroup, self.add_to_static_playlist_menu_item_callback)
def pre_popup_menu_callback(self, *args):
'''
Callback when the popup menu is about to be displayed
'''
state,sensitive = self.shell.props.shell_player.get_playing()
state, sensitive = self.shell.props.shell_player.get_playing()
if not state:
sensitive = False
@@ -781,11 +784,12 @@ class CoverArtEntryView(BaseView):
if not self.external_plugins:
self.external_plugins = \
CreateExternalPluginMenu("ev_full_entryview", 5, self.popup)
CreateExternalPluginMenu("ev_full_entryview", 5, self.popup)
self.external_plugins.create_menu('entryview_full_popup_menu')
self.playlist_menu_item_callback()
GObject.type_register(CoverArtEntryView)
GObject.type_register(CoverArtCompactEntryView)
+54 -52
Ver Arquivo
@@ -35,55 +35,56 @@ import os
import sys
import subprocess
class CoverArtExport(GObject.Object):
'''
This class provides for various export routines
'''
TARGET_BITRATE = 128
def __init__(self, plugin, shell, album_manager):
self.plugin = plugin
self.shell = shell
self.album_manager = album_manager
self._gstreamer_has_initialised = False
def is_search_plugin_enabled(self):
peas = Peas.Engine.get_default()
loaded_plugins = peas.get_loaded_plugins()
result = False
if 'coverart_search_providers' in loaded_plugins:
info = peas.get_plugin_info('coverart_search_providers')
version = info.get_version()
if NaturalString(version) >= "0.9":
result = True
return result
def embed_albums(self, selected_albums):
'''
method to create the menu items for all supported plugins
:selected_albums: `Album` - array of albums
'''
self._initialise_gstreamer()
from coverart_search_tracks import CoverArtTracks
search_tracks = CoverArtTracks()
playlist_manager = self.shell.props.playlist_manager
playlists_entries = playlist_manager.get_playlists()
ui = Gtk.Builder()
ui.add_from_file(rb.find_plugin_file(self.plugin,
'ui/coverart_exportembed.ui'))
'ui/coverart_exportembed.ui'))
ui.connect_signals(self)
embeddialog = ui.get_object('exportembeddialog')
folderchooserbutton = ui.get_object('folderchooserbutton')
folderchooserbutton = ui.get_object('folderchooserbutton')
use_album_name_checkbutton = ui.get_object('use_album_name_checkbutton')
open_filemanager_checkbutton = ui.get_object('open_filemanager_checkbutton')
convert_checkbutton = ui.get_object('convert_checkbutton')
@@ -92,12 +93,12 @@ class CoverArtExport(GObject.Object):
resize_spinbutton = ui.get_object('resize_spinbutton')
bitrate_spinbutton.set_value(self.TARGET_BITRATE)
resize_spinbutton.set_value(128)
downloads_dir = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOWNLOAD)
folderchooserbutton.set_current_folder(downloads_dir)
response = embeddialog.run()
if response != Gtk.ResponseType.OK:
embeddialog.destroy()
return
@@ -113,7 +114,7 @@ class CoverArtExport(GObject.Object):
resize = int(resize_spinbutton.get_value())
else:
resize = -1
embeddialog.destroy()
albums = {}
@@ -127,27 +128,28 @@ class CoverArtExport(GObject.Object):
def complete():
self.album_manager.progress = 1
if open_filemanager:
#code taken from http://stackoverflow.com/questions/1795111/is-there-a-cross-platform-way-to-open-a-file-browser-in-python
if sys.platform=='win32':
if sys.platform == 'win32':
import winreg
path= r('SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon')
path = r('SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon')
for root in (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE):
try:
with winreg.OpenKey(root, path) as k:
value, regtype= winreg.QueryValueEx(k, 'Shell')
value, regtype = winreg.QueryValueEx(k, 'Shell')
except WindowsError:
pass
else:
if regtype in (winreg.REG_SZ, winreg.REG_EXPAND_SZ):
shell= value
shell = value
break
else:
shell= 'Explorer.exe'
shell = 'Explorer.exe'
subprocess.Popen([shell, final_folder_store])
elif sys.platform=='darwin':
elif sys.platform == 'darwin':
subprocess.Popen(['open', final_folder_store])
else:
@@ -156,29 +158,29 @@ class CoverArtExport(GObject.Object):
self._albumiter = iter(albums)
self._tracknumber = 0
self._album = next(self._albumiter)
def idle_call(data):
exit_idle = True
track = albums[self._album][self._tracknumber]
if not process_track(self._album, track):
exit_idle = False
self._tracknumber = self._tracknumber + 1
if self._tracknumber >= len(albums[self._album]):
if self._tracknumber >= len(albums[self._album]):
try:
self._tracknumber = 0
self._album = next(self._albumiter)
except StopIteration:
exit_idle = False
if not exit_idle:
complete()
return exit_idle
def process_track(album, track):
self.album_manager.progress = self._track_count / total
self._track_count = self._track_count + 1
@@ -186,16 +188,16 @@ class CoverArtExport(GObject.Object):
key = album.create_ext_db_key()
finalPath = rb3compat.unquote(track.location)[7:]
album_name = RB.search_fold(album.name)
if use_album_name:
folder_store = final_folder_store + '/' + album_name
else:
folder_store = final_folder_store
try:
if not os.path.exists(folder_store):
os.makedirs(folder_store)
if convert:
self.convert_to_mp3(finalPath, folder_store, bitrate)
finalPath = self._calc_mp3_filename(finalPath, folder_store)
@@ -208,27 +210,27 @@ class CoverArtExport(GObject.Object):
dest = os.path.join(folder_store, os.path.basename(finalPath))
desturi = 'file://' + rb3compat.pathname2url(dest)
return search_tracks.embed(desturi, key, resize)
data = None
Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, idle_call, data)
def _initialise_gstreamer(self):
if self._gstreamer_has_initialised:
return
self._gstreamer_has_initialised = True
Gst.init(None)
def on_new_decoded_pad(dbin, pad):
decode = pad.get_parent()
pipeline = decode.get_parent()
convert = pipeline.get_by_name('convert')
decode.link(convert)
#we are going to mimic the following
# gst-launch-1.0 filesrc location="02 - ABBA - Knowing Me, Knowing You.ogg" !
# decodebin ! audioconvert ! audioresample ! lamemp3enc target=bitrate bitrate=128 !
@@ -242,15 +244,15 @@ class CoverArtExport(GObject.Object):
convert = Gst.ElementFactory.make('audioconvert', 'convert')
sample = Gst.ElementFactory.make('audioresample', 'sample')
encoder = Gst.ElementFactory.make('lamemp3enc', 'encoder')
encoder.set_property('target', 'bitrate')
encoder.set_property('target', 'bitrate')
encoder.set_property('bitrate', self.TARGET_BITRATE)
xing = Gst.ElementFactory.make('xingmux', 'xing') # needed to make bitrate more accurate
xing = Gst.ElementFactory.make('xingmux', 'xing') # needed to make bitrate more accurate
mux = Gst.ElementFactory.make('id3v2mux', 'mux')
if not mux:
# use id3mux where not available
mux = Gst.ElementFactory.make('id3mux', 'mux')
sink = Gst.ElementFactory.make('filesink', 'sink')
converter.add(source)
@@ -267,33 +269,33 @@ class CoverArtExport(GObject.Object):
#it doesnt have source-pads (http://stackoverflow.com/questions/2993777/gstreamer-of-pythons-gst-linkerror-problem)
decoder.connect("pad-added", on_new_decoded_pad)
Gst.Element.link(convert, sample)
Gst.Element.link(sample, encoder)
Gst.Element.link(encoder, xing)
Gst.Element.link(xing, mux)
Gst.Element.link(mux, sink)
self.converter=converter
self.source=source
self.sink=sink
self.encoder=encoder
self.converter = converter
self.source = source
self.sink = sink
self.encoder = encoder
def _calc_mp3_filename(self, filename, save_folder):
finalname = os.path.basename(filename)
finalname = finalname.rsplit('.')[0] + ".mp3"
return save_folder + "/" + finalname
return save_folder + "/" + finalname
def convert_to_mp3(self, filename, save_folder, bitrate):
self.source.set_property('location', filename)
self.sink.set_property('location', self._calc_mp3_filename(filename, save_folder))
print (bitrate)
print(bitrate)
if bitrate < 32:
bitrate = self.TARGET_BITRATE
self.encoder.set_property('bitrate', int(bitrate))
print (bitrate)
print(bitrate)
# Start playing
ret = self.converter.set_state(Gst.State.PLAYING)
@@ -311,7 +313,7 @@ class CoverArtExport(GObject.Object):
# for some reason in ubuntu 12.04 Gst.CLOCK_TIME_NONE fails
msg = bus.timed_pop_filtered(
18446744073709551615, Gst.MessageType.ERROR | Gst.MessageType.EOS)
# Parse message
if (msg):
if msg.type == Gst.MessageType.ERROR:
+37 -32
Ver Arquivo
@@ -29,10 +29,12 @@ from coverart_rb3compat import ApplicationShell
from coverart_rb3compat import Menu
from coverart_utils import CaseInsensitiveDict
class ExternalPlugin(GObject.Object):
'''
class for all supported ExternalPlugins
'''
def __init__(self, **kargs):
super(ExternalPlugin, self).__init__(**kargs)
@@ -50,7 +52,7 @@ class ExternalPlugin(GObject.Object):
:param key: `str` name of attribute
:param val: `str` value of attribute
'''
if key == 'is_album_menu':
if val == 'yes':
self.attributes[key] = True
@@ -67,16 +69,16 @@ class ExternalPlugin(GObject.Object):
loaded_plugins = peas.get_loaded_plugins()
if self.attributes['plugin_name'] in CaseInsensitiveDict(loaded_plugins):
print ("found %s" % self.attributes['plugin_name'])
print("found %s" % self.attributes['plugin_name'])
return True
print ("search for %s" % self.attributes['plugin_name'])
print (loaded_plugins)
print("search for %s" % self.attributes['plugin_name'])
print(loaded_plugins)
return False
def create_menu_item(self, menubar, section_name, at_position,
save_actiongroup, save_menu, for_album = False):
save_actiongroup, save_menu, for_album=False):
'''
method to create the menu item appropriate to the plugin.
A plugin can have many menu items - all menuitems are enclosed
@@ -92,34 +94,35 @@ class ExternalPlugin(GObject.Object):
'''
if for_album and not self.attributes['is_album_menu']:
return False
if not self.is_activated():
return False
action = ApplicationShell(save_menu.shell).lookup_action(self.attributes['action_group_name'],
self.attributes['action_name'], self.attributes['action_type'])
self.attributes['action_name'],
self.attributes['action_type'])
if action:
self.attributes['action']=action
self.attributes['action'] = action
if self.attributes['new_menu_name'] != '':
self.attributes['label'] = self.attributes['new_menu_name']
else:
self.attributes['label']=action.label
#self.attributes['sensitive']=action.get_sensitive()
self.attributes['label'] = action.label
#self.attributes['sensitive']=action.get_sensitive()
else:
print ("action not found")
print (self.attributes)
print("action not found")
print(self.attributes)
return False
action = save_actiongroup.add_action(func=self.menuitem_callback,
action_name=self.attributes['action_name'], album=for_album,
shell=save_menu.shell, label=self.attributes['label'])
action_name=self.attributes['action_name'], album=for_album,
shell=save_menu.shell, label=self.attributes['label'])
new_menu_item = save_menu.insert_menu_item(menubar, section_name,
at_position, action)
at_position, action)
return new_menu_item
def do_deactivate(self):
pass
@@ -149,9 +152,10 @@ class ExternalPlugin(GObject.Object):
shell = args['shell']
if for_album:
self.set_entry_view_selected_entries(shell)
self.attributes['action'].activate()
class CreateExternalPluginMenu(GObject.Object):
'''
This is the key class called to initialise all supported plugins
@@ -160,22 +164,23 @@ class CreateExternalPluginMenu(GObject.Object):
:param at_position: `int` position within the GtkMenu to create menu - ignored for RB2.99
:param popup: `Menu` whole popupmenu including sub-menus
'''
def __init__(self, section_name, at_position, popup, **kargs):
super(CreateExternalPluginMenu, self).__init__(**kargs)
self.menu = popup
self.section_name = section_name
self.at_position = at_position
self._actiongroup = ActionGroup(popup.shell, section_name + '_externalplugins')
# all supported plugins will be defined in the following array by parsing
# the plugins XML file for the definition. Supported plugins are split between
# rb2.99 and later and rb2.98 and earlier due to the likelihood that earlier
# plugins may never be updated by their authors
self.supported_plugins = []
extplugins = rb.find_plugin_file(popup.plugin, 'ui/coverart_external_plugins.xml')
root = ET.parse(open(extplugins)).getroot()
@@ -188,7 +193,7 @@ class CreateExternalPluginMenu(GObject.Object):
pluginname = elem.attrib['name']
basemenu = base + "[@name='" + pluginname + "']/menu"
for menuelem in root.xpath(basemenu):
ext = ExternalPlugin()
ext.appendattribute('plugin_name', pluginname)
@@ -206,8 +211,8 @@ class CreateExternalPluginMenu(GObject.Object):
ext.appendattribute(key, val)
self.supported_plugins.append(ext)
def create_menu(self, menu_name, for_album = False):
def create_menu(self, menu_name, for_album=False):
'''
method to create the menu items for all supported plugins
@@ -217,18 +222,18 @@ class CreateExternalPluginMenu(GObject.Object):
EntryView
'''
self.menu_name = menu_name
self._actiongroup.remove_actions()
self._actiongroup.remove_actions()
self.menu.remove_menu_items(self.menu_name, self.section_name)
items_added = False
for plugin in self.supported_plugins:
new_menu_item = plugin.create_menu_item(self.menu_name, self.section_name,
self.at_position, self._actiongroup, self.menu, for_album)
self.at_position, self._actiongroup, self.menu, for_album)
if (not items_added) and new_menu_item:
items_added = True
items_added = True
if items_added:
self.menu.insert_separator(self.menu_name, self.at_position)
+11 -10
Ver Arquivo
@@ -21,6 +21,7 @@ from coverart_widgets import AbstractView
from gi.repository import GObject
from gi.repository import GLib
class ListShowingPolicy(GObject.Object):
'''
Policy that mostly takes care of how and when things should be showed on
@@ -38,37 +39,37 @@ class ListShowingPolicy(GObject.Object):
return
self._has_initialised = True
class ListView(AbstractView):
__gtype_name__ = "ListView"
name = 'listview'
use_plugin_window = False
def __init__(self):
super(ListView, self).__init__()
self.view = self
self._has_initialised = False
self._has_initialised = False
self.show_policy = ListShowingPolicy(self)
def initialise(self, source):
if self._has_initialised:
return
self._has_initialised = True
self.view_name = "list_view"
super(ListView, self).initialise(source)
#self.album_manager = source.album_manager
self.shell = source.shell
def switch_to_view(self, source, album):
self.initialise(source)
GLib.idle_add(self.shell.props.display_page_tree.select,
self.shell.props.library_source)
self.shell.props.library_source)
def get_selected_objects(self):
'''
finds what has been selected
+115 -114
Ver Arquivo
@@ -28,14 +28,15 @@ import random
LOAD_CHUNK = 50
class WebPlaylist(object):
MAX_TRACKS_TO_ADD = 3 # number of tracks to add to a source for each fetch
MIN_TRACKS_TO_FETCH = 5 # number of tracks in source before a fetch will be required
TOTAL_TRACKS_REMEMBERED = 25 # total number of tracks for all artists before a fetch is allowed
MAX_TRACKS_PER_ARTIST = 3 # number of tracks allowed to be remembered per artist
MAX_TRACKS_TO_ADD = 3 # number of tracks to add to a source for each fetch
MIN_TRACKS_TO_FETCH = 5 # number of tracks in source before a fetch will be required
TOTAL_TRACKS_REMEMBERED = 25 # total number of tracks for all artists before a fetch is allowed
MAX_TRACKS_PER_ARTIST = 3 # number of tracks allowed to be remembered per artist
def __init__(self, shell, source, playlist_name):
self.shell = shell
#lets fill up the queue with artists
self.candidate_artist = {}
@@ -47,54 +48,54 @@ class WebPlaylist(object):
self.tracks_not_played = 0
# cache for artist information: valid for a month, can be used indefinitely
# if offline, discarded if unused for six months
self.info_cache = rb.URLCache(name = playlist_name,
path = os.path.join('coverart_browser', playlist_name),
refresh = 30,
discard = 180)
self.info_cache = rb.URLCache(name=playlist_name,
path=os.path.join('coverart_browser', playlist_name),
refresh=30,
discard=180)
self.info_cache.clean()
def playing_song_changed(self, player, entry):
if not entry:
return
if player.get_playing_source() != self.source:
self.playlist_started = False
self.played_artist.clear()
self.tracks_not_played = 0
if self.playlist_started and len(self.source.props.query_model) < self.MIN_TRACKS_TO_FETCH:
self.start(entry)
def start(self, seed_entry, reinitialise = False):
def start(self, seed_entry, reinitialise=False):
artist = seed_entry.get_string(RB.RhythmDBPropType.ARTIST)
if reinitialise:
self.played_artist.clear()
self.tracks_not_played = 0
self.playlist_started = False
player = self.shell.props.shell_player
_, is_playing = player.get_playing()
_, is_playing = player.get_playing()
if is_playing:
player.stop()
for row in self.source.props.query_model:
self.source.props.query_model.remove_entry(row[0])
if self.tracks_not_played > self.TOTAL_TRACKS_REMEMBERED:
print(("we have plenty of tracks to play yet - no need to fetch more %d", self.tracks_not_played))
self.add_tracks_to_source()
return
search_artist = urllib.parse.quote(artist.encode("utf8"))
if search_artist in self.played_artist:
print ("we have already searched for that artist")
print("we have already searched for that artist")
return
self.search_entry = seed_entry
self.played_artist[search_artist] = True
self.playlist_started = True
self._running = False
self._start_process()
@@ -103,14 +104,14 @@ class WebPlaylist(object):
if not self._running:
self._running = True
self.search_website()
def search_website(self):
pass
def _clear_next(self):
self.search_artists = ""
self._running = False
self._running = False
@idle_iterator
def _load_albums(self):
def process(row, data):
@@ -120,8 +121,8 @@ class WebPlaylist(object):
lookup_title = entry.get_string(RB.RhythmDBPropType.TITLE_FOLDED)
if lookup in self.artist and \
lookup_title in \
self.artist[lookup]:
lookup_title in \
self.artist[lookup]:
if lookup not in self.candidate_artist:
self.candidate_artist[lookup] = []
@@ -129,16 +130,16 @@ class WebPlaylist(object):
# N.B. every artist has an array of dicts with a known format of track & add-to-source elements
# the following extracts the track-title and add-to-source to form a dict of track-title and a value
# of the add-to-source
d=dict((i['track-title'], i['add-to-source']) for i in self.candidate_artist[lookup])
d = dict((i['track-title'], i['add-to-source']) for i in self.candidate_artist[lookup])
if len(d) < self.MAX_TRACKS_PER_ARTIST and lookup_title not in d:
# we only append a max of three tracks to each artist
self.candidate_artist[lookup].append({
'track':entry,
'add-to-source':False,
'track-title':lookup_title})
'track': entry,
'add-to-source': False,
'track-title': lookup_title})
self.tracks_not_played = self.tracks_not_played + 1
def after(data):
# update the progress
pass
@@ -147,40 +148,40 @@ class WebPlaylist(object):
print(('Error processing entries: ' + str(exception)))
def finish(data):
self.add_tracks_to_source()
self.add_tracks_to_source()
self._clear_next()
return LOAD_CHUNK, process, after, error, finish
def display_error_message(self):
dialog = Gtk.MessageDialog(None,
Gtk.DialogFlags.MODAL,
Gtk.MessageType.INFO,
Gtk.ButtonsType.OK,
_("No matching tracks have been found"))
Gtk.DialogFlags.MODAL,
Gtk.MessageType.INFO,
Gtk.ButtonsType.OK,
_("No matching tracks have been found"))
dialog.run()
dialog.destroy()
def add_tracks_to_source(self):
entries = []
for artist in self.candidate_artist:
d=dict((i['track'], (self.candidate_artist[artist].index(i),
i['add-to-source'],
artist)) for i in self.candidate_artist[artist])
d = dict((i['track'], (self.candidate_artist[artist].index(i),
i['add-to-source'],
artist)) for i in self.candidate_artist[artist])
for entry, elements in d.items():
element_pos, add_to_source, artist = elements
if not add_to_source:
entries.append({entry: elements})
random.shuffle(entries)
count = 0
for row in entries:
print (row)
print(row)
entry, elements = list(row.items())[0]
element_pos, add_to_source, artist = elements
self.source.add_entry(entry, -1)
@@ -190,24 +191,24 @@ class WebPlaylist(object):
self.tracks_not_played = self.tracks_not_played - 1
if count == self.MAX_TRACKS_TO_ADD:
break
player = self.shell.props.shell_player
_, is_playing = player.get_playing()
_, is_playing = player.get_playing()
if len(self.source.props.query_model) > 0 and not is_playing:
player.play_entry(self.source.props.query_model[0][0], self.source)
class LastFMTrackPlaylist(WebPlaylist):
def __init__(self, shell, source):
WebPlaylist.__init__(self, shell, source, "lastfm_trackplaylist")
def search_website(self):
# unless already cached - directly fetch from lastfm similar track information
apikey = "844353bce568b93accd9ca47674d6c3e"
url = "http://ws.audioscrobbler.com/2.0/?method=track.getsimilar&api_key={0}&artist={1}&track={2}&format=json"
artist = self.search_entry.get_string(RB.RhythmDBPropType.ARTIST)
title = self.search_entry.get_string(RB.RhythmDBPropType.TITLE)
artist = urllib.parse.quote(artist.encode("utf8"))
@@ -216,25 +217,25 @@ class LastFMTrackPlaylist(WebPlaylist):
artist,
title)
print (formatted_url)
cachekey = "artist:%s:title:%s" % (artist, title)
print(formatted_url)
cachekey = "artist:%s:title:%s" % (artist, title)
self.info_cache.fetch(cachekey, formatted_url, self.similar_info_cb, None)
def similar_info_cb(self, data, _):
if not data:
print ("nothing to do")
print("nothing to do")
self.display_error_message()
self._clear_next()
return
similar = json.loads(data.decode('utf-8'))
# loop through the response and find all titles for the artists returned
self.artist = {}
if 'similartracks' not in similar:
print ("No matching data returned from LastFM")
print("No matching data returned from LastFM")
self.display_error_message()
self._clear_next()
return
@@ -242,56 +243,56 @@ class LastFMTrackPlaylist(WebPlaylist):
name = RB.search_fold(song['artist']['name'])
if name not in self.artist:
self.artist[name] = []
self.artist[name].append(RB.search_fold(song['name']))
if len(self.artist) == 0:
print ("no artists returned")
print("no artists returned")
self._clear_next()
return
# loop through every track - see if the track contains the artist & title
# if yes then this is a candidate similar track to remember
query_model = self.shell.props.library_source.props.base_query_model
self._load_albums(iter(query_model), albums={}, model=query_model,
total=len(query_model), progress=0.)
total=len(query_model), progress=0.)
class EchoNestPlaylist(WebPlaylist):
def __init__(self, shell, source):
WebPlaylist.__init__(self, shell, source, "echonest_playlist")
def search_website(self):
# unless already cached - directly fetch from echonest similar artist information
apikey = "N685TONJGZSHBDZMP"
url = "http://developer.echonest.com/api/v4/playlist/basic?api_key={0}&artist={1}&format=json&results=100&type=artist-radio&limited_interactivity=true"
artist = self.search_entry.get_string(RB.RhythmDBPropType.ARTIST)
artist = urllib.parse.quote(artist.encode("utf8"))
formatted_url = url.format(urllib.parse.quote(apikey),
artist)
print (formatted_url)
cachekey = "artist:%s" % artist
print(formatted_url)
cachekey = "artist:%s" % artist
self.info_cache.fetch(cachekey, formatted_url, self.similar_info_cb, None)
def similar_info_cb(self, data, _):
if not data:
print ("nothing to do")
print("nothing to do")
self.display_error_message()
self._clear_next()
return
similar = json.loads(data.decode('utf-8'))
# loop through the response and find all titles for the artists returned
self.artist = {}
if 'songs' not in similar['response']:
print ("No matching data returned from EchoNest")
print("No matching data returned from EchoNest")
self.display_error_message()
self._clear_next()
return
@@ -299,56 +300,56 @@ class EchoNestPlaylist(WebPlaylist):
name = RB.search_fold(song['artist_name'])
if name not in self.artist:
self.artist[name] = []
self.artist[name].append(RB.search_fold(song['title']))
if len(self.artist) == 0:
print ("no artists returned")
print("no artists returned")
self._clear_next()
return
# loop through every track - see if the track contains the artist & title
# if yes then this is a candidate similar track to remember
query_model = self.shell.props.library_source.props.base_query_model
self._load_albums(iter(query_model), albums={}, model=query_model,
total=len(query_model), progress=0.)
total=len(query_model), progress=0.)
class EchoNestGenrePlaylist(WebPlaylist):
def __init__(self, shell, source):
WebPlaylist.__init__(self, shell, source, "echonest_genre_playlist")
def search_website(self):
# unless already cached - directly fetch from echonest similar artist information
apikey = "N685TONJGZSHBDZMP"
url = "http://developer.echonest.com/api/v4/playlist/basic?api_key={0}&genre={1}&format=json&results=100&type=genre-radio&limited_interactivity=true"
genre = self.search_entry.get_string(RB.RhythmDBPropType.GENRE).lower()
genre = urllib.parse.quote(genre.encode("utf8"))
formatted_url = url.format(urllib.parse.quote(apikey),
genre)
print (formatted_url)
cachekey = "genre:%s" % genre
print(formatted_url)
cachekey = "genre:%s" % genre
self.info_cache.fetch(cachekey, formatted_url, self.similar_info_cb, None)
def similar_info_cb(self, data, _):
if not data:
print ("nothing to do")
print("nothing to do")
self.display_error_message()
self._clear_next()
return
similar = json.loads(data.decode('utf-8'))
# loop through the response and find all titles for the artists returned
self.artist = {}
if 'songs' not in similar['response']:
print ("No matching data returned from EchoNest")
print("No matching data returned from EchoNest")
self.display_error_message()
self._clear_next()
return
@@ -356,18 +357,18 @@ class EchoNestGenrePlaylist(WebPlaylist):
name = RB.search_fold(song['artist_name'])
if name not in self.artist:
self.artist[name] = []
self.artist[name].append(RB.search_fold(song['title']))
if len(self.artist) == 0:
print ("no artists returned")
print("no artists returned")
self._clear_next()
return
# loop through every track - see if the track contains the artist & title
# if yes then this is a candidate similar track to remember
query_model = self.shell.props.library_source.props.base_query_model
self._load_albums(iter(query_model), albums={}, model=query_model,
total=len(query_model), progress=0.)
total=len(query_model), progress=0.)
+11 -10
Ver Arquivo
@@ -21,6 +21,7 @@ from coverart_widgets import AbstractView
from gi.repository import GObject
from gi.repository import GLib
class QueueShowingPolicy(GObject.Object):
'''
Policy that mostly takes care of how and when things should be showed on
@@ -38,37 +39,37 @@ class QueueShowingPolicy(GObject.Object):
return
self._has_initialised = True
class QueueView(AbstractView):
__gtype_name__ = "QueueView"
name = 'queueview'
use_plugin_window = False
def __init__(self):
super(QueueView, self).__init__()
self.view = self
self._has_initialised = False
self._has_initialised = False
self.show_policy = QueueShowingPolicy(self)
def initialise(self, source):
if self._has_initialised:
return
self._has_initialised = True
self.view_name = "queue_view"
super(QueueView, self).initialise(source)
#self.album_manager = source.album_manager
self.shell = source.shell
def switch_to_view(self, source, album):
self.initialise(source)
GLib.idle_add(self.shell.props.display_page_tree.select,
self.shell.props.queue_source)
self.shell.props.queue_source)
def get_selected_objects(self):
'''
finds what has been selected
+158 -138
Ver Arquivo
@@ -31,37 +31,40 @@ import sys
import rb
import xml.etree.ElementTree as ET
def pygobject_version():
'''
returns float of the major and minor parts of a pygobject version
e.g. version (3, 9, 5) return float(3.9)
'''
to_number = lambda t: ".".join(str(v) for v in t)
str_version = to_number(GObject.pygobject_version)
return float(str_version.rsplit('.',1)[0])
return float(str_version.rsplit('.', 1)[0])
def compare_pygobject_version(version):
'''
return True if version is less than pygobject_version
i.e. 3.9 < 3.11
'''
to_number = lambda t: ".".join(str(v) for v in t)
str_version = to_number(GObject.pygobject_version)
split = str_version.rsplit('.',2)
split_compare = version.rsplit('.',2)
if int(split_compare[0])<int(split[0]):
split = str_version.rsplit('.', 2)
split_compare = version.rsplit('.', 2)
if int(split_compare[0]) < int(split[0]):
return True
if int(split_compare[1])<int(split[1]):
if int(split_compare[1]) < int(split[1]):
return True
return False
PYVER = sys.version_info[0]
if PYVER >= 3:
@@ -74,65 +77,75 @@ if PYVER >= 3:
import http.client
else:
import httplib
def responses():
if PYVER >=3:
if PYVER >= 3:
return http.client.responses
else:
return httplib.responses
def unicodestr(param, charset):
if PYVER >=3:
return param#str(param, charset)
if PYVER >= 3:
return param #str(param, charset)
else:
return unicode(param, charset)
def unicodeencode(param, charset):
if PYVER >=3:
return param#str(param).encode(charset)
if PYVER >= 3:
return param #str(param).encode(charset)
else:
return unicode(param).encode(charset)
def unicodedecode(param, charset):
if PYVER >=3:
if PYVER >= 3:
return param
else:
return param.decode(charset)
def urlparse(uri):
if PYVER >=3:
if PYVER >= 3:
return urllib.parse.urlparse(uri)
else:
return rb2urlparse(uri)
def url2pathname(url):
if PYVER >=3:
if PYVER >= 3:
return urllib.request.url2pathname(url)
else:
return urllib.url2pathname(url)
def urlopen(filename):
if PYVER >=3:
if PYVER >= 3:
return urllib.request.urlopen(filename)
else:
return urllib.urlopen(filename)
def pathname2url(filename):
if PYVER >=3:
if PYVER >= 3:
return urllib.request.pathname2url(filename)
else:
return urllib.pathname2url(filename)
def unquote(uri):
if PYVER >=3:
if PYVER >= 3:
return urllib.parse.unquote(uri)
else:
return urllib.unquote(uri)
def quote(uri, safe=None):
if PYVER >=3:
if PYVER >= 3:
if safe:
return urllib.parse.quote(uri,safe=safe)
return urllib.parse.quote(uri, safe=safe)
else:
return urllib.parse.quote(uri)
else:
@@ -140,27 +153,30 @@ def quote(uri, safe=None):
return urllib.quote(uri, safe=safe)
else:
return urllib.quote(uri)
def quote_plus(uri):
if PYVER >=3:
if PYVER >= 3:
return urllib.parse.quote_plus(uri)
else:
return urllib.quote_plus(uri)
def is_rb3(*args):
if hasattr(RB.Shell.props, 'ui_manager'):
return False
else:
return True
return True
class Menu(GObject.Object):
'''
Menu object used to create window popup menus
'''
__gsignals__ = {
'pre-popup': (GObject.SIGNAL_RUN_LAST, None, ())
}
}
def __init__(self, plugin, shell):
'''
Initializes the menu.
@@ -169,10 +185,10 @@ class Menu(GObject.Object):
self.plugin = plugin
self.shell = shell
self._unique_num = 0
self._rbmenu_items = {}
self._rbmenu_objects = {}
def add_menu_item(self, menubar, section_name, action):
'''
add a new menu item to the popup
@@ -191,7 +207,7 @@ class Menu(GObject.Object):
:param action: `Action` to associate with the menu item
'''
label = action.label
if is_rb3(self.shell):
app = self.shell.props.application
item = Gio.MenuItem()
@@ -201,14 +217,14 @@ class Menu(GObject.Object):
if not section_name in self._rbmenu_items:
self._rbmenu_items[section_name] = []
self._rbmenu_items[section_name].append(label)
app.add_plugin_menu_item(section_name, label, item)
else:
item = Gtk.MenuItem(label=label)
action.associate_menuitem(item)
self._rbmenu_items[label] = item
bar = self.get_menu_object(menubar)
if position == -1:
bar.append(item)
else:
@@ -245,15 +261,15 @@ class Menu(GObject.Object):
if is_rb3(self.shell):
if not section_name in self._rbmenu_items:
return
app = self.shell.props.application
for menu_item in self._rbmenu_items[section_name]:
app.remove_plugin_menu_item(section_name, menu_item)
if self._rbmenu_items[section_name]:
del self._rbmenu_items[section_name][:]
else:
if not self._rbmenu_items:
@@ -267,8 +283,8 @@ class Menu(GObject.Object):
bar.show_all()
uim.ensure_update()
def load_from_file(self, rb2_ui_filename, rb3_ui_filename ):
def load_from_file(self, rb2_ui_filename, rb3_ui_filename):
'''
utility function to load the menu structure
:param rb2_ui_filename: `str` RB2.98 and below UI file
@@ -277,21 +293,22 @@ class Menu(GObject.Object):
self.builder = Gtk.Builder()
try:
from coverart_browser_prefs import CoverLocale
cl = CoverLocale()
self.builder.set_translation_domain(cl.Locale.LOCALE_DOMAIN)
except:
pass
if is_rb3(self.shell):
ui_filename = rb3_ui_filename
else:
ui_filename = rb2_ui_filename
self.ui_filename = ui_filename
self.builder.add_from_file(rb.find_plugin_file(self.plugin,
ui_filename))
ui_filename))
def _connect_rb3_signals(self, signals):
def _menu_connect(action_name, func):
@@ -299,29 +316,29 @@ class Menu(GObject.Object):
action.connect('activate', func)
action.set_enabled(True)
self.shell.props.window.add_action(action)
for key,value in signals.items():
_menu_connect( key, value)
for key, value in signals.items():
_menu_connect(key, value)
def _connect_rb2_signals(self, signals):
def _menu_connect(menu_item_name, func):
menu_item = self.get_menu_object(menu_item_name)
menu_item.connect('activate', func)
for key,value in signals.items():
_menu_connect( key, value)
for key, value in signals.items():
_menu_connect(key, value)
def connect_signals(self, signals):
'''
connect all signal handlers with their menuitem counterparts
:param signals: `dict` key is the name of the menuitem
and value is the function callback when the menu is activated
'''
'''
if is_rb3(self.shell):
self._connect_rb3_signals(signals)
else:
self._connect_rb2_signals(signals)
def get_gtkmenu(self, source, popup_name):
'''
utility function to obtain the GtkMenu from the menu UI file
@@ -330,7 +347,7 @@ class Menu(GObject.Object):
if popup_name in self._rbmenu_objects:
return self._rbmenu_objects[popup_name]
item = self.builder.get_object(popup_name)
if is_rb3(self.shell):
app = self.shell.props.application
app.link_shared_menus(item)
@@ -338,11 +355,11 @@ class Menu(GObject.Object):
popup_menu.attach_to_widget(source, None)
else:
popup_menu = item
self._rbmenu_objects[popup_name] = popup_menu
return popup_menu
def get_menu_object(self, menu_name_or_link):
'''
utility function returns the GtkMenuItem/Gio.MenuItem
@@ -359,9 +376,9 @@ class Menu(GObject.Object):
popup_menu = app.get_plugin_menu(menu_name_or_link)
else:
popup_menu = item
print (menu_name_or_link)
print(menu_name_or_link)
self._rbmenu_objects[menu_name_or_link] = popup_menu
return popup_menu
def set_sensitive(self, menu_or_action_item, enable):
@@ -371,14 +388,14 @@ class Menu(GObject.Object):
that is to be enabled/disabled
:param enable: `bool` value to enable/disable
'''
if is_rb3(self.shell):
item = self.shell.props.window.lookup_action(menu_or_action_item)
item.set_enabled(enable)
else:
item = self.get_menu_object(menu_or_action_item)
item.set_sensitive(enable)
def popup(self, source, menu_name, button, time):
'''
utility function to show the popup menu
@@ -386,16 +403,17 @@ class Menu(GObject.Object):
self.emit('pre-popup')
menu = self.get_gtkmenu(source, menu_name)
menu.popup(None, None, None, None, button, time)
class ActionGroup(object):
'''
container for all Actions used to associate with menu items
'''
# action_state
STANDARD=0
TOGGLE=1
STANDARD = 0
TOGGLE = 1
def __init__(self, shell, group_name):
'''
constructor
@@ -404,12 +422,12 @@ class ActionGroup(object):
'''
self.group_name = group_name
self.shell = shell
self._actions = {}
if is_rb3(self.shell):
self.actiongroup = Gio.SimpleActionGroup()
else:
else:
self.actiongroup = Gtk.ActionGroup(group_name)
uim = self.shell.props.ui_manager
uim.insert_action_group(self.actiongroup)
@@ -417,14 +435,14 @@ class ActionGroup(object):
@property
def name(self):
return self.group_name
def remove_actions(self):
'''
utility function to remove all actions associated with the ActionGroup
'''
for action in self.actiongroup.list_actions():
self.actiongroup.remove_action(action)
def get_action(self, action_name):
'''
utility function to obtain the Action from the ActionGroup
@@ -447,8 +465,8 @@ class ActionGroup(object):
'''
args['accel'] = accel
return self.add_action(func, action_name, **args)
def add_action(self, func, action_name, **args ):
def add_action(self, func, action_name, **args):
'''
Creates an Action and adds it to the ActionGroup
@@ -465,21 +483,21 @@ class ActionGroup(object):
if 'label' in args:
label = args['label']
else:
label=action_name
label = action_name
if 'accel' in args:
accel = args['accel']
else:
accel = None
state = ActionGroup.STANDARD
state = ActionGroup.STANDARD
if 'action_state' in args:
state = args['action_state']
if is_rb3(self.shell):
if state == ActionGroup.TOGGLE:
action = Gio.SimpleAction.new_stateful(action_name, None,
GLib.Variant('b', False))
GLib.Variant('b', False))
else:
action = Gio.SimpleAction.new(action_name, None)
@@ -489,7 +507,7 @@ class ActionGroup(object):
action_type = 'app'
app = Gio.Application.get_default()
if action_type == 'app':
app.add_action(action)
else:
@@ -497,56 +515,58 @@ class ActionGroup(object):
self.actiongroup.add_action(action)
if accel:
app.add_accelerator(accel, action_type+"."+action_name, None)
app.add_accelerator(accel, action_type + "." + action_name, None)
else:
if 'stock_id' in args:
stock_id = args['stock_id']
else:
stock_id = Gtk.STOCK_CLEAR
if state == ActionGroup.TOGGLE:
action = Gtk.ToggleAction(label=label,
name=action_name,
tooltip='', stock_id=stock_id)
name=action_name,
tooltip='', stock_id=stock_id)
else:
action = Gtk.Action(label=label,
name=action_name,
tooltip='', stock_id=stock_id)
name=action_name,
tooltip='', stock_id=stock_id)
if accel:
self.actiongroup.add_action_with_accel(action, accel)
else:
self.actiongroup.add_action(action)
act = Action(self.shell, action)
act.connect('activate', func, args)
act.label = label
act.accel = accel
self._actions[action_name] = act
return act
class ApplicationShell(object):
'''
Unique class that mirrors RB.Application & RB.Shell menu functionality
'''
# storage for the instance reference
__instance = None
class __impl:
""" Implementation of the singleton interface """
def __init__(self, shell):
self.shell = shell
if is_rb3(self.shell):
self._uids = {}
else:
self._uids = []
self._action_groups = {}
def insert_action_group(self, action_group):
'''
Adds an ActionGroup to the ApplicationShell
@@ -554,7 +574,7 @@ class ApplicationShell(object):
:param action_group: `ActionGroup` to add
'''
self._action_groups[action_group.name] = action_group
def lookup_action(self, action_group_name, action_name, action_type='app'):
'''
looks up (finds) an action created by another plugin. If found returns
@@ -564,7 +584,7 @@ class ApplicationShell(object):
:param action_name: `str` unique name for the action to look for
:param action_type: `str` RB2.99+ action type ("win" or "app")
'''
if is_rb3(self.shell):
if action_type == "app":
action = self.shell.props.application.lookup_action(action_name)
@@ -582,7 +602,7 @@ class ApplicationShell(object):
action = None
if actiongroup:
action = actiongroup.get_action(action_name)
if action:
return Action(self.shell, action)
else:
@@ -611,24 +631,24 @@ class ApplicationShell(object):
for elem in root.findall(".//menuitem"):
action_name = elem.attrib['action']
item_name = elem.attrib['name']
group = self._action_groups[group_name]
act = group.get_action(action_name)
item = Gio.MenuItem()
item.set_detailed_action('app.' + action_name)
item.set_label(act.label)
item.set_attribute_value("accel", GLib.Variant("s", act.accel))
app = Gio.Application.get_default()
index = menu+action_name
app.add_plugin_menu_item(menu,
index, item)
index = menu + action_name
app.add_plugin_menu_item(menu,
index, item)
self._uids[index] = menu
else:
uim = self.shell.props.ui_manager
self._uids.append(uim.add_ui_from_string(ui_string))
uim.ensure_update()
def add_browser_menuitems(self, ui_string, group_name):
'''
utility function to add popup menu items to existing browser popups
@@ -648,19 +668,19 @@ class ApplicationShell(object):
root = ET.fromstring(ui_string)
for elem in root.findall("./popup"):
popup_name = elem.attrib['name']
menuelem = elem.find('.//menuitem')
action_name = menuelem.attrib['action']
item_name = menuelem.attrib['name']
group = self._action_groups[group_name]
act = group.get_action(action_name)
item = Gio.MenuItem()
item.set_detailed_action('win.' + action_name)
item.set_label(act.label)
app = Gio.Application.get_default()
if popup_name == 'QueuePlaylistViewPopup':
plugin_type = 'queue-popup'
elif popup_name == 'BrowserSourceViewPopup':
@@ -670,11 +690,11 @@ class ApplicationShell(object):
elif popup_name == 'PodcastViewPopup':
plugin_type = 'podcast-episode-popup'
else:
print ("unknown type %s" % plugin_type)
index = plugin_type+action_name
print("unknown type %s" % plugin_type)
index = plugin_type + action_name
app.add_plugin_menu_item(plugin_type, index, item)
self._uids[index]=plugin_type
self._uids[index] = plugin_type
else:
uim = self.shell.props.ui_manager
self._uids.append(uim.add_ui_from_string(ui_string))
@@ -686,9 +706,8 @@ class ApplicationShell(object):
'''
if is_rb3(self.shell):
for uid in self._uids:
Gio.Application.get_default().remove_plugin_menu_item(self._uids[uid],
uid)
Gio.Application.get_default().remove_plugin_menu_item(self._uids[uid],
uid)
else:
uim = self.shell.props.ui_manager
for uid in self._uids:
@@ -713,11 +732,12 @@ class ApplicationShell(object):
""" Delegate access to implementation """
return setattr(self.__instance, attr, value)
class Action(object):
'''
class that wraps around either a Gio.Action or a Gtk.Action
'''
def __init__(self, shell, action):
'''
constructor.
@@ -727,19 +747,19 @@ class Action(object):
'''
self.shell = shell
self.action = action
self._label = ''
self._accel = ''
self._current_state = False
self._do_update_state = True
def connect(self, address, func, args):
self._connect_func = func
self._connect_args = args
if address == 'activate':
func = self._activate
if is_rb3(self.shell):
self.action.connect(address, func, args)
else:
@@ -749,9 +769,9 @@ class Action(object):
if self._do_update_state:
self._current_state = not self._current_state
self.set_state(self._current_state)
self._connect_func(action, None, self._connect_args)
@property
def label(self):
'''
@@ -764,21 +784,21 @@ class Action(object):
return self.action.get_label()
else:
return self._label
@label.setter
def label(self, new_label):
if not is_rb3(self.shell):
self.action.set_label(new_label)
self._label = new_label
@property
def accel(self):
'''
get the accelerator associated with the Action
'''
return self._accel
@accel.setter
def accel(self, new_accelerator):
if new_accelerator:
@@ -796,7 +816,7 @@ class Action(object):
return self.action.get_enabled()
else:
return self.action.get_sensitive()
def set_state(self, value):
'''
set the state of a stateful action - this is applicable only
@@ -813,7 +833,7 @@ class Action(object):
self.action.activate(None)
else:
self.action.activate()
def set_active(self, value):
'''
activate or deactivate a stateful action signal
@@ -822,7 +842,7 @@ class Action(object):
:param value: `boolean` state value
'''
if is_rb3(self.shell):
self.action.change_state(GLib.Variant('b', value))
self._current_state = value
@@ -831,7 +851,7 @@ class Action(object):
self._do_update_state = True
else:
self.action.set_active(value)
def get_active(self):
'''
get the state of the action
@@ -851,7 +871,7 @@ class Action(object):
'''
if is_rb3(self.shell):
menuitem.set_detailed_action('win.'+self.action.get_name())
menuitem.set_detailed_action('win.' + self.action.get_name())
else:
menuitem.set_related_action(self.action)
+30 -27
Ver Arquivo
@@ -25,6 +25,7 @@ import coverart_rb3compat as rb3compat
from coverart_album import Album
from coverart_browser_prefs import webkit_support
class CoverSearchPane(Gtk.Box):
'''
This UI represents a pane where different covers can be presented
@@ -32,6 +33,7 @@ class CoverSearchPane(Gtk.Box):
customize the default search and select covers from the pane and use them
as the covers (either with a double click or dragging them).
'''
def __init__(self, plugin, selection_color):
'''
Initializes the pane, loading it's html templates and it's ui.
@@ -54,26 +56,26 @@ class CoverSearchPane(Gtk.Box):
'''
Loads the templates and stylesheets to be used by the pane.
'''
# input_encoding='utf-8',
# input_encoding='utf-8',
path = rb.find_plugin_file(plugin,
'tmpl/albumartsearch-tmpl.html')
'tmpl/albumartsearch-tmpl.html')
self.template = Template(filename=path,
default_filters=['decode.utf8'],
module_directory='/tmp/',
encoding_errors='replace')
default_filters=['decode.utf8'],
module_directory='/tmp/',
encoding_errors='replace')
path = rb.find_plugin_file(plugin,
'tmpl/albumartsearchempty-tmpl.html')
'tmpl/albumartsearchempty-tmpl.html')
self.empty_template = Template(filename=path,
default_filters=['decode.utf8'],
module_directory='/tmp/',
encoding_errors='replace')
default_filters=['decode.utf8'],
module_directory='/tmp/',
encoding_errors='replace')
path = rb.find_plugin_file(plugin,
'tmpl/artistartsearch-tmpl.html')
'tmpl/artistartsearch-tmpl.html')
self.artist_template = Template(filename=path,
default_filters=['decode.utf8'],
module_directory='/tmp/',
encoding_errors='replace')
default_filters=['decode.utf8'],
module_directory='/tmp/',
encoding_errors='replace')
self.styles = rb.find_plugin_file(plugin, 'tmpl/main.css')
def init_gui(self):
@@ -82,6 +84,7 @@ class CoverSearchPane(Gtk.Box):
'''
#---- set up webkit pane -----#
from gi.repository import WebKit
self.webview = WebKit.WebView()
settings = self.webview.get_settings()
settings.set_property('enable-default-context-menu', False)
@@ -102,13 +105,13 @@ class CoverSearchPane(Gtk.Box):
of the album or artist passed.
'''
print ("coverart-search do_search")
print("coverart-search do_search")
if coverobject is self.current_searchobject:
return
self.current_searchobject = coverobject
self.callback = callback
if isinstance(coverobject, Album):
artist = coverobject.artist
album_name = coverobject.name
@@ -119,9 +122,9 @@ class CoverSearchPane(Gtk.Box):
if artist.upper() == "UNKNOWN":
artist = ""
if not(album_name == "" and artist == ""):
if not (album_name == "" and artist == ""):
artist = rb3compat.unicodestr(artist.replace('&', '&amp;'),
'utf-8')
'utf-8')
album_name = rb3compat.unicodestr(album_name.replace('&', '&amp;'), 'utf-8')
self.render_album_art_search(artist, album_name)
else:
@@ -130,9 +133,9 @@ class CoverSearchPane(Gtk.Box):
if artist_name.upper() == "UNKNOWN":
artist_name = ""
if not(artist_name == ""):
if not (artist_name == ""):
artist = rb3compat.unicodestr(artist_name.replace('&', '&amp;'),
'utf-8')
'utf-8')
self.render_artist_art_search(artist)
@@ -141,23 +144,23 @@ class CoverSearchPane(Gtk.Box):
Renders the template on the webview.
'''
temp_file = self.template.render(artist=artist, album=album_name,
stylesheet=self.styles, selection_color=self.selection_color)
stylesheet=self.styles, selection_color=self.selection_color)
print ("here")
print("here")
self.webview.load_string(temp_file, 'text/html', 'utf-8',
self.basepath)
self.basepath)
def render_artist_art_search(self, artist):
'''
Renders the template on the webview.
'''
temp_file = self.artist_template.render(artist=artist,
stylesheet=self.styles, selection_color=self.selection_color)
stylesheet=self.styles, selection_color=self.selection_color)
print ("here")
print("here")
self.webview.load_string(temp_file, 'text/html', 'utf-8',
self.basepath)
self.basepath)
def clear(self):
'''
Clears the webview of any specific info/covers.
@@ -166,7 +169,7 @@ class CoverSearchPane(Gtk.Box):
temp_file = self.empty_template.render(stylesheet=self.styles)
self.webview.load_string(temp_file, 'text/html', 'utf-8',
self.basepath)
self.basepath)
def set_cover(self, webview, arg):
'''
+33 -30
Ver Arquivo
@@ -39,6 +39,7 @@ from coverart_browser_prefs import webkit_support
import rb
class Toolbar(GObject.Object):
def __init__(self, plugin, mainbox, controllers):
super(Toolbar, self).__init__()
@@ -81,10 +82,10 @@ class Toolbar(GObject.Object):
search_entry.controller = controllers['search']
Theme(self.plugin).connect('theme_changed', self._theme_changed,
controllers)
controllers)
self.builder = builder.get_object('toolbar')
#now theme the toolbar including child objects such as the button popups
style_context = self.builder.get_style_context()
style_context.add_class(Gtk.STYLE_CLASS_TOOLBAR)
@@ -93,6 +94,7 @@ class Toolbar(GObject.Object):
for controller in list(controllers.values()):
controller.update_images(True)
class TopToolbar(Toolbar):
ui = 'ui/coverart_topbar.ui'
name = 'main'
@@ -115,11 +117,11 @@ class LeftToolbar(Toolbar):
if self.builder.get_visible():
self.builder.hide()
self.plugin.shell.remove_widget(self.builder,
RB.ShellUILocation.SIDEBAR)
RB.ShellUILocation.SIDEBAR)
def show(self):
self.plugin.shell.add_widget(self.builder,
RB.ShellUILocation.SIDEBAR, expand=False, fill=False)
RB.ShellUILocation.SIDEBAR, expand=False, fill=False)
self.builder.show()
@@ -131,32 +133,33 @@ class RightToolbar(Toolbar):
if self.builder.get_visible():
self.builder.hide()
self.plugin.shell.remove_widget(self.builder,
RB.ShellUILocation.RIGHT_SIDEBAR)
RB.ShellUILocation.RIGHT_SIDEBAR)
def show(self):
self.plugin.shell.add_widget(self.builder,
RB.ShellUILocation.RIGHT_SIDEBAR, expand=False, fill=False)
RB.ShellUILocation.RIGHT_SIDEBAR, expand=False, fill=False)
self.builder.show()
class ToolbarObject(object):
#properties
PROPERTIES='properties_button'
SORT_BY='sort_by'
SORT_ORDER='sort_order'
SORT_BY_ARTIST='sort_by_artist'
SORT_ORDER_ARTIST='sort_order_artist'
GENRE='genre_button'
PLAYLIST='playlist_button'
DECADE='decade_button'
SEARCH='search'
VIEW='view_button'
PROPERTIES = 'properties_button'
SORT_BY = 'sort_by'
SORT_ORDER = 'sort_order'
SORT_BY_ARTIST = 'sort_by_artist'
SORT_ORDER_ARTIST = 'sort_order_artist'
GENRE = 'genre_button'
PLAYLIST = 'playlist_button'
DECADE = 'decade_button'
SEARCH = 'search'
VIEW = 'view_button'
class ToolbarManager(GObject.Object):
# properties
toolbar_pos = GObject.property(type=str, default=TopToolbar.name)
def __init__(self, plugin, main_box, viewmgr):
super(ToolbarManager, self).__init__()
self.plugin = plugin
@@ -166,19 +169,19 @@ class ToolbarManager(GObject.Object):
# initialize toolbars
self._bars = {}
self._bars[TopToolbar.name] = TopToolbar(plugin, main_box,
controllers)
controllers)
self._bars[LeftToolbar.name] = LeftToolbar(plugin, main_box,
controllers)
controllers)
self._bars[RightToolbar.name] = RightToolbar(plugin, main_box,
controllers)
controllers)
self.last_toolbar_pos = None
# connect signal and properties
self._connect_signals()
self._connect_properties()
self._controllers = controllers
def set_enabled(self, enabled, toolbar_object=None):
'''
enable or disable the toolbar object.
@@ -193,7 +196,7 @@ class ToolbarManager(GObject.Object):
else:
for controller in self._controllers:
self._controllers[controller].enabled = enabled
def _connect_signals(self):
self.connect('notify::toolbar-pos', self._on_notify_toolbar_pos)
@@ -201,12 +204,12 @@ class ToolbarManager(GObject.Object):
gs = GSetting()
setting = gs.get_setting(gs.Path.PLUGIN)
setting.bind(gs.PluginKey.TOOLBAR_POS, self, 'toolbar_pos',
Gio.SettingsBindFlags.GET)
Gio.SettingsBindFlags.GET)
def _create_controllers(self, plugin, viewmgr):
controllers = {}
album_model=viewmgr.source.album_manager.model
album_model = viewmgr.source.album_manager.model
controllers[ToolbarObject.PROPERTIES] = \
PropertiesMenuController(plugin, viewmgr.source)
controllers[ToolbarObject.SORT_BY] = \
@@ -225,7 +228,7 @@ class ToolbarManager(GObject.Object):
DecadePopupController(plugin, album_model)
controllers[ToolbarObject.SEARCH] = \
AlbumSearchEntryController(album_model)
controllers[ToolbarObject.VIEW] = viewmgr.controller
return controllers
+60 -41
Ver Arquivo
@@ -35,14 +35,17 @@ from coverart_search_providers import lastfm_connected
from coverart_search_providers import get_search_providers
from collections import namedtuple
class FauxTb(object):
def __init__(self, tb_frame, tb_lineno, tb_next):
self.tb_frame = tb_frame
self.tb_lineno = tb_lineno
self.tb_next = tb_next
def current_stack(skip=0):
try: 1/0
try:
1 / 0
except ZeroDivisionError:
f = sys.exc_info()[2].tb_frame
for i in range(skip + 2):
@@ -53,6 +56,7 @@ def current_stack(skip=0):
f = f.f_back
return lst
def extend_traceback(tb, stack):
"""Extend traceback with stack info."""
head = tb
@@ -60,17 +64,20 @@ def extend_traceback(tb, stack):
head = FauxTb(tb_frame, tb_lineno, head)
return head
def full_exc_info():
"""Like sys.exc_info, but includes the full traceback."""
t, v, tb = sys.exc_info()
full_tb = extend_traceback(tb, current_stack(1))
return t, v, full_tb
def dumpstack(message):
''' dumps the current stack - useful of debugging
'''
logging.error(message, exc_info=full_exc_info())
def uniquify_and_sort(iterable):
''' Removes duplicates of an iterables and returns a list of unique
elements.
@@ -86,6 +93,7 @@ def uniquify_and_sort(iterable):
GenreType = namedtuple("GenreType", ["name", "genre_type"])
class NaturalString(str):
'''
this class implements an object that can naturally compare
@@ -97,7 +105,7 @@ class NaturalString(str):
super(NaturalString, self).__init__()
convert = lambda text: int(text) if text.isdigit() else text.lower()
alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)',
key)]
key)]
self._string_elements = alphanum_key(string)
@@ -298,7 +306,6 @@ class SortedCollection(object):
class ReversedSortedCollection(object):
def __init__(self, sorted_collection):
self._sorted_collection = sorted_collection
@@ -354,7 +361,6 @@ class ReversedSortedCollection(object):
class IdleCallIterator(object):
def __init__(self, chunk, process, after=None, error=None, finish=None):
default = lambda *_: None
@@ -403,6 +409,7 @@ def idle_iterator(func):
return iter_function
class Theme:
'''
This class manages the theme details
@@ -421,7 +428,7 @@ class Theme:
'''
__gsignals__ = {
'theme_changed': (GObject.SIGNAL_RUN_LAST, None, ())
}
}
# below public variables and methods that can be called for Theme
def __init__(self, plugin):
'''
@@ -453,11 +460,11 @@ class Theme:
def _connect_properties(self):
self.setting.bind(self.gs.PluginKey.THEME, self,
'theme', Gio.SettingsBindFlags.GET)
'theme', Gio.SettingsBindFlags.GET)
def _connect_signals(self):
self.connect('notify::theme', self._on_theme_changed,
None)
None)
def _on_theme_changed(self, *args):
self.emit('theme_changed')
@@ -480,11 +487,11 @@ class Theme:
""" Delegate access to implementation """
return setattr(self.__instance, attr, value)
class SpriteSheet(object):
class SpriteSheet(object):
def __init__(self, image, icon_width, icon_height, x_spacing, y_spacing,
x_start, y_start, across_dimension, down_dimension,
alpha_color=None, size=None):
x_start, y_start, across_dimension, down_dimension,
alpha_color=None, size=None):
# load the image
base_image = GdkPixbuf.Pixbuf.new_from_file(image)
@@ -499,15 +506,15 @@ class SpriteSheet(object):
for y in range(0, down_dimension):
for x in range(0, across_dimension):
sprite = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, True,
8, icon_width, icon_height)
8, icon_width, icon_height)
base_image.copy_area(x_start + (x * delta_x),
y_start + (y * delta_y), icon_width, icon_height,
sprite, 0, 0)
y_start + (y * delta_y), icon_width, icon_height,
sprite, 0, 0)
if size:
sprite = sprite.scale_simple(size[0], size[1],
GdkPixbuf.InterpType.BILINEAR)
GdkPixbuf.InterpType.BILINEAR)
self._sprites.append(sprite)
@@ -517,14 +524,15 @@ class SpriteSheet(object):
def __getitem__(self, index):
return self._sprites[index]
class ConfiguredSpriteSheet(object):
def __init__(self, plugin, sprite_name, size=None):
popups = rb.find_plugin_file(plugin, 'img/popups.xml')
root = ET.parse(open(popups)).getroot()
base = 'theme/theme[@folder_name="' + Theme(plugin).current\
+ '"]/spritesheet[@name="' + sprite_name + '"]/'
image = rb.find_plugin_file(plugin, 'img/' + Theme(plugin).current\
+ '/' + root.xpath(base + 'image')[0].text)
base = 'theme/theme[@folder_name="' + Theme(plugin).current \
+ '"]/spritesheet[@name="' + sprite_name + '"]/'
image = rb.find_plugin_file(plugin, 'img/' + Theme(plugin).current \
+ '/' + root.xpath(base + 'image')[0].text)
icon_width = int(root.xpath(base + 'icon')[0].attrib['width'])
icon_height = int(root.xpath(base + 'icon')[0].attrib['height'])
x_spacing = int(root.xpath(base + 'spacing')[0].attrib['x'])
@@ -536,7 +544,7 @@ class ConfiguredSpriteSheet(object):
try:
alpha_color = list(map(int,
root.xpath(base + 'alpha')[0].text.split(' ')))
root.xpath(base + 'alpha')[0].text.split(' ')))
except:
alpha_color = None
@@ -547,7 +555,7 @@ class ConfiguredSpriteSheet(object):
lang = cl.get_locale()
base = sprite_name + '/' + sprite_name + \
'[@spritesheet="' + sprite_name + '"]'
'[@spritesheet="' + sprite_name + '"]'
for elem in root.xpath(base + '[not(@xml:lang)]'):
self.names.append(elem.text)
@@ -557,12 +565,12 @@ class ConfiguredSpriteSheet(object):
if (not self.locale_names) and len(lang) > 2:
for elem in root.xpath(base + '[@xml:lang="' + \
lang[0:2] + '"]'):
lang[0:2] + '"]'):
self.locale_names[elem.text] = elem.attrib['name']
self._sheet = SpriteSheet(image, icon_width, icon_height, x_spacing,
y_spacing, x_start, y_start, across_dimension, down_dimension,
alpha_color, size)
y_spacing, x_start, y_start, across_dimension, down_dimension,
alpha_color, size)
self._genre_db = RB.ExtDB(name='cb_genre')
@@ -581,6 +589,7 @@ class ConfiguredSpriteSheet(object):
def keys(self):
return self.names
class GenreConfiguredSpriteSheet(ConfiguredSpriteSheet):
'''
A sprite-sheet of genres. Creates a pixbuf representation of a picture
@@ -602,7 +611,7 @@ class GenreConfiguredSpriteSheet(ConfiguredSpriteSheet):
def __init__(self, plugin, sprite_name, size=None):
super(GenreConfiguredSpriteSheet, self).__init__(plugin, sprite_name,
size)
size)
self.genre_alternate = {} # contains GenreType tuples
self._alt_icons = {}
self._sprite_name = sprite_name
@@ -626,7 +635,7 @@ class GenreConfiguredSpriteSheet(ConfiguredSpriteSheet):
sprite = GdkPixbuf.Pixbuf.new_from_file(icon_location)
if self._size:
sprite = sprite.scale_simple(self._size[0], self._size[1],
GdkPixbuf.InterpType.BILINEAR)
GdkPixbuf.InterpType.BILINEAR)
self._alt_icons[str(index)] = sprite
self.names.append(str(index))
@@ -654,7 +663,7 @@ class GenreConfiguredSpriteSheet(ConfiguredSpriteSheet):
# if (not self.locale_alternate) and len(lang) > 2:
if len(lang) > 2:
for elem in root.xpath(base + '[@xml:lang="' + \
lang[0:2] + '"]/alt'):
lang[0:2] + '"]/alt'):
self.genre_alternate[GenreType(name=elem.text, genre_type=self.GENRE_LOCALE)] = elem.attrib['genre']
def add_genre_icon(self, filename):
@@ -667,7 +676,7 @@ class GenreConfiguredSpriteSheet(ConfiguredSpriteSheet):
key = RB.ExtDBKey.create_storage('icon', str(next_index))
uri = "file://" + rb3compat.pathname2url(filename)
self._genre_db.store_uri(key, RB.ExtDBSourceType.USER_EXPLICIT, uri)
pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
@@ -675,7 +684,7 @@ class GenreConfiguredSpriteSheet(ConfiguredSpriteSheet):
if self._size:
pixbuf = pixbuf.scale_simple(self._size[0], self._size[1],
GdkPixbuf.InterpType.BILINEAR)
GdkPixbuf.InterpType.BILINEAR)
self._alt_icons[new_genre.name] = pixbuf
self.names.append(new_genre.name)
@@ -731,6 +740,7 @@ class GenreConfiguredSpriteSheet(ConfiguredSpriteSheet):
print("nothing found to amend")
return None
def get_stock_size():
what, width, height = Gtk.icon_size_lookup(Gtk.IconSize.BUTTON)
@@ -742,30 +752,38 @@ def create_pixbuf_from_file_at_size(filename, width, height):
if pixbuf.get_width() != width or pixbuf.get_height() != height:
pixbuf = pixbuf.scale_simple(width, height,
GdkPixbuf.InterpType.BILINEAR)
GdkPixbuf.InterpType.BILINEAR)
return pixbuf
'''
class to search through a dict without case-sensitivity nor
unicode vs string issues
'''
class CaseInsensitiveDict(collections.Mapping):
def __init__(self, d):
self._d = d
self._s = dict((RB.search_fold(k), k) for k in d)
def __contains__(self, k):
return RB.search_fold(k) in self._s
def __len__(self):
return len(self._s)
def __iter__(self):
return iter(self._s)
def __getitem__(self, k):
return self._d[self._s[RB.search_fold(k)]]
def actual_key_case(self, k):
return self._s.get(RB.search_fold(k))
def check_lastfm(force_check=False):
'''
check validity of lastfm connection
@@ -774,26 +792,27 @@ def check_lastfm(force_check=False):
Also returns True if lastFM is not in the list of search providers
'''
providers = get_search_providers()
print (providers)
print (force_check)
print(providers)
print(force_check)
if force_check or 'lastfm-search' in providers:
connected = lastfm_connected()
print (connected)
print(connected)
return connected
elif not 'lastfm-search' in providers:
print ("not lastm-search")
print("not lastm-search")
return True
else:
print ("returning default")
print("returning default")
return False
def create_button_image(plugin, icon_name):
'create a pixbuf for the given icon_name sized according to the stock icon size'
path = 'img/'
return create_pixbuf_from_file_at_size(
rb.find_plugin_file(plugin, path + icon_name),
*get_stock_size())
rb.find_plugin_file(plugin, path + icon_name),
*get_stock_size())
+103 -99
Ver Arquivo
@@ -30,9 +30,11 @@ from coverart_browser_prefs import GSetting
import rb
def enum(**enums):
return type('Enum', (object,), enums)
class OptionsWidget(Gtk.Widget):
def __init__(self, *args, **kwargs):
super(OptionsWidget, self).__init__(*args, **kwargs)
@@ -54,7 +56,7 @@ class OptionsWidget(Gtk.Widget):
# connect signals
self._options_changed_id = self._controller.connect('notify::options',
self._update_options)
self._update_options)
self._current_key_changed_id = self._controller.connect(
'notify::current-key', self._update_current_key)
self._update_image_changed_id = self._controller.connect(
@@ -68,7 +70,7 @@ class OptionsWidget(Gtk.Widget):
def _update_visibility(self, *args):
self.set_visible(self._controller.enabled)
def _update_options(self, *args):
self.update_options()
@@ -86,14 +88,14 @@ class OptionsWidget(Gtk.Widget):
def update_image(self):
pass
def calc_popup_position(self, widget):
# this calculates the popup positioning - algorithm taken
# from Gtk3.8 gtk/gtkmenubutton.c
toplevel = self.get_toplevel()
toplevel.set_type_hint(Gdk.WindowTypeHint.DROPDOWN_MENU)
menu_req, pref_req = widget.get_preferred_size()
align = widget.get_halign()
direction = self.get_direction()
@@ -107,7 +109,7 @@ class OptionsWidget(Gtk.Widget):
allocation = self.get_allocation()
ret, x,y = window.get_origin()
ret, x, y = window.get_origin()
x += allocation.x
y += allocation.y
@@ -120,15 +122,15 @@ class OptionsWidget(Gtk.Widget):
y -= menu_req.height
else:
y -= menu_req.height
return x, y
class OptionsPopupWidget(OptionsWidget):
class OptionsPopupWidget(OptionsWidget):
# signals
__gsignals__ = {
'item-clicked': (GObject.SIGNAL_RUN_LAST, None, (str,))
}
}
def __init__(self, *args, **kwargs):
OptionsWidget.__init__(self, *args, **kwargs)
@@ -189,31 +191,30 @@ class OptionsPopupWidget(OptionsWidget):
if self._controller:
# inform the controller
self._controller.option_selected(key)
def _popup_callback(self, *args):
x, y = self.calc_popup_position(self._popup_menu)
return x, y, False, None
def show_popup(self, align=True):
'''
show the current popup menu
'''
if align:
self._popup_menu.popup(None, None, self._popup_callback, self, 0,
Gtk.get_current_event_time())
Gtk.get_current_event_time())
else:
self._popup_menu.popup(None, None, None, None, 0,
Gtk.get_current_event_time())
Gtk.get_current_event_time())
def do_delete_thyself(self):
self.clear_popupmenu()
del self._popupmenu
class EnhancedButton(Gtk.ToggleButton):
button_relief = GObject.property(type=bool, default=False)
def __init__(self, *args, **kwargs):
@@ -222,10 +223,10 @@ class EnhancedButton(Gtk.ToggleButton):
gs = GSetting()
setting = gs.get_setting(gs.Path.PLUGIN)
setting.bind(gs.PluginKey.BUTTON_RELIEF, self,
'button_relief', Gio.SettingsBindFlags.GET)
'button_relief', Gio.SettingsBindFlags.GET)
self.connect('notify::button-relief',
self.on_notify_button_relief)
self.on_notify_button_relief)
def on_notify_button_relief(self, *arg):
if self.button_relief:
@@ -235,7 +236,6 @@ class EnhancedButton(Gtk.ToggleButton):
class PixbufButton(EnhancedButton):
button_relief = GObject.property(type=bool, default=False)
def __init__(self, *args, **kwargs):
@@ -250,7 +250,7 @@ class PixbufButton(EnhancedButton):
if hasattr(self, "controller.enabled") and not self.controller.enabled:
pixbuf = self._getBlendedPixbuf(pixbuf)
self.get_image().set_from_pixbuf(pixbuf)
self.on_notify_button_relief()
@@ -259,12 +259,12 @@ class PixbufButton(EnhancedButton):
"""Turn a pixbuf into a blended version of the pixbuf by drawing a
transparent alpha blend on it."""
pixbuf = pixbuf.copy()
w,h = pixbuf.get_width(), pixbuf.get_height()
w, h = pixbuf.get_width(), pixbuf.get_height()
surface = cairo.ImageSurface(
cairo.FORMAT_ARGB32, pixbuf.get_width(), pixbuf.get_height())
context = cairo.Context(surface)
Gdk.cairo_set_source_pixbuf(context, pixbuf, 0, 0)
context.paint()
@@ -284,7 +284,7 @@ class PopupButton(PixbufButton, OptionsPopupWidget):
# signals
__gsignals__ = {
'item-clicked': (GObject.SIGNAL_RUN_LAST, None, (str,))
}
}
def __init__(self, *args, **kwargs):
'''
@@ -292,13 +292,13 @@ class PopupButton(PixbufButton, OptionsPopupWidget):
'''
PixbufButton.__init__(self, *args, **kwargs)
OptionsPopupWidget.__init__(self, *args, **kwargs)
self._popup_menu.attach_to_widget(self, None) #critical to ensure theming works
self._popup_menu.attach_to_widget(self, None) #critical to ensure theming works
self._popup_menu.connect('deactivate', self.popup_deactivate)
# initialise some variables
self._first_menu_item = None
def popup_deactivate(self, *args):
self.set_active(False)
@@ -321,14 +321,15 @@ class PopupButton(PixbufButton, OptionsPopupWidget):
if (event.button == Gdk.BUTTON_PRIMARY):
self.show_popup()
self.set_active(True)
class TextPopupButton(EnhancedButton, OptionsPopupWidget):
__gtype_name__ = "TextPopupButton"
# signals
__gsignals__ = {
'item-clicked': (GObject.SIGNAL_RUN_LAST, None, (str,))
}
}
def __init__(self, *args, **kwargs):
'''
@@ -336,8 +337,8 @@ class TextPopupButton(EnhancedButton, OptionsPopupWidget):
'''
EnhancedButton.__init__(self, *args, **kwargs)
OptionsPopupWidget.__init__(self, *args, **kwargs)
self._popup_menu.attach_to_widget(self, None) #critical to ensure theming works
self._popup_menu.attach_to_widget(self, None) #critical to ensure theming works
self._popup_menu.connect('deactivate', self.popup_deactivate)
# initialise some variables
@@ -362,7 +363,7 @@ class MenuButton(PixbufButton, OptionsPopupWidget):
# signals
__gsignals__ = {
'item-clicked': (GObject.SIGNAL_RUN_LAST, None, (str,))
}
}
def __init__(self, *args, **kwargs):
'''
@@ -370,11 +371,11 @@ class MenuButton(PixbufButton, OptionsPopupWidget):
'''
PixbufButton.__init__(self, *args, **kwargs)
OptionsPopupWidget.__init__(self, *args, **kwargs)
self._popup_menu.attach_to_widget(self, None) #critical to ensure theming works
self._popup_menu.attach_to_widget(self, None) #critical to ensure theming works
self._popup_menu.connect('deactivate', self.popup_deactivate)
self._states = {}
def popup_deactivate(self, *args):
self.set_active(False)
@@ -382,11 +383,11 @@ class MenuButton(PixbufButton, OptionsPopupWidget):
'''
add a new menu item to the popup
'''
label = key.label
menutype = key.menutype
typevalue = key.typevalue
if menutype and menutype == 'separator':
new_menu_item = Gtk.SeparatorMenuItem().new()
elif menutype and menutype == 'check':
@@ -399,7 +400,7 @@ class MenuButton(PixbufButton, OptionsPopupWidget):
new_menu_item.show()
self._popup_menu.append(new_menu_item)
def clear_popupmenu(self):
'''
reinitialises/clears the current popup menu and associated actions
@@ -416,7 +417,7 @@ class MenuButton(PixbufButton, OptionsPopupWidget):
for key in self._controller.options:
self.add_menuitem(key)
self._states = {}
def _fire_item_clicked(self, menu_item):
@@ -426,7 +427,7 @@ class MenuButton(PixbufButton, OptionsPopupWidget):
value of the selected item.
'''
self.emit('item-clicked', menu_item.get_label())
def update_image(self):
super(MenuButton, self).update_image()
self.set_image(self._controller.get_current_image())
@@ -484,8 +485,8 @@ class ImageToggleButton(PixbufButton, OptionsWidget):
class ImageRadioButton(Gtk.RadioButton, OptionsWidget):
# this is legacy code that will not as yet work with
# the new toolbar - consider removing this later
# this is legacy code that will not as yet work with
# the new toolbar - consider removing this later
__gtype_name__ = "ImageRadioButton"
@@ -501,10 +502,10 @@ class ImageRadioButton(Gtk.RadioButton, OptionsWidget):
gs = GSetting()
setting = gs.get_setting(gs.Path.PLUGIN)
setting.bind(gs.PluginKey.BUTTON_RELIEF, self,
'button_relief', Gio.SettingsBindFlags.GET)
'button_relief', Gio.SettingsBindFlags.GET)
self.connect('notify::button-relief',
self.on_notify_button_relief)
self.on_notify_button_relief)
# initialise some variables
self.image_display = False
@@ -545,9 +546,10 @@ class ImageRadioButton(Gtk.RadioButton, OptionsWidget):
def update_current_key(self):
# update the current image and tooltip
#self.set_image(self._controller.get_current_image(Gtk.Buildable.get_name(self)))
self.set_tooltip_text("") #self._controller.get_current_description())
self.set_tooltip_text("") #self._controller.get_current_description())
from gi.repository import Gdk
if self.controller.current_key == Gtk.Buildable.get_name(self):
self.set_active(True)
self._set_colour(Gtk.StateFlags.NORMAL)
@@ -555,14 +557,14 @@ class ImageRadioButton(Gtk.RadioButton, OptionsWidget):
self._set_colour(Gtk.StateFlags.INSENSITIVE)
def _set_colour(self, state_flag):
if len(self.get_children()) == 0:
return
def get_standard_colour(label, state_flag):
def get_standard_colour(label, state_flag):
context = label.get_style_context()
return context.get_color(state_flag)
label0 = self.get_children()[0]
if not self._not_active_colour:
@@ -571,24 +573,25 @@ class ImageRadioButton(Gtk.RadioButton, OptionsWidget):
if not self._active_colour:
self._active_colour = get_standard_colour(label0, Gtk.StateFlags.NORMAL)
if state_flag == Gtk.StateFlags.INSENSITIVE:
if state_flag == Gtk.StateFlags.INSENSITIVE:
label0.override_color(Gtk.StateType.NORMAL, self._not_active_colour)
else:
label0.override_color(Gtk.StateType.NORMAL, self._active_colour)
class SearchEntry(RB.SearchEntry, OptionsPopupWidget):
__gtype_name__ = "SearchEntry"
# signals
__gsignals__ = {
'item-clicked': (GObject.SIGNAL_RUN_LAST, None, (str,))
}
}
def __init__(self, *args, **kwargs):
RB.SearchEntry.__init__(self, *args, **kwargs)
OptionsPopupWidget.__init__(self)
#self.props.explicit_mode = True
@OptionsPopupWidget.controller.setter
def controller(self, controller):
if self._controller:
@@ -613,7 +616,7 @@ class SearchEntry(RB.SearchEntry, OptionsPopupWidget):
super(SearchEntry, self).update_current_key()
self.set_placeholder(self._controller.get_current_description())
def do_show_popup(self):
'''
Callback called by the search entry when the magnifier is clicked.
@@ -637,7 +640,7 @@ class QuickSearchEntry(Gtk.Frame):
__gsignals__ = {
'quick-search': (GObject.SIGNAL_RUN_LAST, None, (str,)),
'arrow-pressed': (GObject.SIGNAL_RUN_LAST, None, (object,))
}
}
def __init__(self, *args, **kwargs):
super(QuickSearchEntry, self).__init__(*args, **kwargs)
@@ -645,7 +648,7 @@ class QuickSearchEntry(Gtk.Frame):
# text entry for the quick search input
text_entry = Gtk.Entry(halign='center', valign='center',
margin=5)
margin=5)
self.add(text_entry)
@@ -677,7 +680,7 @@ class QuickSearchEntry(Gtk.Frame):
return False
Gdk.threads_add_timeout_seconds(GLib.PRIORITY_DEFAULT_IDLE, 4,
hide_on_timeout, None)
hide_on_timeout, None)
def do_parent_set(self, old_parent, *args):
if old_parent:
@@ -685,12 +688,12 @@ class QuickSearchEntry(Gtk.Frame):
parent = self.get_parent()
self._on_parent_key_press_id = parent.connect('key-press-event',
self._on_parent_key_press, self.get_child())
self._on_parent_key_press, self.get_child())
def _on_parent_key_press(self, parent, event, entry):
if not self.get_visible() and \
event.keyval not in [Gdk.KEY_Shift_L, Gdk.KEY_Shift_R,
Gdk.KEY_Control_L, Gdk.KEY_Control_R, Gdk.KEY_Escape]:
event.keyval not in [Gdk.KEY_Shift_L, Gdk.KEY_Shift_R,
Gdk.KEY_Control_L, Gdk.KEY_Control_R, Gdk.KEY_Escape]:
# grab focus, redirect the pressed key and make the quick search
# entry visible
entry.set_text('')
@@ -758,12 +761,11 @@ class ProxyPopupButton(Gtk.Frame):
class OptionsListViewWidget(OptionsWidget):
# signals
__gsignals__ = {
'item-clicked': (GObject.SIGNAL_RUN_LAST, None, (str,)),
'deactivate': (GObject.SIGNAL_RUN_LAST, None, ())
}
}
def __init__(self, *args, **kwargs):
OptionsWidget.__init__(self, *args, **kwargs)
@@ -773,7 +775,7 @@ class OptionsListViewWidget(OptionsWidget):
def controller(self, controller):
ui = Gtk.Builder()
ui.add_from_file(rb.find_plugin_file(controller.plugin,
'ui/coverart_listwindow.ui'))
'ui/coverart_listwindow.ui'))
ui.connect_signals(self)
self._listwindow = ui.get_object('listwindow')
self._liststore = ui.get_object('liststore')
@@ -838,10 +840,10 @@ class OptionsListViewWidget(OptionsWidget):
if self._increment:
if button is self._scrolldown_button:
adjustment.set_value(adjustment.get_value()
+ self._step)
+ self._step)
else:
adjustment.set_value(adjustment.get_value()
- self._step)
- self._step)
return self._increment
@@ -851,7 +853,7 @@ class OptionsListViewWidget(OptionsWidget):
self.on_scroll_button_released()
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 50,
scroll, None)
scroll, None)
def on_scroll_button_leave(self, *args):
self._increment = False
@@ -884,7 +886,7 @@ class ListViewButton(PixbufButton, OptionsListViewWidget):
__gsignals__ = {
'item-clicked': (GObject.SIGNAL_RUN_LAST, None, (str,)),
'deactivate': (GObject.SIGNAL_RUN_LAST, None, ())
}
}
def __init__(self, *args, **kwargs):
'''
@@ -892,19 +894,19 @@ class ListViewButton(PixbufButton, OptionsListViewWidget):
'''
PixbufButton.__init__(self, *args, **kwargs)
OptionsListViewWidget.__init__(self, *args, **kwargs)
self._popup.connect('deactivate', self.popup_deactivate)
def popup_deactivate(self, *args):
# add a slight delay to allow the click of button to occur
# before the deactivation of the button - this will allow
# us to toggle the popup via the button correctly
def deactivate(*args):
self.set_active(False)
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 50, deactivate, None)
def update_image(self):
super(ListViewButton, self).update_image()
self.set_image(self._controller.get_current_image())
@@ -934,9 +936,9 @@ class EnhancedIconView(Gtk.IconView):
# signals
__gsignals__ = {
'item-clicked': (GObject.SIGNAL_RUN_LAST, None, (object, object))
}
}
object_column = GObject.property(type=int, default= -1)
object_column = GObject.property(type=int, default=-1)
def __init__(self, *args, **kwargs):
super(EnhancedIconView, self).__init__(*args, **kwargs)
@@ -957,7 +959,7 @@ class EnhancedIconView(Gtk.IconView):
# don't need to reaccommodate if it's a vertical change
self._reallocate_count += 1
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 500,
self._reallocate_columns, None)
self._reallocate_columns, None)
Gtk.IconView.do_size_allocate(self, allocation)
@@ -983,7 +985,7 @@ class EnhancedIconView(Gtk.IconView):
# if the item being clicked isn't selected, we should clear
# the current selection
if len(self.get_selected_objects()) > 0 and \
not self.path_is_selected(current_path):
not self.path_is_selected(current_path):
self.unselect_all()
self.select_path(current_path)
@@ -1011,7 +1013,7 @@ class EnhancedIconView(Gtk.IconView):
model = self.get_model()
selected_objects = list(reversed([model[selected][self.object_column]
for selected in selected_items]))
for selected in selected_items]))
return selected_objects
@@ -1037,7 +1039,7 @@ class PanedCollapsible(Gtk.Paned):
# values for expand method
Paned = enum(DEFAULT=1, EXPAND=2, COLLAPSE=3)
# this indicates the latest position for the handle before a child was
# collapsed
collapsible_y = GObject.property(type=int, default=0)
@@ -1048,7 +1050,7 @@ class PanedCollapsible(Gtk.Paned):
# signals
__gsignals__ = {
'expanded': (GObject.SIGNAL_RUN_LAST, None, (bool,))
}
}
def __init__(self, *args, **kwargs):
super(PanedCollapsible, self).__init__(*args, **kwargs)
@@ -1059,7 +1061,7 @@ class PanedCollapsible(Gtk.Paned):
self.connect('notify::collapsible1', self._on_collapsible1_changed)
self.connect('notify::collapsible2', self._on_collapsible2_changed)
self.connect('notify::collapsible_label',
self._on_collapsible_label_changed)
self._on_collapsible_label_changed)
def _on_collapsible1_changed(self, *args):
if self.collapsible1 and self.collapsible2:
@@ -1072,7 +1074,7 @@ class PanedCollapsible(Gtk.Paned):
def _on_collapsible2_changed(self, *args):
if self.collapsible1 and self.collapsible2:
# check consistency, only one collapsible at a time
# check consistency, only one collapsible at a time
self.collapsible1 = False
child = self.get_child2()
@@ -1193,16 +1195,16 @@ class PanedCollapsible(Gtk.Paned):
def _create_expander(self, widget):
self._expander = Gtk.Expander(label=self.collapsible_label,
visible=True)
visible=True)
self._expander.add(widget)
# connect the expanded signal
self._expander.connect('notify::expanded',
self._on_collapsible_expanded)
self._on_collapsible_expanded)
# connect the initial collapse
self._allocate_id = self._expander.connect('size-allocate',
self._initial_collapse)
self._initial_collapse)
return self._expander
@@ -1213,8 +1215,8 @@ class PanedCollapsible(Gtk.Paned):
def _collapse(self):
new_y = self.get_allocated_height() - \
self.get_handle_window().get_height() - \
self._expander.get_label_widget().get_allocated_height()
self.get_handle_window().get_height() - \
self._expander.get_label_widget().get_allocated_height()
self.set_position(new_y)
@@ -1228,9 +1230,9 @@ class PanedCollapsible(Gtk.Paned):
self._expander.set_expanded(True)
elif force == PanedCollapsible.Paned.COLLAPSE:
self._expander.set_expanded(False)
elif force==PanedCollapsible.Paned.DEFAULT:
elif force == PanedCollapsible.Paned.DEFAULT:
self._expander.set_expanded(not self._expander.get_expanded())
def get_expansion_status(self):
'''
returns the position of the expander i.e. expanded or not
@@ -1241,6 +1243,7 @@ class PanedCollapsible(Gtk.Paned):
return value
class AbstractView(GObject.Object):
'''
intention is to document 'the must have' methods all views should define
@@ -1255,28 +1258,29 @@ class AbstractView(GObject.Object):
# where abstractview is part of multiple inheritance
__gsignals__ = {
'update-toolbar': (GObject.SIGNAL_RUN_LAST, None, ())
}
}
def __init__(self):
super(AbstractView, self).__init__()
def initialise(self, source):
self.source = source
self.plugin = source.plugin
self.connect('update-toolbar', self.do_update_toolbar)
def do_update_toolbar(self, *args):
'''
called when update-toolbar signal is emitted
by default the toolbar objects are made visible
'''
from coverart_toolbar import ToolbarObject
self.source.toolbar_manager.set_enabled(True, ToolbarObject.SORT_BY)
self.source.toolbar_manager.set_enabled(True, ToolbarObject.SORT_ORDER)
self.source.toolbar_manager.set_enabled(False, ToolbarObject.SORT_BY_ARTIST)
self.source.toolbar_manager.set_enabled(False, ToolbarObject.SORT_ORDER_ARTIST)
def resize_icon(self, cover_size):
'''
resize the view main picture icon
@@ -1304,7 +1308,7 @@ class AbstractView(GObject.Object):
find a path and highlight (select) that object
'''
pass
def scroll_to_album(self, album):
'''
scroll to the album in the view
@@ -1332,33 +1336,33 @@ class AbstractView(GObject.Object):
consistent
'''
pass
def get_view_icon_name(self):
'''
every view should have an icon - subject to removal
since we'll probably just have text buttons for the view
'''
return ""
def get_default_manager(self):
'''
every view should have a default manager
for example an AlbumManager or ArtistManager
by default - use the AlbumManager from the source
'''
return self.source.album_manager
def switch_to_coverpane(self, cover_search_pane):
'''
called from the source to update the coverpane when
it is switched from the track pane
'''
selected = self.get_selected_objects()
if selected:
manager = self.get_default_manager()
cover_search_pane.do_search(selected[0],
manager.cover_man.update_cover)
manager.cover_man.update_cover)
+1
Ver Arquivo
@@ -1,6 +1,7 @@
import gi
from gi.repository import Pango
from gi.repository import Gtk
gi.require_version("Gtk", "3.0")
import logging
+14 -16
Ver Arquivo
@@ -57,6 +57,7 @@ class ShapeStar():
coords = []
from math import cos, pi, sin
step = pi / points
for i in range(2 * points):
@@ -72,7 +73,7 @@ class ShapeStar():
def layout(self, cr, x, y, w, h):
points = [(sx_sy[0] * w + x, sx_sy[1] * h + y)
for sx_sy in self.coords]
for sx_sy in self.coords]
cr.move_to(*points[0])
for p in points[1:]:
@@ -82,7 +83,6 @@ class ShapeStar():
class StarRenderer(ShapeStar):
def __init__(self):
ShapeStar.__init__(self, 5, 0.6)
@@ -107,7 +107,7 @@ class StarRenderer(ShapeStar):
# public
def create_normal_surfaces(self,
context, vis_width, vis_height, star_width):
context, vis_width, vis_height, star_width):
rgba1 = context.get_border_color(Gtk.StateFlags.NORMAL)
rgba0 = context.get_color(Gtk.StateFlags.ACTIVE)
@@ -118,7 +118,7 @@ class StarRenderer(ShapeStar):
# paint full
full_surf = cairo.ImageSurface(
cairo.FORMAT_ARGB32, vis_width, vis_height)
cairo.FORMAT_ARGB32, vis_width, vis_height)
cr = cairo.Context(full_surf)
cr.set_source(lin)
@@ -136,7 +136,7 @@ class StarRenderer(ShapeStar):
# paint empty
empty_surf = cairo.ImageSurface(
cairo.FORMAT_ARGB32, vis_width, vis_height)
cairo.FORMAT_ARGB32, vis_width, vis_height)
cr = cairo.Context(empty_surf)
cr.set_source(lin)
@@ -154,11 +154,11 @@ class StarRenderer(ShapeStar):
return full_surf, empty_surf
def create_reactive_surfaces(self,
context, vis_width, vis_height, star_width):
context, vis_width, vis_height, star_width):
# paint full
full_surf = cairo.ImageSurface(
cairo.FORMAT_ARGB32, vis_width, vis_height)
cairo.FORMAT_ARGB32, vis_width, vis_height)
cr = cairo.Context(full_surf)
if self.rounded:
@@ -196,7 +196,7 @@ class StarRenderer(ShapeStar):
# paint empty
empty_surf = cairo.ImageSurface(
cairo.FORMAT_ARGB32, vis_width, vis_height)
cairo.FORMAT_ARGB32, vis_width, vis_height)
cr = cairo.Context(empty_surf)
if self.rounded:
@@ -230,8 +230,8 @@ class StarRenderer(ShapeStar):
elif self.hints == StarRenderHints.REACTIVE:
surfs = self.create_reactive_surfaces(
context, vis_width,
vis_height, star_width)
context, vis_width,
vis_height, star_width)
# dict keys
full_key, empty_key = self._get_mangled_keys(size)
@@ -319,13 +319,13 @@ class Star(Gtk.EventBox, StarRenderer):
self.yalign = yalign
self.queue_draw()
#~ def set_padding(*args):
#~ def set_padding(*args):
#~ return
def get_alignment(self):
return self.xalign, self.yalign
#~ def get_padding(*args):
#~ def get_padding(*args):
#~ return
def on_style_updated(self, widget):
@@ -396,7 +396,6 @@ class Star(Gtk.EventBox, StarRenderer):
class StarRatingsWidget(Gtk.HBox):
def __init__(self):
Gtk.Box.__init__(self)
self.set_spacing(StockEms.SMALL)
@@ -423,12 +422,11 @@ class StarRatingsWidget(Gtk.HBox):
class ReactiveStar(Star):
__gsignals__ = {
"changed": (GObject.SignalFlags.RUN_LAST,
None,
(),)
}
}
def __init__(self, size=StarSize.SMALL):
Star.__init__(self, size)
@@ -469,7 +467,7 @@ class ReactiveStar(Star):
if self.get_rating() == 1 and star_index == 1:
star_index = 0
self.set_rating(star_index)
self.emit('changed')