Comparar commits

...

1026 Commits

Autor SHA1 Mensagem Data
David Mohammed 0d2c984564 use $HOME rather than $USER for those users who's home is not home 2015-01-06 21:06:39 +00:00
David Mohammed 3bf1747540 Update README.md 2014-12-09 15:14:16 +00:00
fossfreedom c72e2e5ca5 add latest bg translation 2014-08-25 17:03:27 +01:00
David Mohammed ecb4d48624 release v2.0 2014-07-17 21:11:20 +01:00
fossfreedom bc46b63133 fix links icons in global installation 2014-07-16 21:58:34 +01:00
fossfreedom 37510a762c fix global darker theme install and missing css file 2014-07-15 21:49:34 +01:00
fossfreedom 2d6c2a5722 corrected call from makefile to ensure translation build does not fail 2014-07-15 21:07:28 +01:00
fossfreedom eb562ed4e9 fixed makefile install for translations 2014-07-15 20:38:15 +01:00
fossfreedom 4058056b70 adjust readme for release info 2014-07-12 00:24:48 +01:00
fossfreedom a9fe7ddd27 release info in plugin files 2014-07-12 00:21:38 +01:00
fossfreedom ac52161047 latest readme 2014-07-12 00:19:35 +01:00
fossfreedom e2803df857 Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2014-07-12 00:00:30 +01:00
fossfreedom 8aa2e7105e updated coverart_artistinfo icons as per jrbastiens recommendations - issue #317 2014-07-12 00:00:08 +01:00
fossfreedom f968221bff new majority translation - ru 2014-07-11 13:48:31 +01:00
fossfreedom 90b4bf31f3 latest translation - ms 2014-07-11 07:10:15 +01:00
fossfreedom 49b355b8cf latest ko translation and run and update_all 2014-07-08 08:12:11 +01:00
fossfreedom edfd24bbd3 some fixups when adding and deleting tracks 2014-07-06 22:59:49 +01:00
fossfreedom c5042bdc57 amend readme 2014-07-06 13:00:00 +01:00
fossfreedom e6c650d7b8 replace views label with an ellipsis - issue #328 2014-07-06 12:58:20 +01:00
fossfreedom 52e3e7e2ef latest translations 2014-07-05 22:01:41 +01:00
fossfreedom 82558792b5 latest translations and remove translations where no longer majority translated 2014-07-04 19:49:40 +01:00
fossfreedom e0e5e5b812 reduce install to a users locale or if it doesnt exist then default to en_US - issue #327 2014-07-03 21:16:33 +01:00
fossfreedom 31041fa39a rework coverart display to overcome perceived GTK scrollwindow resizing bug when policy is usually set to NEVER to hide the scrollwindow scrollbars - issue #325 2014-07-03 20:41:05 +01:00
fossfreedom 1dcbc4ce07 latest translations 2014-07-01 23:03:22 +01:00
fossfreedom 003b10afa6 change entryview compact image from AspectFrame to Frame - this gives the image better balance vertically 2014-06-30 23:06:43 +01:00
fossfreedom c1fbb27b0c latest fr_CA translation 2014-06-30 12:28:31 +01:00
fossfreedom 657abe17fb update readme to reflect localisation - issue #319 2014-06-29 19:50:34 +01:00
fossfreedom ba5d9e139a add internationalization on lastfm results - issue #319 2014-06-29 19:41:42 +01:00
fossfreedom d345e75952 latest de translation 2014-06-28 23:29:18 +01:00
fossfreedom 0f20559e2f latest pt_BR translation 2014-06-28 22:11:23 +01:00
fossfreedom 5977335253 latest translations from launchpad 2014-06-28 21:44:37 +01:00
fossfreedom 6d5be5812a delay cover_size update slightly to improve usability 2014-06-28 15:18:45 +01:00
fossfreedom 5c20b3bdcc readme update 2014-06-28 10:54:57 +01:00
fossfreedom 17c871897e cleanup unneeded icons in source base 2014-06-28 10:27:28 +01:00
fossfreedom 8947aebe48 reversed shadow cover hover icons - prefer the standard white icons 2014-06-28 10:25:44 +01:00
fossfreedom 802cd5a36a various cleanups and fix display of entry-pane coverart if artist info is displayed and the entry-view is opened for the first time 2014-06-28 10:18:58 +01:00
fossfreedom 115a652a71 use more solid hover icons when covers have shadow effect 2014-06-26 21:36:12 +01:00
fossfreedom 2148fd428a corrected hover icon calculation to take into account icon spacing and padding correctly - issue #316 2014-06-26 21:11:18 +01:00
fossfreedom 9c90b0b8bb add missing files and add the alternative hover icon set 2014-06-25 23:27:14 +01:00
fossfreedom e10a75945a revamped single click icons - issue #300 2014-06-25 23:09:33 +01:00
fossfreedom b1837365a2 add ability to see and adjust what is playing - issue #296 2014-06-25 22:53:49 +01:00
fossfreedom d9dc24447c latest es and fr_CA translations - issue #288 2014-06-22 14:45:10 +01:00
fossfreedom b35f4b5615 add missing imports - issue #288 2014-06-22 10:33:02 +01:00
fossfreedom ad04ae14dd remove bool error and regen locales to capture extra message - issue #288 2014-06-22 10:30:40 +01:00
fossfreedom 8392648ffe fix propagation issues for areas that respond to button_release events - the idea being that button_release events must have a corresponding button_press event otherwise the call is invalid and comes from somewhere else - issue #298 2014-06-20 21:08:49 +01:00
fossfreedom fd07433c87 Merge remote-tracking branch 'origin/master' 2014-06-20 17:26:45 +01:00
fossfreedom a78ad58de5 fix issue with track artist filter opening when clicking on artist info panes - issue #309 2014-06-20 17:26:12 +01:00
fossfreedom 052df42b29 add missing queued to playing notification message - issue #288 2014-06-20 13:09:04 +01:00
fossfreedom cd3847ba67 latest fr_CA from transifex 2014-06-20 09:16:42 +01:00
David Mohammed cabc112d1c Merge pull request #310 from jrbastien/master
Updated monochrome coverart browser icon and original design files
2014-06-20 08:46:57 +01:00
jrbastien cfde5a42b4 Changing the monochrome coverart browser icon for the left tool bar and adding the original design files as per discussion in issue #300. 2014-06-19 21:25:37 -04:00
fossfreedom 72710425e5 latest fr_CA and it translations - issue #299 2014-06-19 20:24:32 +01:00
fossfreedom 606bb0d065 fix blink display of single click icon if moving the mouse fast between cells 2014-06-18 23:50:36 +01:00
fossfreedom 7ec7ff72e3 stop inadvertent refresh of tile display due to setting of the display text radio button on first load - issue #308 2014-06-18 19:58:39 +01:00
fossfreedom b6dd370470 new entryview icon - issue #300 2014-06-18 19:16:35 +01:00
fossfreedom 5b23026ae8 better spacing in flow appearance dialog - issue #266 2014-06-18 19:13:28 +01:00
fossfreedom e71656aeb3 fix locale display issues with the coverart info panes - issue #299 2014-06-17 20:06:00 +01:00
fossfreedom 67b5575fb4 latest it.po translation 2014-06-16 21:14:27 +01:00
fossfreedom 0a71517161 add and remove some debug trying to track down issue #307 2014-06-15 10:34:10 +01:00
fossfreedom 5735d6d6f3 added some more translator hints and ensure that these comments are correctly extracted - issue #299 2014-06-14 23:21:33 +01:00
fossfreedom c98881b676 latest translations from release 1.1 and update config files to pull the latest translation items from the source files - issue #299 2014-06-14 12:14:33 +01:00
fossfreedom 7806c91c86 incorporated recommendations from mgratton to resolve focus crashes - issue #305 2014-06-12 23:40:28 +01:00
fossfreedom a34ebbd3db better vertical pane handles - simple gradient with a smaller size. Works with dark themes and standard themes such as adwaita and ambiance 2014-06-10 21:41:03 +01:00
fossfreedom 635afa73e5 completely rewrite single click capability - issue #288 2014-06-09 22:34:40 +01:00
fossfreedom ee5b1a19ea ignore ALT-Left key to summons the quick search - issue #305 2014-06-08 09:43:28 +01:00
fossfreedom cb3b67991b replicated auto-open capability for track artist pane for artist info pane 2014-06-07 22:40:59 +01:00
fossfreedom 1504c90b4d stop using child allocated width and just use the paned position #298 2014-06-07 17:56:37 +01:00
fossfreedom 9a8efff960 more debug info to track down issue #298 2014-06-07 09:59:58 +01:00
fossfreedom 9068149d07 Merge remote-tracking branch 'origin/master' 2014-06-07 07:17:42 +01:00
fossfreedom 4decb6c260 add notification feedback that an album has been queue when single click append has been chosen - issue #288 2014-06-07 07:17:21 +01:00
David Mohammed 8303145003 Update README.md 2014-06-06 08:44:59 +01:00
fossfreedom 039a772795 fix local genre error on playing - issue #303 2014-06-06 00:08:40 +01:00
fossfreedom 84f34bcf18 hide whatplayingicon correctly 2014-06-05 21:43:53 +01:00
fossfreedom c766a4dedc fix locale tooltip for All Genres - issue #303 2014-06-05 21:37:42 +01:00
fossfreedom 4fe43a950f make the highlight a little more stylish - issue #298 2014-06-05 20:58:28 +01:00
fossfreedom 437dc66cf7 add highlight for the vertical panes to indicate that something can be selected - issue #298 2014-06-05 20:18:17 +01:00
fossfreedom d4e172aafb add highlight for the vertical panes to indicate that something can be selected - issue #298 2014-06-05 20:17:40 +01:00
fossfreedom 28a8c346c7 remove bottom display hiding option - issue #302 2014-06-04 19:45:00 +01:00
fossfreedom a4c8d23fbb fix for preferences button from plugins - issue #301 2014-06-04 19:38:44 +01:00
fossfreedom d35f173453 prelim stuff for displaying queue tracks - issue #296 2014-06-04 19:35:27 +01:00
fossfreedom 3a9589074f run pycharms optimize imports over the python modules in the project 2014-06-03 20:04:38 +01:00
David Mohammed 8cf80c99bd Update README.md 2014-06-03 17:39:25 +01:00
David Mohammed 0bbde2077c Update README.md 2014-06-03 16:30:56 +01:00
fossfreedom 519fb78c84 general cleanup - remove old rb2 code that is no longer applicable 2014-06-02 20:31:26 +01:00
fossfreedom 2d828b4587 Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2014-06-02 00:18:31 +01:00
fossfreedom 0dd34ca8f3 missing file for entryview smallwindow support 2014-06-02 00:17:56 +01:00
fossfreedom 2d6bcd02b6 add missing file for compact view 2014-06-01 23:26:18 +01:00
fossfreedom 5fa4fd391e latest readme 2014-05-26 14:08:38 +01:00
fossfreedom 535059df62 add uninstall for coverart-browser - issue #232 2014-05-26 14:04:12 +01:00
fossfreedom 6f10706ac1 support smallwindow plugin - issue #295 2014-05-25 00:13:14 +01:00
fossfreedom a5a3d6b85c reformat code according to pycharm 2014-05-24 14:52:09 +01:00
fossfreedom cae74bf6e7 entryview add-to-playing menu item. Also have only one query model for the entire source rather than as before seperate query models for entry views and main source - issue #288 2014-05-24 00:10:19 +01:00
fossfreedom 08b979fa0a entryview add-to-playing menu item. Also have only one query model for the entire source rather than as before seperate query models for entry views and main source - issue #288 2014-05-23 23:52:40 +01:00
fossfreedom 7a20522c2f view add-to-playing menu item - issue #288 2014-05-23 19:54:11 +01:00
fossfreedom 8243b2ed34 single click queue adds to playing source - issue #288 2014-05-22 23:47:28 +01:00
fossfreedom 1ea5d3dd67 Add Views button to Play Queue and and Play Queue within the views button - issue #294 2014-05-21 21:37:05 +01:00
fossfreedom 4c2ef51454 latest README updates 2014-05-21 19:50:15 +01:00
fossfreedom c7696d412c slightly delay filter whilst typing - issue #293 2014-05-21 19:26:42 +01:00
fossfreedom 82adb4653d better spacing of the coverart - issue #291 2014-05-19 22:11:25 +01:00
fossfreedom 41e80ee47b various fixups part 2 - issue #291 2014-05-19 21:28:09 +01:00
fossfreedom 87ea90d054 various fixups - issue #291 2014-05-19 21:05:54 +01:00
fossfreedom ec24905ed0 update README with the latest information 2014-05-18 23:48:35 +01:00
fossfreedom 1758a56ef2 refactored track pane code issue #291 2014-05-18 23:32:33 +01:00
fossfreedom 934606eb2f fix for autosizing problem in compact view issue #289 2014-05-18 10:39:53 +01:00
fossfreedom 043790eda2 add follow song option to properties menu together with its implementation issue #278 2014-05-18 08:41:21 +01:00
fossfreedom eed81c45a1 complete stack conversion for artist view issue #287 2014-05-17 23:49:45 +01:00
fossfreedom edf995395a change display of cover to be a stack to give it an animation effect when changing the selection - issue #289 2014-05-10 23:49:23 +01:00
fossfreedom e12d09e52e cleanup and use scrolledwindow to display image rather than custom draw - issue #289 2014-05-10 21:23:31 +01:00
fossfreedom 0ec0cd8f75 replace notebook with GtkStack - issue #289 2014-05-09 22:36:54 +01:00
fossfreedom 473b656412 ignore pycharms idea files 2014-05-05 20:33:31 +01:00
fossfreedom 22c0b68add its not unusual to not be returned similar tracks for a given search - lets reduce the display of the info box to real errors being returned from the provider 2014-05-04 11:08:56 +01:00
fossfreedom 8edf61af6a add similar genre playlist 2014-05-04 10:54:59 +01:00
fossfreedom e8aad1c034 remember the setting of the entry view mode as well as making the menu signals unique for each entry view. We need to do this otherwise clicking on a menu option will invoke the method in the wrong entry view class - e.g. SongInfo dialog fails to display in compact view mode because the signal being invoked is the full entry view class. 2014-05-03 23:16:09 +01:00
fossfreedom 4091c9be50 fix closure of artist info where the icons confused the width calculation 2014-05-03 18:38:07 +01:00
fossfreedom d2be6895f0 some cleanup 2014-05-02 23:49:42 +01:00
fossfreedom 52b8588f3f add initial capability for compact vs full entry view mode - issue #289 2014-05-02 22:43:07 +01:00
fossfreedom e7669d3117 attempt to redraw correctly to improve appearance of icon - issue #285 2014-04-17 00:23:48 +01:00
fossfreedom 1804a55426 single click queue - issue #288 2014-04-17 00:09:00 +01:00
fossfreedom b514740e19 add similar playlist capability - issue #258 2014-04-14 22:24:44 +01:00
David Mohammed d39e964bc5 Update README.md 2014-04-04 21:57:09 +01:00
fossfreedom c7f6281e11 fix locale display of window title - issue #284 2014-04-04 21:34:37 +01:00
fossfreedom 4afc22c686 add filter by composer 2014-03-29 00:28:14 +00:00
fossfreedom fb64f74663 copy coverart_rb3compat module from release1.1 2014-03-28 21:50:44 +00:00
fossfreedom 3896c62844 fix search placement so that it correctly moves when rhythmbox window is resized 2014-03-28 21:42:38 +00:00
David Mohammed 52033cb0a9 Update README.md 2014-03-22 10:05:02 +00:00
fossfreedom 48c21be900 lets remember the last genre folder chosen - issue #215 2014-03-22 10:00:58 +00:00
fossfreedom 72a903992d Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2014-03-22 09:35:31 +00:00
fossfreedom f8351aadb4 lets save custom user icon info in the cache folder not the plugin folder so that it survives a reinstall - issue #215 2014-03-22 09:35:07 +00:00
David Mohammed 29da8eda3e Update README.md 2014-03-22 00:17:33 +00:00
fossfreedom b202684e0a boost artistinfo with echonest biography - issue #272 2014-03-21 23:50:55 +00:00
fossfreedom 873571f1ab add filter buttons to artist info to allow filtering by similar artist - issue #279 2014-03-17 21:31:41 +00:00
fossfreedom 48342cdd18 prettify export dialog - issue #266 2014-03-10 23:43:49 +00:00
fossfreedom 90e02345fb prettify the browser plugin preferences - issue #266 2014-03-10 21:57:41 +00:00
fossfreedom b48731ed6a cleanup 2014-03-09 23:10:31 +00:00
fossfreedom 870b4cdd7b rework jump to playing so that all views are correctly scrolled to - issue #278 2014-03-09 23:09:46 +00:00
fossfreedom ecfdc66706 Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2014-03-09 18:53:19 +00:00
fossfreedom aa8d23e65b fix focus issues so that we don't jump back to the plugin unless we started playing from the plugin - issue #278 2014-03-09 18:52:54 +00:00
David Mohammed 5013de9127 Update README.md 2014-03-09 11:54:10 +00:00
fossfreedom 0c0d4d6c57 add support for JumpToPlaying song shortcut - issue #278 2014-03-09 11:43:36 +00:00
fossfreedom 9da85017b9 manually apply #277 patch 2014-03-06 18:31:31 +00:00
fossfreedom 4174f71436 fix track artist filter display on startup if closed 2014-02-15 22:54:53 +00:00
fossfreedom 3088768fe6 some tidyups 2014-02-15 21:24:11 +00:00
fossfreedom acb6caaba0 wrong override caused failure to check lastfm plugin presence in artist view - issue #264 2014-02-15 00:29:54 +00:00
fossfreedom 8619d73007 fix 14.04 playlists + openfolder plugin support - issue #263 2014-02-14 22:54:33 +00:00
fossfreedom 18666de36f change some of the defaults for new users 2014-02-05 23:53:23 +00:00
fossfreedom e9af41d634 rework track artist filter to mirror the artist info pane - issue #271 2014-02-05 00:02:21 +00:00
fossfreedom 9763743a95 snap shut the artist paned if too small to visually see anything useful - issue #268 2014-02-04 19:46:58 +00:00
fossfreedom b6ffa2de70 update README 2014-02-04 00:07:51 +00:00
fossfreedom 74adbe6c3f transfer darker stuff from popups.xml to popups.xml.in so that it is not overwritten when we run the translation stuff 2014-02-04 00:06:02 +00:00
fossfreedom fd42d4e601 Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2014-02-04 00:03:11 +00:00
fossfreedom 0b97a39edc add artist-info-pane implementation - issue #268 2014-02-04 00:03:01 +00:00
David Mohammed 23e2ed5297 Merge pull request #270 from jrbastien/master
Fine tuning of the lighter theme and added new darker theme.
2014-02-03 15:17:06 -08:00
jrbastien 273841ec65 Fine tuning of the lighter theme and added new darker theme. 2014-02-02 19:29:25 -05:00
fossfreedom d128149d8d this is now a python3 only install - remove the old python2 installation stuff 2014-01-26 22:01:39 +00:00
fossfreedom 9cdf49e975 fix rb2 encoding issue with last commit - issue #262 2014-01-22 21:40:49 +00:00
fossfreedom 74319826d2 correct action_name to be valid in RB3 - issue #262 2014-01-22 21:33:16 +00:00
fossfreedom afea82eee5 use rb3compat functions - issue #257 2014-01-22 20:30:24 +00:00
fossfreedom dda323ac04 fix crash for album titles that have unicode characters - issue #257 2014-01-22 20:20:23 +00:00
fossfreedom a09a25690c fix crash for non-english locales where artist is not translated by the plugin 2014-01-22 18:10:09 +00:00
fossfreedom f27ee2464c replace tabs with spaces 2014-01-20 23:59:04 +00:00
fossfreedom 18aaf08f4b fix sorting of artist view after album information such its rating has been changed - issue #257 2014-01-20 23:14:28 +00:00
fossfreedom c03166007f fix boundary issues which caused crash when moving albums to the end of the view - issue #259 2014-01-20 23:06:56 +00:00
fossfreedom 0c66334569 latest sprite image - issue #240 2014-01-20 09:11:34 +00:00
fossfreedom 1e88bab104 latest bg translation 2014-01-19 20:23:46 +00:00
fossfreedom b2eabeb8eb fix sorting issue after restart together with scroll sync issue between artist and tile view - issue #257 2014-01-19 19:46:33 +00:00
fossfreedom d93ea16d1a fix for float issues in earlier versions of rhythmbox - issue #256 2014-01-18 21:22:22 +00:00
fossfreedom ac321f7815 update README for beta 3 release 2014-01-18 14:43:05 +00:00
fossfreedom c53e6c33f8 scale tooltip to original image dimensions - issue #256 2014-01-18 14:35:15 +00:00
fossfreedom 368dda444b latest translations 2014-01-18 14:02:54 +00:00
fossfreedom d2640cf11a latest translations 2014-01-16 22:50:23 +00:00
fossfreedom b56ec0e5d9 reorder the album when the sort keys change - issue #254 2014-01-16 21:34:45 +00:00
fossfreedom 5d0d513f93 fix trusty icon offset issue 2014-01-15 23:57:53 +00:00
fossfreedom 914098f4f4 fix trusty spacing issues 2014-01-15 22:54:43 +00:00
fossfreedom 16beb62e9c fix display of icons in trusty - issue #253 2014-01-15 22:00:29 +00:00
fossfreedom c1eb1d279e redisplay pictures in iconview 2014-01-15 21:44:13 +00:00
fossfreedom f9ab0104cd remove depreciation of dialog creation 2014-01-15 20:46:44 +00:00
fossfreedom 1a7e154f64 more depreciation this time with GObject.idle_add 2014-01-15 20:16:02 +00:00
fossfreedom 4cfe692b9e fix various depreciations in trusty 2014-01-15 19:58:19 +00:00
fossfreedom d165f5fc0c updated README for beta 2 2014-01-14 21:50:24 +00:00
fossfreedom 06a7478c70 latest translations 2014-01-14 21:23:10 +00:00
fossfreedom 7d629e5bcb add a space after labels in export dialog - issue #248 2014-01-14 21:11:19 +00:00
fossfreedom 5ddf0b306c resolve misaligned sorting issues - issue #250 2014-01-14 20:46:30 +00:00
fossfreedom ade9939053 rerun translation extraction 2014-01-13 21:50:19 +00:00
fossfreedom e6f8c887c9 Merge branch 'sort'
Conflicts:
	po/fr_CA.po
2014-01-13 21:35:58 +00:00
fossfreedom db09860d08 add new sprites, fix sorting of artists column so that we cannot become unsorted and finally, fix a bug on startup showing sort key error 2014-01-13 21:33:41 +00:00
fossfreedom 1718296e46 swap sort keys for album artist and album name so that we can use those sort keys correctly - issue #249 2014-01-12 21:56:14 +00:00
fossfreedom f5a6b56c3a latest cs translation 2014-01-12 12:37:25 +00:00
fossfreedom 53ff1990d2 add new cs translation 2014-01-11 23:18:52 +00:00
fossfreedom 4e5929237f tidied up sort actions 2014-01-11 22:52:13 +00:00
fossfreedom f1f4f451b8 Merge branch 'master' into sort
Conflicts:
	po/fr_CA.po
2014-01-11 12:03:32 +00:00
fossfreedom 581c1b6c0c work in progress - connect artist sort buttons and sort viewable albums 2014-01-11 12:01:33 +00:00
fossfreedom 2cafad60b6 use the correct translator comments for px - issue #248 2014-01-10 00:14:43 +00:00
fossfreedom 1cd75c4664 give new artist icons its own config called sort_artist 2014-01-09 23:51:38 +00:00
fossfreedom 161997c0d0 use separate sort and sort order buttons for album and artist views and connect signals for these buttons to sort independently 2014-01-08 23:26:34 +00:00
fossfreedom cc32904e80 latest fr_CA translation 2014-01-08 14:54:11 +00:00
fossfreedom c0edd4895e resolve out of synch picture popup - issue #244 2014-01-07 21:53:03 +00:00
fossfreedom f9dfa0c39d add latest fr_CA translation 2014-01-05 23:42:49 +00:00
fossfreedom 7eaeebf168 set the default sort indicator on the artist sort column to be ascending - issue #245 2014-01-05 23:08:09 +00:00
fossfreedom 290f06981e rerun translation stuff to capture new lastfm translation 2014-01-05 16:05:37 +00:00
fossfreedom 87de8b9a8b check if valid lastfm connection before searching - issue #243 2014-01-05 12:25:43 +00:00
fossfreedom 7a233c2dbb display artist albums ordered by year + their name rather than the current random order - issue #241 2014-01-05 11:18:28 +00:00
fossfreedom 931135ba70 remove secondary image update for disabled toolbar icons - issue #240 2014-01-05 10:23:31 +00:00
fossfreedom adba91d077 fix shake in treeview caused by expander lines - need to add space in next column to allow the expander lines to float into - issue #239 2014-01-05 00:13:17 +00:00
fossfreedom 466112068a encode unicode string to fix crash when clicking on artist - issue #238 2014-01-04 22:57:50 +00:00
fossfreedom 767e2791cf tidied up clicking on row actions to correct display of track & cover pane together with ensure that we show the artist cover search when clicking on artists - issue #236 2014-01-04 09:00:27 +00:00
fossfreedom f60bf048b2 latest es.po 2014-01-03 23:21:42 +00:00
fossfreedom 006d621327 add english language locales 2014-01-03 21:34:09 +00:00
fossfreedom 03b01b58f2 ensure translation of Views button works correctly 2014-01-03 21:17:54 +00:00
fossfreedom e915b353cc translation clarifications 2014-01-03 04:39:00 +00:00
fossfreedom cf8e2b383e version and help info moved to v1.0 2014-01-02 11:02:31 +00:00
fossfreedom 272127f5a3 make sure we copy the plugin3 file to allow the POSTINST script to run in the rb3 debian package 2014-01-01 21:12:55 +00:00
fossfreedom d4f0e9212a corrected version and source icon display for rb3 2014-01-01 20:40:42 +00:00
fossfreedom 78546f3643 re-run translation stuff to upload to launchpad 2014-01-01 15:31:00 +00:00
David Mohammed e1cd035095 Update README.md 2014-01-01 00:56:59 +00:00
fossfreedom 07aca0cdf6 redo deletion of artists when there is no albums - this time lets do stuff simply - just look at the iters we remembered when adding the albums to artists - issue #209 2013-12-28 22:05:30 +00:00
fossfreedom 7b96b47a6c correctly rename album artist and update the artist model - issue #209 2013-12-28 19:20:14 +00:00
fossfreedom d3654e1457 bug fix crash when modifying album in artist view 2013-12-28 17:31:04 +00:00
fossfreedom 98045cae32 blank expander column allows clicking on artist cover - issue #209 2013-12-28 15:15:26 +00:00
fossfreedom 50ae5a2a99 remove export and reduce search to just the artist - issue #209 2013-12-28 12:11:01 +00:00
fossfreedom 0ca674fb7b add right click menu for artists - issue #209 2013-12-28 11:50:47 +00:00
fossfreedom 0ed6f0e3ec tidied up permissions and reduce spacing in carousel to improve look 2013-12-27 17:09:11 +00:00
fossfreedom ed1d8899be add lighter icons to the Makefile 2013-12-27 16:27:15 +00:00
fossfreedom 0c8d408688 disable flowview if webkit support is disabled 2013-12-27 09:02:16 +00:00
fossfreedom d67a802b3d updated readme 2013-12-27 08:57:50 +00:00
fossfreedom 51c05b6202 add xingmux for accurate bitrate resampling 2013-12-27 00:40:41 +00:00
fossfreedom 2a098b1047 copy po's from release 0.9 2013-12-23 00:24:24 +00:00
fossfreedom 5effd50acd latest translation updates 2013-12-23 00:19:14 +00:00
fossfreedom 729d7f1ff0 sync artist view with the other views so that the artist last selected in the other views is scrolled to correctly 2013-12-22 23:50:40 +00:00
fossfreedom 47faf9a509 reflect album name changes back into the artist view. Also took the opportunity to fix a bug specific to RB3 to ensure all track changes are correctly updated in the album-model - issue #209 2013-12-20 18:20:24 +00:00
fossfreedom 6afe9bbed5 remove naff try except by using arrays rather than generators 2013-12-18 23:35:16 +00:00
fossfreedom 6bb5f885bb bug fix when calculating album year - ignore tracks where the year is zero ... this can cause the year to be the current date because the min value returns zero rather than the min of real track years 2013-12-18 23:10:11 +00:00
fossfreedom f763b00f3a cope with locales in markup - issue #209 2013-12-18 16:45:04 +00:00
fossfreedom 7d903e84cf format album names for artist view - issue #209 2013-12-18 16:38:59 +00:00
fossfreedom 7cbf51d168 update README for toolbar changes 2013-12-18 00:06:05 +00:00
fossfreedom 4a1443cf66 link our plugin menu in the music library up with our plugin - issue #230 2013-12-16 23:55:21 +00:00
fossfreedom 7a45d9531e switch to music library source when requested from the plugin view button - issue #230 2013-12-15 16:16:22 +00:00
fossfreedom b1544a8983 temporary fix to prevent crash for RB2.96 2013-12-15 00:12:02 +00:00
fossfreedom 1ff243df66 Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2013-12-14 23:59:09 +00:00
fossfreedom 9076627ca9 rework toolbars to be RB3 visually compatible with popups placed below the buttons and themed accordingly. Also taken the opportunity to redo the right toolbar to fit on one less line since this sidebar is often used with lyrics which is wider and thus offers more space. Preliminary views button created for library source although not connected as yet. issue #230 2013-12-14 23:58:21 +00:00
fossfreedom 49d9540f8c 26 languages not 22 in the README 2013-12-07 21:24:46 +00:00
fossfreedom 5d1e68c268 more readme changes 2013-12-07 21:09:20 +00:00
fossfreedom 4692c6b8ee add rb2 support for repeat one song - issue #231 2013-12-07 20:52:53 +00:00
fossfreedom f34fd79952 revert plugin loader 2013-12-07 20:16:04 +00:00
fossfreedom 295ee80351 add rb3 menu item for repeat one song - issue#231 2013-12-07 20:15:05 +00:00
fossfreedom a508e52f0d latest README for changes 2013-12-07 17:20:43 +00:00
fossfreedom 2bda0f1cb0 reduce list of target to just uris - issue #233 2013-12-07 17:12:21 +00:00
fossfreedom 74f37075e5 cleanup 2013-12-07 15:08:18 +00:00
fossfreedom 88821ef68a drag-drop support for albums in artist view 2013-12-07 14:57:13 +00:00
fossfreedom 677855e5c7 fix cancel mechanism for artist search 2013-12-07 00:24:07 +00:00
fossfreedom d593463a25 cleanup 2013-12-06 19:39:16 +00:00
fossfreedom 66070a6b0e cleanup 2013-12-03 23:06:35 +00:00
fossfreedom 66b4895458 rework to use new coverartextdb 2013-12-02 21:49:02 +00:00
fossfreedom 2f6af4fa7a use coverview for artists as well as for albums 2013-11-24 21:19:00 +00:00
fossfreedom 80eefa8737 expand or collapse artist on single click 2013-11-23 13:27:41 +00:00
fossfreedom 5fa51b6ed3 allow drag-drop from coverview and also autoexpand when clicking on artist 2013-11-22 22:54:31 +00:00
fossfreedom 4bdd1b4809 show artist cover in a enlarged tooltip 2013-11-22 18:55:48 +00:00
fossfreedom 0c65a494d6 use download covers menu option to download artist covers 2013-11-21 22:48:50 +00:00
fossfreedom f0acad9dca also support album cover update for drag and drop events 2013-11-20 23:21:40 +00:00
fossfreedom 10b4c152c4 support drag and drop of pictures onto artists 2013-11-20 20:29:33 +00:00
fossfreedom 1eab887896 fire the load of the artists after the album model has loaded so that the artists view is correctly initialised 2013-11-17 13:00:58 +00:00
fossfreedom 616ccb2d53 add standard right-click menu for albums 2013-11-17 09:42:40 +00:00
fossfreedom 8b5e234cd2 link filters to artists so that they show or hide artists in the treeview 2013-11-16 10:28:31 +00:00
fossfreedom dfc0002a2c some cleanups 2013-11-13 21:54:30 +00:00
fossfreedom 111147ca2a remove unneeded file 2013-11-13 20:52:22 +00:00
fossfreedom 297378349c change view to be an album-artists view 2013-11-13 20:51:51 +00:00
fossfreedom 0678179585 convert adding of artists as iterators to improve visual appearance when loading 2013-11-12 22:55:51 +00:00
fossfreedom a03119ef29 disable rather than hide toolbar objects 2013-11-11 23:11:51 +00:00
fossfreedom 66ddba7d78 remove unnecessary file 2013-11-11 00:10:22 +00:00
fossfreedom 4a23b78816 update toolbar signal whenever view is initialised or moved to 2013-11-11 00:09:53 +00:00
fossfreedom 07273d8351 remove obsolete file 2013-11-08 23:50:35 +00:00
fossfreedom 1dc678f31f refactor toolbar stuff into its own module 2013-11-08 23:50:09 +00:00
fossfreedom 64f7f3a978 various cleanups and use RB.search_fold for ordering of track artists 2013-11-08 21:20:04 +00:00
fossfreedom 846e995d82 add dummy column so that main column doesnt excessively expand to fill the space 2013-11-08 13:38:35 +00:00
fossfreedom 840ef0fd68 add clickable header for sorting 2013-11-08 13:00:03 +00:00
fossfreedom c562d3c4b3 ensure startup of view has a collapsed paned and is also initialised correctly when switching to it from another view 2013-11-06 23:44:27 +00:00
fossfreedom b2aec88ccf rework to use selection changed event which is similar to how the iconview is done 2013-11-06 22:53:59 +00:00
fossfreedom 7302165293 connect click and double click events 2013-11-05 00:09:36 +00:00
fossfreedom 9502bcf528 display artist and album images in tree-view 2013-11-04 23:00:24 +00:00
fossfreedom afbc8bd3b9 display albums for artists when the artist is expanded 2013-11-04 19:37:23 +00:00
fossfreedom 958e6ad6de refactor to separate data model from the view to be analagous to how album data model is done 2013-11-03 18:07:48 +00:00
fossfreedom 7027112b58 reactivate artist button ready for new artist treeview 2013-10-31 21:57:05 +00:00
fossfreedom 2c8554d79f display message if albums do not meet threshold - issue #213 2013-10-29 17:22:57 +00:00
fossfreedom 16f6055488 make sure double click and hover play use favourites - issue #213 2013-10-29 00:28:10 +00:00
fossfreedom d09ac59971 use 'New Playlist' as the default label when adding a new playlist - issue #213 2013-10-29 00:19:27 +00:00
fossfreedom 9e83e420bc its back! trick was realising calculation needs to be in widget coords - issue #84 2013-10-28 11:13:48 +00:00
fossfreedom 7e2eb1b8f9 temporarily abandon hotspot functionality - when view is scrolled it doesnt work anyway due to the way the hotspot position is calculated. Need to investigate further - issue #84 2013-10-28 09:42:38 +00:00
fossfreedom d7b7de854a rejig to delay immediate hotspot display slightly and also to force a view redraw so that hover is displayed even if the mouse is not moving - issue #84 2013-10-28 09:24:59 +00:00
fossfreedom 025cd52e3c refine hover display - displays immediately if move cursor over the hotspot region - issue #84 2013-10-27 23:42:24 +00:00
fossfreedom ab7643d7fa turn-off tooltips if cover display-text is active - issue #228 2013-10-26 23:14:54 +01:00
fossfreedom 211f4019bb slightly reduce time interval and remove print statements 2013-10-26 23:00:43 +01:00
fossfreedom 5ccd07e4b2 rework with new button images and half-second delay before display of hover - issue #84 2013-10-26 21:36:55 +01:00
fossfreedom 7b2c529228 rework to support play-pause on hover icon 2013-10-23 21:58:19 +01:00
fossfreedom 2a28592360 rejig so that iconview controls play icon 2013-10-23 20:08:33 +01:00
fossfreedom 00e0c80594 Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2013-10-22 21:37:27 +01:00
fossfreedom a79591f9c0 ensure play icon responds to mouse hovering over it by dimming to show it is clickable - issue #84 2013-10-22 21:37:04 +01:00
David Mohammed f32713855f Update README.md 2013-10-20 17:49:03 +01:00
fossfreedom 27fdc07f28 fix multiple progress bars appearing on loading - issue #226 2013-10-20 17:36:28 +01:00
fossfreedom 8ba870184d add rb3 monochrome icon for the browser source icon - issue #225 2013-10-20 17:08:35 +01:00
fossfreedom bd9f7c52c0 update readme and available GTK version for single click play - issue #84 2013-10-19 22:37:53 +01:00
fossfreedom 3fd7fa281e single click play on hovered play-symbol shown in cover - issue #84 2013-10-19 22:06:59 +01:00
fossfreedom cafda03761 lighten the mood 2013-10-16 22:07:00 +01:00
fossfreedom 752357ad68 removed obsolete file 2013-10-13 19:49:58 +01:00
fossfreedom 1394cedbd2 latest az translation 2013-10-13 19:47:26 +01:00
fossfreedom 5bc4d5ef31 updated missing artwork images - issue #183 2013-10-12 14:05:16 +01:00
fossfreedom 97ec1dab0a ensure that text_within option does not conflict with add-shadow option 2013-10-11 21:10:16 +01:00
fossfreedom d0e3f218e0 link up coverart-text-pos with cellrenderer 2013-10-11 20:14:36 +01:00
fossfreedom e37655d7e0 add text position option to preferences and schema 2013-10-11 00:02:50 +01:00
fossfreedom d8967fb4e1 add cellrenderer for the text embed capability 2013-10-10 22:59:46 +01:00
fossfreedom b7a5a8e023 readded jrbastiens changes in popup.xml into the translation equivalent so that we dont lose the changes when we regenerate the translation files later 2013-10-07 13:30:58 +01:00
David Mohammed 57808278d3 Update README.md 2013-10-07 13:19:27 +01:00
David Mohammed 9412ddfb29 Merge pull request #222 from jrbastien/master
Introducing a new icons theme for the coverart browser
2013-10-07 05:09:55 -07:00
jrbastien d99cab53d1 Merge branch 'master' of https://github.com/jrbastien/coverart-browser 2013-10-06 20:00:46 -04:00
jrbastien 9312c37305 Adding the documentary genre as an alternate genre for the spoken genre. 2013-10-06 19:58:23 -04:00
Jean-Rene Bastien f8eb618d84 Delete Tour_Effel.png
Custom icon. Should not have been included in the theme.
2013-10-06 19:33:55 -04:00
jrbastien 3f610a74a1 Introducing a new icons theme with the goal of having something less distracting on the eyes. All icons have been redesigned but the genre sprite. 2013-10-06 19:29:13 -04:00
fossfreedom 12826cdde6 minor mods for finnish translation 2013-10-05 23:05:01 +01:00
fossfreedom c6eb082aca ensure that columns are correctly ordered - issue #219 2013-09-30 20:39:22 +01:00
fossfreedom 58815e03a9 add progress bar if required for RB3.0 - issue #189 2013-09-29 20:14:58 +01:00
fossfreedom 1380515c63 made properties option favourites and quick artist filter persistent - issue #213 2013-09-26 21:06:59 +01:00
fossfreedom 3c18ca9052 Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2013-09-23 19:45:45 +01:00
fossfreedom f9422d8a19 remove all the pointless menu label changing stuff - #213 2013-09-23 19:45:23 +01:00
fossfreedom d64f1bcaf7 change labels for favourites - issue #213 2013-09-22 10:21:13 +01:00
fossfreedom 27c386e172 fix for external plugin case sensitive issue and fix wrong group name for opencontainingfolder plugin 2013-09-17 00:01:24 +01:00
fossfreedom 5e2cec619e add new translation - pt 2013-09-16 22:49:51 +01:00
fossfreedom 8ab66a192f tidied master readme 2013-09-11 21:10:42 +01:00
fossfreedom 4e480c40fe Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2013-09-11 20:58:12 +01:00
fossfreedom 9b58df3784 add use favourites menu option to properties menu - issue #213 2013-09-11 20:57:54 +01:00
fossfreedom 34583e0086 latest translations from launchpad 2013-09-11 16:09:58 +01:00
fossfreedom 368e582fe2 fix initial display of album info on startup - issue #214 2013-09-11 08:35:00 +01:00
fossfreedom 186977f6ef ensure the version of the coverart_search_providers plugin is correct to prevent unnecessary crashes and other confusion - issue #212 2013-09-08 21:53:22 +01:00
fossfreedom ce119120b7 connect resize art option - issue #212 2013-09-08 20:43:08 +01:00
fossfreedom 6759472a2e add optional mp3 conversion with bitrate calculation - issue #212 2013-09-08 11:47:55 +01:00
fossfreedom 58ef685380 latest spanish translation 2013-08-27 20:51:31 +01:00
fossfreedom 594feaca93 latest ro translation 2013-08-27 12:45:10 +01:00
fossfreedom edcf74f005 latest az, fr & pl translations 2013-08-26 16:38:51 +01:00
fossfreedom a6993e3d2e latest tr.po 2013-08-26 10:54:07 +01:00
fossfreedom 5ccc4c1c7d Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2013-08-25 23:54:33 +01:00
fossfreedom 35335da6b8 latest translations and removed those translations that are less than 50 percent translated 2013-08-25 23:54:22 +01:00
David Mohammed ba9ae93d87 Update README.md 2013-08-25 23:45:18 +01:00
David Mohammed 6b999b0099 Update README.md 2013-08-25 23:33:53 +01:00
David Mohammed 388e8faec6 Updated - ready for release 2013-08-25 23:22:52 +01:00
fossfreedom da92ed7d9a bug fix - prevent help button being continually duplicated when preferences dialog repeatedly opened 2013-08-25 10:09:01 +01:00
fossfreedom a17a32e33b latest translations 25/08/13 2013-08-25 09:43:29 +01:00
fossfreedom 17db0b98df remove obsolete icons 2013-08-22 14:09:04 +01:00
fossfreedom d500fdb0bf latest translations 22/08/13 2013-08-22 11:52:14 +01:00
fossfreedom 69fccf10f4 latest fr_CA translations - issue #202 2013-08-18 20:06:11 +01:00
David Mohammed fa2050486e remove "sh" from the running of the bash shell script install.sh - issue #211 2013-08-18 15:57:08 +01:00
fossfreedom 3f85bc1da6 updated padding description - issue #202 2013-08-18 15:42:56 +01:00
fossfreedom 9d59cb0748 remember the position of the quick artist filter handle position - issue #210 2013-08-18 15:26:46 +01:00
fossfreedom e1aa1ba082 readd previous translations that jrbastien made - issue #202 2013-08-18 10:57:58 +01:00
fossfreedom 8ac60117ac center view buttons and allow search button to expand to its full extent - issue #202 2013-08-18 10:33:18 +01:00
fossfreedom 7179b2e601 latest fr_CA translation 2013-08-17 21:58:21 +01:00
fossfreedom e93a7dd36b Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2013-08-17 20:38:53 +01:00
fossfreedom 85df2fa79d regenerated po's using less restrictive intltool-extract 2013-08-17 20:30:27 +01:00
fossfreedom 3e55c8daa9 changed tile to tiles, tooltip swap and added translator info for 'All' 2013-08-17 20:07:16 +01:00
David Mohammed 59f1a840ec Update README.md 2013-08-15 23:02:10 +01:00
fossfreedom 74d6a62238 add help button to preferences dialog called from coverart-browser 2013-08-13 19:36:46 +01:00
fossfreedom ef657da311 remove obsolete code when right click on source in rb2.96 2013-08-11 21:31:12 +01:00
David Mohammed 250eea8053 Update README.md 2013-08-10 21:24:59 +01:00
fossfreedom f243e2aba2 add llyrics and fileorganizer as supported plugins for rb2.99 users 2013-08-10 21:17:23 +01:00
fossfreedom e47c3dedac latest translations update 2013-08-07 14:50:31 +01:00
fossfreedom 2a2a51c1ed hook up quick artist filter to properties menu item 2013-08-06 21:54:04 +01:00
fossfreedom c27ae05e3c connect up quick artist filter to album model 2013-08-06 20:43:34 +01:00
fossfreedom 00ccb84fc3 Merge branch 'master' of https://github.com/fossfreedom/coverart-browser into quickfilter 2013-08-05 21:07:30 +01:00
fossfreedom 83e73f95d5 fix missing import of RB 2013-08-05 21:06:05 +01:00
fossfreedom 154e5df152 force the state of a stateful action - this fixes the issue where an accelerator is used to activate rather than a menu option - issue #208 2013-08-05 21:04:23 +01:00
David Mohammed 5781a67a70 Update coverart_rb3compat.py 2013-08-05 13:26:01 +01:00
David Mohammed 0322dfd248 Merge pull request #207 from dmo60/patch-1
enable passing a custom stock_id for actions
2013-08-05 05:22:29 -07:00
Timo Loewe ecd1a07026 Update coverart_rb3compat.py
enable passing a custom stock_id for actions
2013-08-04 12:48:48 +02:00
fossfreedom 3273c920ec create paned layout and connect model to library source 2013-08-04 00:20:14 +01:00
fossfreedom 7b33009e67 fixed name reference for our panedcollapsible widget 2013-08-03 21:30:29 +01:00
fossfreedom 73e218e99f update readme for iconview toggle 2013-08-03 18:10:35 +01:00
fossfreedom 30a6ebeb45 add optional track-view toggle to keep consistent with flow-view 2013-08-03 18:08:15 +01:00
fossfreedom 2e27bbd794 add rb3 install instructions - issue #194 2013-08-03 13:48:52 +01:00
fossfreedom a029c43144 add option to install RB version 3 - issue #194 2013-08-03 13:46:21 +01:00
fossfreedom 474fba83c0 fix python3 depreciation warnings in ubuntu saucy - issue #187 2013-08-02 22:23:27 +01:00
fossfreedom 491d35b581 update makefile for global installation and deb creation 2013-08-02 16:14:48 +01:00
fossfreedom e7b68f91b7 uplift source files for translation activities 2013-08-02 15:01:45 +01:00
fossfreedom c348988325 each view now remembers if the track-view should be open or closed when switching between those views - issue #206 2013-08-01 23:06:06 +01:00
fossfreedom 6c76820891 relaxed check for mouse to be over the coverflow when controlling the coverflow via keyboard - issue #206 2013-07-31 10:18:17 +01:00
fossfreedom fadb361a0c remove scrollbar 2013-07-30 23:07:05 +01:00
fossfreedom 52d6828480 remove unwanted file 2013-07-30 23:02:47 +01:00
fossfreedom 9d0666144b fix resizing issue on coverflow 2013-07-30 23:02:17 +01:00
fossfreedom c9d27129b1 some python3 fixups 2013-07-30 21:20:21 +01:00
fossfreedom 5fc8e853ad move signal update out of loop so that it always fires not just when the caption is visible 2013-07-29 22:00:52 +01:00
fossfreedom 0c072028c0 add optional argument 2013-07-29 20:46:32 +01:00
fossfreedom bc3d194920 centre view buttons correctly on top-bar - issue #206 2013-07-29 14:59:07 +01:00
fossfreedom da37365b38 display active album in trackview whilst scrolling in coverflow - issue #206 2013-07-29 14:08:34 +01:00
fossfreedom ebfc12ef15 reset on_first_use counter after it actually has been through this section of code 2013-07-29 11:38:59 +01:00
fossfreedom 017312e7c5 expand pane on very first use on coverflow - issue #206 2013-07-29 11:27:08 +01:00
fossfreedom 5302cca0a1 changed label to reflect true meaning 2013-07-29 09:50:53 +01:00
fossfreedom b9f82a794f changed filter message to be more meaningful - issue #206 2013-07-29 09:48:29 +01:00
fossfreedom b9ac6a5c8a Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2013-07-28 23:52:34 +01:00
fossfreedom 1e1e6481a9 move items creation to filter_changed to be consistent with the rest of the html manipulation 2013-07-28 23:52:03 +01:00
David Mohammed 6840a52d67 Update README.md 2013-07-28 19:21:02 +01:00
fossfreedom 42fb3f650b add support to disable call to webkit library via a gsettings value - issue #199 2013-07-28 19:19:02 +01:00
fossfreedom 9130f32038 focus issues on coverflow after quick search and expand descriptions of the abstract view 2013-07-28 09:15:14 +01:00
fossfreedom 6a7b7f7146 remove obsolete code and renamed methods that are really just private to the coverflow 2013-07-28 08:59:50 +01:00
fossfreedom f4f1086883 fix for reason why rating was not being recalculating - issue #204 2013-07-27 22:37:33 +01:00
fossfreedom 14b84f1a9c lets add generic support for RB2.98 and 2.99 i.e. assume something has modified on the album 2013-07-27 17:26:56 +01:00
fossfreedom 44cf1a94c6 remove some strange code causing spurious album updates whenever anything changes - now the track modified signal is only thrown when a track artist details are changed 2013-07-27 17:19:02 +01:00
fossfreedom e348ec9b45 fix locking issue due to incorrect use of threads 2013-07-27 05:51:01 +01:00
fossfreedom 0ea4d413c3 some cleanups including reducing flickering when changing album properties or changing cover as well as fixing the quick search on the flow 2013-07-26 20:15:39 +01:00
fossfreedom 69800ebb6d move separators to more logical locations 2013-07-26 12:57:20 +01:00
fossfreedom e9c869e5ad fix typos primarily in album-updated emit signal 2013-07-26 12:53:50 +01:00
fossfreedom 34367b8e85 add jrbastiens new icons and hide the artist button 2013-07-26 09:25:42 +01:00
fossfreedom 77436aca08 fix quick search on iconview if flowview was the default on startup 2013-07-25 23:51:55 +01:00
fossfreedom d72cac5424 a bit of a hack - refilter the coverflow whenever something on an album changes such as changing the picture 2013-07-25 21:50:01 +01:00
fossfreedom 10d26bb4d3 disable default drag-drop on webview and enable the active element to receive the dropped picture from the cover pane 2013-07-24 20:36:21 +01:00
fossfreedom f9cff865bf substitute properties.png with jrbastien's version 2013-07-24 13:28:46 +01:00
fossfreedom 0e43b68a73 simplify processing, link display to flow-max number, re-add slider and circular-flow 2013-07-23 21:09:25 +01:00
David Mohammed 0541665488 Update README.md 2013-07-23 13:00:57 +01:00
fossfreedom 76a9610422 add random album functionality - issue #196 2013-07-21 10:34:27 +01:00
fossfreedom 6c09a042f9 connect automatic click setting to coverflow 2013-07-20 22:09:28 +01:00
fossfreedom 844a514ccc revert toggle buttons to text only and differentiate the label colour depending upon which is active 2013-07-20 11:07:53 +01:00
fossfreedom b991fd6993 add separator in properties menu to differentiate between preferences and other view functionality 2013-07-20 09:48:36 +01:00
fossfreedom ab47c5f97d add search providers dialog to properties button 2013-07-19 20:40:05 +01:00
fossfreedom 6a5eace53d link up plugin preferences dialog to properties button menu 2013-07-19 14:57:07 +01:00
fossfreedom 639429fed1 changed my mind - lets display albums anyway - just need to add the limit when refactoring the batch processing part 2013-07-18 22:31:13 +01:00
fossfreedom 28ee9474f8 connect max albums to preferences 2013-07-18 21:55:57 +01:00
fossfreedom b98ea85bdb add max number of albums to display in coverflow 2013-07-18 21:38:47 +01:00
fossfreedom bb02cbc035 correct names for notify properties so that they sync when preferences change 2013-07-18 14:29:34 +01:00
fossfreedom 3ef9bb6afc refactor coverflow code for properties and their signals 2013-07-18 13:14:34 +01:00
fossfreedom b3ed080e09 wrong default width for coverflow 2013-07-18 12:24:30 +01:00
fossfreedom eb5014483a slight modifications to README 2013-07-17 23:54:07 +01:00
fossfreedom 8bd4799bf4 connect startup gsettings values to various values in the coverflow index.html 2013-07-17 23:35:49 +01:00
fossfreedom a3c3631ecb link prefs dialog flow items to gsettings 2013-07-17 19:27:24 +01:00
David Mohammed e4ecbb3c11 Add LICENSE file via addalicense.com 2013-07-17 01:21:38 -07:00
fossfreedom 94f7e1045e Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2013-07-16 21:20:08 +01:00
fossfreedom a5a9096732 add check on paned release event - temporary resolution for flow click issue 2013-07-16 21:20:00 +01:00
fossfreedom 1f3f5b7df1 add GUI options to preferences - note, these have not been linked as yet to the flow 2013-07-16 13:01:36 +01:00
fossfreedom da09955aeb add check and use path instead of pixbuf when creating Cover on drag-drop - issue #195 2013-07-14 20:03:26 +01:00
fossfreedom 1fce749c3e reinstate download all covers via properties button 2013-07-13 00:02:28 +01:00
fossfreedom 1bb5e94b1e add various flow addons 2013-07-12 11:32:09 +01:00
fossfreedom f19a5d3e75 relinked the toggle with the view on startup 2013-07-11 23:47:24 +01:00
fossfreedom e479bb2983 fix picture display on top toolbar 2013-07-11 23:05:19 +01:00
fossfreedom 2d61b6ebab fix sidebar display of new buttons 2013-07-11 21:58:32 +01:00
fossfreedom 339b25633a rework toggle buttons to be image buttons and add a dummy properties button as well as a dummy artist view button 2013-07-11 20:45:16 +01:00
fossfreedom a651ac93d4 fix right click on coverflow as well as selection issues when switching views 2013-07-10 23:13:54 +01:00
fossfreedom 44a281ff99 minor mod to use scalefactor rather than maxheight for the sizing of images 2013-07-10 22:45:39 +01:00
fossfreedom 64b900f21a minor mod to use scalefactor rather than maxheight for the sizing of images 2013-07-10 22:44:04 +01:00
fossfreedom dd84f9183c various fixes to ensure that the coverflow is loaded after all covers have been loaded as well as resolving various scroll to album issues 2013-07-09 23:24:22 +01:00
fossfreedom acb86fea37 Update coverart_browser.plugin 2013-07-08 08:19:59 +01:00
fossfreedom dcc89ed8a2 now works for RB3.00 - adjusted comment - issue #186 2013-07-08 00:05:42 +01:00
fossfreedom 100a552d56 fix some python3 issues 2013-07-07 23:59:22 +01:00
fossfreedom f96bc30716 tidyup playlist monitoring code 2013-07-07 22:51:32 +01:00
fossfreedom 727165a9d5 assume the shell display-page-model changes can be used to monitor playlist events as well - issue #192 2013-07-07 22:24:15 +01:00
fossfreedom b6bb7eaeb6 fix playlist crash in RB2.99 - issue #191 2013-07-07 21:56:56 +01:00
fossfreedom 6002454340 tie two views to clickable toolbar radio-buttons 2013-07-07 15:14:07 +01:00
fossfreedom 023ebcf1d3 must remember to at least run RB to check for crashes... 2013-07-07 08:58:11 +01:00
fossfreedom 4a3add381b fix for fedora19 crash #191 2013-07-07 08:51:23 +01:00
fossfreedom 206f574cdf delegate switching of views to themselves and better sync between views 2013-07-06 07:41:43 +01:00
fossfreedom 9789b697f0 sync selected albums when flipping between views 2013-07-05 21:48:10 +01:00
fossfreedom 0116413aa2 connect views to viewmanager via a schema key 2013-07-04 21:59:31 +01:00
fossfreedom 2b03440adc ensure new batch is passed the album identifier 2013-07-04 13:24:18 +01:00
fossfreedom d22355d29d refactor iconview specific methods from abstractview 2013-07-04 12:38:59 +01:00
fossfreedom 1ccde3b5d4 fix constant dragging issue after right-click menu is removed from the display 2013-07-03 23:09:21 +01:00
fossfreedom a10dc87ef0 add rudimentary right-click support 2013-07-03 22:26:46 +01:00
fossfreedom 828787dc8d added rudimentary response to click and double click events 2013-07-02 23:49:57 +01:00
fossfreedom bc7052f671 link coverflow with albummodel 2013-07-02 21:36:46 +01:00
fossfreedom afc4a2939c bug fix - playlists were never updated on the playlist menu 2013-07-02 21:35:59 +01:00
fossfreedom 5bad5fa5e2 various RB3 & python3 issues 2013-07-01 21:07:51 +01:00
fossfreedom 452de7f834 integrate coverflow folder structure using initial prototype code 2013-06-30 20:32:18 +01:00
fossfreedom 205fa02435 update supported external plugins 2013-06-30 12:08:48 +01:00
fossfreedom 791787559b restructure so that we have a definitive list of methods a view should have and create a ViewManager class which will eventually manage our different views 2013-06-29 23:35:24 +01:00
fossfreedom 70575fb308 add space between : to cope with french locale requirements - issue #185 2013-06-25 21:38:18 +01:00
fossfreedom 7360482c9d add space between : to cope with french locale requirements - issue #185 2013-06-25 10:41:00 +01:00
fossfreedom d05231de82 latest az translation 2013-06-22 16:04:18 +01:00
fossfreedom 8b38ae236e move cover view specific stuff from covert_album to the cover icon view module 2013-06-22 00:09:09 +01:00
fossfreedom 0ed9d54e22 regeneration of plugin and popups 2013-06-21 20:36:46 +01:00
fossfreedom e7d7efaf61 readd copyright statement 2013-06-21 20:36:14 +01:00
fossfreedom 4cac3ed47d latest translations in release-0.8 2013-06-21 20:32:50 +01:00
fossfreedom 777e78fbc4 more translations for zh_CN 2013-06-21 19:35:02 +01:00
fossfreedom e5e66a1f24 fix change selection 2013-06-21 19:33:30 +01:00
fossfreedom 6aaee47cb6 latest zh_CN translation 2013-06-21 15:57:06 +01:00
fossfreedom db028fa7c8 latest zh_CN translation 2013-06-21 09:23:13 +01:00
fossfreedom 2f50a382ea move icon notify gsetting signals to new module 2013-06-20 23:58:52 +01:00
fossfreedom 60c6cba7cf move signals for iconview to new module 2013-06-20 23:41:22 +01:00
fossfreedom 424f18799d drag drop refactoring 2013-06-20 23:25:04 +01:00
fossfreedom b12423ca63 new translations - bs, el and fi 2013-06-20 22:38:36 +01:00
fossfreedom f6c23576a0 refactored covericonview specifics into its own source module 2013-06-20 22:02:55 +01:00
fossfreedom 5c1736cb04 Update README.md 2013-06-19 21:04:52 +02:00
fossfreedom ebe6678e43 latest pl translation 2013-06-19 09:29:47 +01:00
fossfreedom 6ab0bcecd3 cherry pick 97e1f79ad5 2013-06-18 23:53:48 +01:00
fossfreedom b0b6a09100 Merge branch 'release-0.8' of https://github.com/fossfreedom/coverart-browser into release-0.8 2013-06-18 23:42:53 +01:00
fossfreedom 97e1f79ad5 fix for crash if using BPM column - issue #188 2013-06-18 23:42:19 +01:00
fossfreedom 302048e93b Update README.md 2013-06-18 22:16:05 +02:00
fossfreedom 157cc0359b added new romanian translation 2013-06-18 10:00:37 +01:00
fossfreedom 85b76af44a Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2013-06-17 22:43:45 +01:00
fossfreedom 6f29b47576 python3 fixes for git compiled version of RB running on saucy 2013-06-17 22:43:25 +01:00
fossfreedom 45aa5281e2 remove obsolete param in constructor call 2013-06-17 11:52:28 +01:00
fossfreedom 6f4ff35547 revert python3 requirement 2013-06-17 11:51:45 +01:00
fossfreedom b578d8f386 fixed some errors that prevented plugin initialisation under python3 2013-06-15 16:55:27 +01:00
fossfreedom 8d40dad422 Update README.md 2013-06-13 12:37:03 +02:00
fossfreedom c7d6dc3481 resync from release-0.8 branch 2013-06-13 11:26:37 +01:00
fossfreedom 953a159018 latest pl translation 2013-06-13 11:19:17 +01:00
fossfreedom 68aacc4f72 latest az translation 2013-06-10 15:35:24 +01:00
fossfreedom 1dad8ae6d3 sync with lastfmqueue changes 2013-06-08 11:32:32 +01:00
fossfreedom 6860b4d4a3 latest polish translation 2013-06-06 20:05:08 +01:00
fossfreedom 28e6ad9a04 pull in changes whilst working on lastfm_queue 2013-06-05 18:00:25 +01:00
fossfreedom c2260d6806 fix display of accelerators in rb299 2013-05-31 16:30:25 +01:00
fossfreedom 6ffdc361b3 add support for RB2.99 view app menu as well as the default tools app menu 2013-05-31 00:01:06 +01:00
fossfreedom 4e8a081cad added API to associate accelerators with actions 2013-05-30 17:32:55 +01:00
fossfreedom 193bd37572 Update README.md 2013-05-28 21:05:23 +02:00
fossfreedom e666803e2d Update README.md 2013-05-28 21:04:36 +02:00
fossfreedom d6128831c4 remove spurious .orig files 2013-05-28 19:50:03 +01:00
fossfreedom 3277635a97 merge 2013-05-28 19:47:20 +01:00
fossfreedom cd1a6d1f91 add header to compat module 2013-05-28 19:20:34 +01:00
fossfreedom ce452dbba5 add app action toggle 2013-05-26 08:51:15 +01:00
fossfreedom cf0b264062 fix rb299 action_name vs label override issue 2013-05-25 21:32:15 +01:00
fossfreedom e95e4a0af7 fix tabs vs spaces and oops with send-first action name 2013-05-25 20:17:31 +01:00
fossfreedom c851f3d913 fix some cleanup issues 2013-05-25 16:48:36 +01:00
fossfreedom 83168a14bd bug fix to display browser menu items enabled in rb299 2013-05-25 16:07:40 +01:00
fossfreedom b792a42371 finalised RB299 functionality and completed code definitions - issue #171 2013-05-25 11:15:44 +01:00
fossfreedom dc0b9a113b fix menu crash in rb299 2013-05-25 00:04:40 +01:00
fossfreedom 77a3867152 bug fixes for rb296 after rework of Action object 2013-05-24 22:38:04 +01:00
fossfreedom 6fb90792af add new ast translation 2013-05-22 10:10:20 +01:00
Agustin Carrasco dabb80dc65 Merge branch 'order-keys'
Added support for multiple ordering on some order types (to allow a
better subordering inside each type).

closes #138
Conflicts:
	coverart_album.py
	coverart_browser_source.py
	coverart_utils.py
2013-05-22 00:46:25 -03:00
Agustin Carrasco 8855b85653 Merge branch 'refactor-statusbar' into order-keys 2013-05-22 00:20:26 -03:00
fossfreedom ccff276ab0 fixed app menu support for rb299 2013-05-21 23:50:06 +01:00
fossfreedom 8f42a9ea71 add label support for rb296 actions 2013-05-21 20:57:17 +01:00
fossfreedom c094a8f445 made menu more generic by removing the dependency on the source for the Menu creation 2013-05-20 23:56:30 +01:00
fossfreedom ae44b76a41 bug fixes for rb299 support 2013-05-20 23:02:16 +01:00
fossfreedom 263df5c62c finish restructuring for rb296 2013-05-19 23:39:56 +01:00
fossfreedom c7f8fe0ded fixed activation events for rb296 2013-05-19 21:03:19 +01:00
fossfreedom 3bd947bf16 preliminary rework to external plugins to support rb299 2013-05-18 23:51:34 +01:00
Agustin Carrasco 65de81ebc0 statusbar: removed extra space on status text 2013-05-16 22:52:35 -03:00
Agustin Carrasco a4c8cc4258 statusbar: finished refactoring statusbar (issue #123) 2013-05-16 22:39:48 -03:00
fossfreedom bc59cd85a2 rework external plugin support to use XML datafile instead of python classes 2013-05-15 19:34:38 +01:00
fossfreedom 08dc4ee2e1 config file used to describe external plugins 2013-05-14 20:08:11 +01:00
fossfreedom c098d0a5ac latest changes made by coverart-search-providers 2013-05-14 20:07:28 +01:00
fossfreedom 419b95d49c Update README.md 2013-05-13 00:52:46 +02:00
fossfreedom 8f3c66b726 Update README.md 2013-05-13 00:51:40 +02:00
Agustin Carrasco 35b2bda943 statusbar: start isolating statusbar behavior in a couple of classes 2013-05-11 23:46:57 -03:00
fossfreedom 7c584aa1e3 working playlists - issue #171 2013-05-10 23:15:43 +01:00
fossfreedom d1583f78f5 latest translations from launchpad 2013-05-10 13:57:01 +01:00
fossfreedom fd4f1a86fd add structure around playlist support - need to debug playlist creation and test against older versions of rhythmbox to ensure restructuring hasnt broken extant capabilities - issue #171 2013-05-08 23:26:38 +01:00
fossfreedom 8d58c448e8 converted entry-view menu - issue #171 2013-05-06 09:07:59 +01:00
fossfreedom 83f0194f2f first cut of rb2.99 support - lots of stuff still not working such as playlists, external plugins and track-view still to be converted - issue #171 2013-05-05 17:12:32 +01:00
fossfreedom 6347abcd2f fixed typos 2013-04-28 22:58:40 +01:00
fossfreedom 09c73972a5 fix missing merge 2013-04-28 11:52:41 +01:00
fossfreedom e9f16ab6b3 merge from master 2013-04-28 11:43:47 +01:00
fossfreedom 0a012d97bc convert pure list-map items to loops to prevent wasteful list generation - issue #179 2013-04-27 23:29:54 +01:00
Agustin Carrasco d674618904 albums: fix dumb name collision on sort_keys 2013-04-27 17:00:37 -03:00
Agustin Carrasco 9349e965c3 albums: added internal support for multiple propery sorting (issue #138)
Had to do some little changes to make this work on the AlbumModel:
- The album_sort and album_sort_artist were returning a redundant list
  that made ordering kinda quirky. Used the uniqyfy function to make
  them return a more sane result.
- Added a sort_keys dictionary to translate the different sort keys into
  a group of properties to sort by. This may have to change in the
  future to make it more mantianable.
- Changed the sort method to use the sort_keys when creating the sort
  fuction for the sorted collection.
2013-04-27 16:47:22 -03:00
Agustin Carrasco 0a6dc5620b utils: added a uniqyfy_and_sort function that removes duplicates and sorts an iterable 2013-04-27 16:40:01 -03:00
fossfreedom f78c77c98d Update README.md 2013-04-25 15:36:52 +02:00
fossfreedom 2c025d32f8 Update README.md 2013-04-25 15:36:27 +02:00
fossfreedom aa6144d575 Update README.md 2013-04-25 15:34:39 +02:00
fossfreedom 37f1cfd632 adjust help hyperlink to release-0.8 2013-04-24 23:45:12 +01:00
fossfreedom c38bdf0608 necessary changes to allow plugin to work in global install location 2013-04-24 23:07:27 +01:00
fossfreedom 2550d46f15 necessary changes to allow plugin to work in global install location 2013-04-24 23:06:05 +01:00
fossfreedom c868c1a44e rework specific python2 to 3 function issues - issue #179 2013-04-24 20:55:46 +01:00
fossfreedom 9aa78d4c6a latest polish translation 2013-04-24 12:51:20 +01:00
fossfreedom 059f8a937b Update README.md 2013-04-23 14:00:51 +02:00
fossfreedom 204ff93a59 Merge branch 'release-0.8' of https://github.com/fossfreedom/coverart-browser into release-0.8 2013-04-22 20:17:02 +01:00
fossfreedom f6d83086c4 remove less than 50 percent translated translations 2013-04-22 20:16:21 +01:00
fossfreedom a100ed29da latest polish translation 2013-04-22 20:06:24 +01:00
fossfreedom 555d6490b5 Update README.md 2013-04-22 20:48:30 +02:00
Agustin Carrasco ae6042e228 albums: Added a check to save_to_disk property before adding entry
Doing this prevents that a "trancient" entry gets added to an Album.
This kind of entries usually come from external devices, which generally
generates duplicates on the album track list. (issue #169)
2013-04-22 19:40:23 +01:00
Agustin Carrasco aa44101ed5 albums: Added a check to save_to_disk property before adding entry
Doing this prevents that a "trancient" entry gets added to an Album.
This kind of entries usually come from external devices, which generally
generates duplicates on the album track list. (issue #169)
2013-04-21 23:29:22 -03:00
fossfreedom c6099bb89d a quick 2to3 run to see what breaks when running under python2.7 - this will give a better idea of how to keep both python 2.7 and python3 compatibility - issue #179 2013-04-21 20:43:03 +01:00
fossfreedom 86b25086ac updated spanish translations 2013-04-21 18:25:59 +01:00
fossfreedom 70e8139b48 update readme for release v0.8 2013-04-21 18:17:08 +01:00
fossfreedom c0a5d2566f updated readme for v0.9 2013-04-21 18:14:04 +01:00
fossfreedom 9f4b104322 Merge branch 'viewadvanceoptions' 2013-04-21 17:45:56 +01:00
fossfreedom ab60fb0eb3 latest translations 2013-04-20 23:09:02 +01:00
fossfreedom 292201f926 change the position and icon of the preferences wiki link button 2013-04-20 23:02:02 +01:00
fossfreedom 522cdb25f8 add advance options for iconview spacing and padding 2013-04-20 21:44:55 +01:00
fossfreedom 362abfbedc regenerate translation pot file 2013-04-20 11:53:38 +01:00
fossfreedom 2be1cf05b1 remove unneeded markup tooltips 2013-04-20 11:50:33 +01:00
fossfreedom 82777c340c add link to wiki 2013-04-19 12:51:56 +01:00
fossfreedom 207c3ee97d latest pt_BR translation 2013-04-19 12:51:32 +01:00
fossfreedom 6a5ef715e5 Update README.md 2013-04-19 00:54:30 +02:00
fossfreedom 8be7155fd0 changed launchpad credit to be visible only on request via info button - issue #168 2013-04-18 23:37:30 +01:00
fossfreedom a395c00df8 display translators on preferences dialog - issue #168 2013-04-18 21:50:47 +01:00
fossfreedom e0ef6f040d add translator-credits 2013-04-18 20:16:44 +01:00
fossfreedom 3d41829949 Merge branch 'master' of http://github.com/fossfreedom/coverart-browser 2013-04-18 08:14:02 +01:00
fossfreedom 9c142c6036 latest es.po translation 2013-04-18 08:13:42 +01:00
fossfreedom 6cb11029b1 Update README.md 2013-04-17 23:56:08 +02:00
fossfreedom 449d0375de Update README.md 2013-04-17 23:55:58 +02:00
fossfreedom e880bba103 corrected help link - this will be version defined 2013-04-17 22:21:05 +01:00
Agustin Carrasco c4504c274d Damn, I keep getting it wrong 2013-04-17 08:21:02 -03:00
Agustin Carrasco 0c70ecdb6b Oops, wrong link 2013-04-17 08:18:45 -03:00
Agustin Carrasco edef1f564e Updated url for asermax fattr button 2013-04-17 08:12:20 -03:00
fossfreedom 33820cfeb9 make sure preferences use translation locale before building - issue #176 2013-04-17 10:45:23 +01:00
fossfreedom 50b7c1f936 Update README.md 2013-04-16 20:45:49 +02:00
fossfreedom 8c756fa01a add support for WikipediaSearch plugin 2013-04-16 16:56:43 +01:00
fossfreedom acb5ff7061 prevent crash if search providers plugin is not installed - issue #159 2013-04-16 15:30:13 +01:00
fossfreedom 6af7e36af8 latest launchpad translations 2013-04-16 09:40:39 +01:00
fossfreedom bf56c47c3e latest translations 2013-04-15 12:33:13 +01:00
fossfreedom f3f389006a latest translations from launchpad 2013-04-14 13:43:15 +01:00
fossfreedom 8f18571626 regenerate after asermax ui translate change 2013-04-14 11:13:45 +01:00
Agustin Carrasco ae8f3a5da3 bugfix ui: added translatable marter to the collapsible pane label
Issue #123
2013-04-13 20:17:55 -03:00
fossfreedom 8fcf0f6712 update README for new coverart export and embed functionality 2013-04-13 23:49:02 +01:00
fossfreedom 9ce6e8c7d9 regenerate po for new coverart export and embed strings 2013-04-13 23:46:55 +01:00
fossfreedom aa4904ded7 minor typo 2013-04-13 23:45:51 +01:00
fossfreedom da31e95cab Merge branch 'master' of http://github.com/fossfreedom/coverart-browser 2013-04-13 23:45:02 +01:00
fossfreedom 198426e55e implementation of coverart export and embed functionality - issue #159 2013-04-13 23:44:40 +01:00
fossfreedom ef2ff02b28 add help button on preferences 2013-04-09 09:41:01 +01:00
Agustin Carrasco 3744855057 albums: fixed bug introduced when changed to using _sort properties for
sorting: whenever an album would need to get deleted (because it was
empty), it would fail since the key using for sorting and finding an
element inside the sorted structure was given by the _sort key. The
_sort key was naively decided to be obtained from the first track of the
album, and since the album is empty, it would raise a IndexError.
2013-04-07 16:51:29 -03:00
fossfreedom f296c644a6 drag and drop coverart onto playlists and sources 2013-04-06 12:18:36 +02:00
fossfreedom cd665e2bb9 change the drag icon 2013-04-06 00:06:44 +01:00
fossfreedom 35c02421a5 seems playlists are working now 2013-04-05 23:18:55 +01:00
fossfreedom dcc3dc3187 basic support for drag and drop from coverview - issue #169 2013-04-05 23:02:25 +01:00
fossfreedom 75b9a540fe minor mods 2013-04-03 21:00:34 +01:00
fossfreedom 1d639d6730 remove automatic enablement of plugin 2013-04-03 21:00:17 +01:00
fossfreedom ca1fbcd899 add lLyrics to supported external plugins 2013-04-03 20:59:32 +01:00
fossfreedom 25940e3485 minor mods 2013-04-03 20:12:29 +01:00
Agustin Carrasco 2438cbdd08 widgets: fix bug on EnhancedIconview where the selected objects were returned in reversed order 2013-03-31 23:31:33 -03:00
Agustin Carrasco bab9311225 On play album: use the queue_selected_album instead of copying the entryview query (issue #130) 2013-03-31 23:31:09 -03:00
Agustin Carrasco 76215de1b2 Changed the method to play entries/albums to replace the source's query model
instead of queuing the entries into the play queue (issue #130)
2013-03-31 23:16:12 -03:00
fossfreedom 3c6a3e8c4d queue remaining tracks in entry view on playing - issue #130 2013-03-31 23:53:54 +01:00
fossfreedom eddfe978a3 Merge branch 'master' of http://github.com/fossfreedom/coverart-browser 2013-03-31 11:53:49 +01:00
fossfreedom a38fc28763 latest french translations 2013-03-31 11:53:32 +01:00
fossfreedom 9bf3f333ea Update README.md 2013-03-30 17:06:25 +00:00
fossfreedom 6d2d45b518 add file-organizer support - this is pending a pull request being accepted. It it isnt, this will need to be backed out before release 0.8 2013-03-29 20:20:37 +00:00
fossfreedom 408f78d634 latest fr_CA translation 2013-03-29 16:57:01 +00:00
fossfreedom ced468ac65 fix external module name for opencontainingfolder - issue #147 2013-03-29 11:25:47 +00:00
fossfreedom de9bdf67e3 Merge branch 'master' of http://github.com/fossfreedom/coverart-browser 2013-03-28 23:40:46 +00:00
fossfreedom 8859688606 initial support for external plugins - issue #147 2013-03-28 23:40:37 +00:00
Agustin Carrasco ccd8f10786 widgets: added some documentation for the EnhancedIconView widget. 2013-03-28 19:53:43 -03:00
Agustin Carrasco fe159d07b7 Merge branch 'collapsible-paned'
Refactored code that managed the paned behavior of the source main view on it's
own widget, which also takes care of the expandable on one of it's children.
This new widget abstracts the storing of the paned handle position, handles the
expandable child and handles the workaround to keep the handle in a fixed place
(when the expandable child is collapsed).
2013-03-28 19:10:21 -03:00
Agustin Carrasco 4057318255 widgets: added some more comments to the CollapsiblePaned widget 2013-03-28 19:10:08 -03:00
fossfreedom 3f47a48f71 fix tooltip - issue #168 2013-03-28 10:16:55 +00:00
fossfreedom 2d24ca1d38 latest translations 2013-03-27 12:37:36 +00:00
fossfreedom 039330dbae embed GtkArrows - issue #167 2013-03-26 09:54:40 +00:00
fossfreedom 0590acc605 embed GtkArrows - issue #167 2013-03-26 09:53:27 +00:00
fossfreedom 2a881319e3 Update README.md 2013-03-25 23:52:34 +00:00
fossfreedom fabe82c919 add comments to methods 2013-03-25 23:45:28 +00:00
fossfreedom 0a32a19aec separated providers plugin from coverartbrowser 2013-03-25 19:51:41 +00:00
fossfreedom 62ac5f5a15 latest translation bits 2013-03-25 16:44:24 +00:00
fossfreedom 9889e2ecb8 slight modifications to label positions - issue #151 2013-03-25 15:56:40 +00:00
fossfreedom d3e9961a56 revert excessive cleanup - issue #151 2013-03-25 11:03:57 +00:00
fossfreedom c8624ea86f move load icons and fix system genre lookup match - issue #151 2013-03-24 23:47:55 +00:00
fossfreedom 6e2bd1f650 tidy up genre gui - issue #151 2013-03-24 17:33:01 +00:00
fossfreedom dbdadac043 makes sense for delete button to use the remove icon to complement the add icon 2013-03-22 13:33:21 +00:00
fossfreedom cd9d36f688 add labels, change save icon and move load icons button - issue #151 2013-03-22 13:24:29 +00:00
fossfreedom 7993695a11 cleanup files 2013-03-21 23:30:58 +00:00
fossfreedom ac861a82b5 Merge branch 'master' into customgenre 2013-03-21 23:27:02 +00:00
fossfreedom 69c5979fb6 new genre gui and connect GUI changes with plugin controller 2013-03-21 23:24:39 +00:00
Agustin Carrasco b1d598b762 Added some comments and rewrote a function to reuse a portion of code instead of repeating it 2013-03-20 13:07:59 -03:00
fossfreedom 0db85fe051 background code and gui for alternative genre icons 2013-03-09 07:33:57 +00:00
Agustin Carrasco 6dbce1846e Finished porting some stuff to the CollapsiblePaned widget and removed now obsolete code from coverart_browser_source 2013-03-08 12:29:00 -03:00
Agustin Carrasco f336b2f64e widget: updated the PanedCollapsible and replaced the GtkPaned on the interface. Now it supports the basic expand/collapse functionality and blocking the handle when collapsed 2013-03-05 22:20:07 -03:00
fossfreedom d6028ca00c Update README.md 2013-03-04 12:17:05 +00:00
fossfreedom db8123d960 Merge branch 'master' into artistorder 2013-03-04 12:11:48 +00:00
Agustin Carrasco 6845ebc3b9 CoverartRequester: remove debugging prints 2013-03-02 21:51:39 -03:00
Agustin Carrasco 71cc9976de CoverRequester: modified the locking mechanism, to avoid the call to next from the same album search or timeout to be processed twice 2013-03-02 21:51:39 -03:00
Agustin Carrasco 97634dddd5 This kind of problems make my heard hurt~
This reverts commit 403d1f51bb.
2013-03-02 21:49:48 -03:00
fossfreedom 7ead18fc5d use sort fields in preference to actual fields - issue #161 2013-03-02 15:08:47 +00:00
fossfreedom 3fbc91fb57 Update README.md 2013-02-25 19:26:21 +00:00
fossfreedom 8c8cfcdc6d bug fix - ensure queuing is sorted first by disc number and then by track number - issue #160 2013-02-25 16:29:41 +00:00
fossfreedom dc9d0332c3 lets ensure both plugins are installed 2013-02-24 16:44:01 +00:00
fossfreedom a63c513423 remove duplication of code with helper functions - issue #149 2013-02-23 23:30:51 +00:00
fossfreedom 334976832b move theme logic to toolbar from toolbar manager - issue #149 2013-02-23 17:49:36 +00:00
fossfreedom ff5ccf49f7 add new signal key for buttons to update image - issue #149 2013-02-23 17:39:47 +00:00
fossfreedom 5827b32e54 move theme control to the Toolbar manager rather than on the controller - issue #149 2013-02-23 17:02:38 +00:00
fossfreedom 677fb805a9 update button theme when user changes - issue #149 2013-02-23 07:48:21 +00:00
fossfreedom 7de5cb2940 few tidyups from icon merge 2013-02-22 09:40:17 +00:00
fossfreedom 94e639ef6c Merge pull request #162 from jrbastien/master
Enhancements to standar icons (issue #149)
2013-02-22 00:00:18 -08:00
jrbastien b7a6347a5f Issue #149. New light and dark team + minor tweak to the sprites genre on standard theme. 2013-02-21 21:23:30 -05:00
fossfreedom 403d1f51bb reset 2013-02-19 16:25:33 +00:00
fossfreedom 28aae8aad1 Merge branch 'cover_search_queue' 2013-02-19 16:18:58 +00:00
fossfreedom f6dc74e7d2 reworked from dialog to window and narrowed buttons with image arrows - issue #126 2013-02-18 21:16:56 +00:00
Agustin Carrasco 4c6b3b12ab PanedCollapsible: set options to the current expander 2013-02-18 13:17:54 -03:00
Agustin Carrasco a1eeaaa037 PanedCollapsible: fixed typo and save expander as object variable 2013-02-18 13:11:22 -03:00
Agustin Carrasco 3ce9a8dc85 OptionsListViewWidget: cleaned code formatting 2013-02-18 13:05:11 -03:00
Agustin Carrasco c9fd8cf418 CoverRequester: the queue was working like a stack actually... 2013-02-18 12:59:25 -03:00
fossfreedom e498d3dd92 add button click pageup/down and focus stuff -- issue #126 2013-02-18 15:58:45 +00:00
fossfreedom 3e19f0555a autoscroll - issue #126 2013-02-17 23:39:35 +00:00
fossfreedom 8d56ad73a8 change from selection to button_release to allow hover selection - issue #126 2013-02-17 22:43:52 +00:00
Agustin Carrasco 01e3762c86 ProxyPopupButton: increase the min fixed limit of options to 25 2013-02-17 16:06:57 -03:00
Agustin Carrasco c99eff7af7 CoverManager: reimplemented the requesting mechanism into a different class that makes use of a queue instead of independent process. This gives the requesting process more flexibility 2013-02-17 03:27:08 -03:00
Agustín Carrasco e5e3d935ae Merge pull request #158 from jrbastien/master
Enhancements to standard icons
2013-02-16 19:36:51 -08:00
Jean-Rene Bastien 2853dc507d Update img/popups.xml
Enhancements to standard icons.  Issue #149
2013-02-16 21:26:17 -05:00
jrbastien 02231061d6 Improvements to calendar icons and default genre icon look. 2013-02-16 21:18:18 -05:00
fossfreedom 594a399b0c add theme support - issue #149 2013-02-13 22:13:01 +00:00
Agustin Carrasco 6ca4a5d3a2 PanedCollapsible: initial implementation of the Paned container with a collapsible child. 2013-02-11 20:07:47 -03:00
Agustin Carrasco 41d3cd8cfc CoverManager: fix bug on the timeout routine for the cover search 2013-02-10 04:51:55 -03:00
Agustin Carrasco 650c2320dc CoverManager: fix bug on the timeout routine for the cover search 2013-02-10 04:23:03 -03:00
Agustin Carrasco f2f4e75670 CoverManager: removed the timer module and replaced the timeout managment from the Source with an internal timemout on the search cover routine 2013-02-10 03:22:55 -03:00
Agustin Carrasco 42ddff79e1 Merge branch 'enhanced-iconview'
Introduced the EnhancedIconview widget, which enhances the iconview capability in some senses:
* Adds a signal for a single click on a icon.
* Fixes column reallocation on width change.
* Adds a popup to the widget, that gets fired when right clicking on an icon.
* Adds a new property to indicate the column of the model that contains the actual object to which the icon and other info belongs to. Through this property, a new method get_selected_objects allows to retrieve those objects corresponding to the current selection on the iconview.
* Adds a method to select and scroll to a given path on the iconview.
2013-02-09 22:18:26 -03:00
Agustin Carrasco 30a8c8322e EnhancedIconview: refactored the code to select and scroll to a path on the iconview.
* Moved the code to select and scroll to the a path from the source to the iconview.
* Refactored the AlbumQuickSearchController to use this new capability directly insted of through the source.
2013-02-09 22:16:41 -03:00
Agustin Carrasco 01c2f57f3b CoverartSource: replaced the get_selected_albums method for the new get_selected_objects method of the EnhancedIconview class 2013-02-07 23:55:32 -03:00
Agustin Carrasco 8942dac647 EnhancedIconview: fixed the get_selected_objects methods, it wasn't managing the model correctly 2013-02-07 23:54:50 -03:00
Agustin Carrasco 7888186e0c CoverartSource: fixed a issue with the second click bottom pane expand. It wasn't working correctly after a shift or ctrl multi selection 2013-02-07 23:52:34 -03:00
Agustin Carrasco 161eb5714b Removed a signal that isn't used anymore 2013-02-07 23:40:53 -03:00
Agustin Carrasco 20388f54a7 Gonna start moving up those methods that got refactored to make it easier to identify those that still need to be looked at 2013-02-07 23:29:28 -03:00
Agustin Carrasco b5d9a53d44 Removed old, unused method 2013-02-07 22:27:00 -03:00
Agustin Carrasco b00e3e6949 CoverartSource: refactored the click callback using the new capabilities of the EnhancedIconview.
Selecting the path on right click and showing the popup is taken care on the new iconview, the only left to do in the source is the second click detection to show/hide the bottom pane.
2013-02-07 22:20:43 -03:00
Agustin Carrasco 61b7c6386e EnhancedIconview: made some fixes on the do_button_press_event method.
* The item-clicked event now send the event two
* The call to the parent class do_button_press_event is made after our custom behavior
* Fixed typo (variable brought from the old code).
2013-02-07 22:18:55 -03:00
fossfreedom e1a68c4acb Merge branch 'master' of https://github.com/fossfreedom/coverart-browser
Conflicts:
	coverart_widgets.py
2013-02-06 22:55:53 +00:00
fossfreedom 4f0251902a open popup at cursor position - issue #144 2013-02-06 22:47:30 +00:00
Agustin Carrasco f31dc04af1 Added a timeout to the columns reallocation to avoid innecesary reallocations while the user is adjusting the pane size 2013-02-06 00:04:40 -03:00
Agustin Carrasco dd58b8a5e3 Removed the update_iconview_callback method from the source since it's not needed anymore 2013-02-05 23:57:58 -03:00
Agustin Carrasco d172061280 Use the available variables instead of saving redundant information 2013-02-05 23:57:03 -03:00
Agustin Carrasco 01696eaa2d Had to call to the superclass' do_button_press_event too, otherwise the selection wouldn't work 2013-02-05 23:47:12 -03:00
Agustin Carrasco 82859ceb2f Added the object_column property to the EnhancedIconView on the source ui 2013-02-05 23:36:28 -03:00
Agustin Carrasco 79bf0a5f79 Replaced the IconView for the EnhancedIconView as the cover_view on the source ui 2013-02-05 23:21:53 -03:00
Agustin Carrasco d382e30c2f Did some fixes on the EnhancedIconView
* The do_size_allocate wasn't calling the superclass behavior, and the allocation wasn't being correctly made.
* Added a signal to inform when a single click is made on a icon.
2013-02-05 23:20:14 -03:00
Agustin Carrasco c24fd22f45 Moved the EnhancedIconView class to the end of file to keep it organized 2013-02-05 22:22:40 -03:00
Agustin Carrasco 6f5783f8ba Organized the Toolbar tab; added some margins and stuff to make it look pretty (blame Glade for the other code that got moved around) 2013-02-05 21:32:51 -03:00
fossfreedom 86e7c4a99f Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2013-02-05 20:02:57 +00:00
fossfreedom 2a3d455838 add button style for toolbar - issue #149 2013-02-05 20:02:11 +00:00
fossfreedom 96e425aee9 Update README.md 2013-02-04 23:03:49 +00:00
fossfreedom 47ff73ba09 Update README.md 2013-02-04 23:02:38 +00:00
fossfreedom 1097077104 add dimensions to sprites - issue #149 2013-02-04 14:45:13 +00:00
fossfreedom 18ba48f572 Merge branch 'source-refactoring' 2013-02-04 14:23:14 +00:00
Agustin Carrasco 448663cfef Removed some unused code too. That call wasn't really doing anything. 2013-02-03 20:58:30 -03:00
fossfreedom b52b7ec8dd remove more unneeded code 2013-02-03 23:45:45 +00:00
fossfreedom 83e587405f merge listviewwindow with optionslistviewwidget 2013-02-03 23:41:03 +00:00
fossfreedom 401ae1bd60 Update README.md 2013-02-03 22:57:26 +00:00
fossfreedom cdd681a8e5 Merge branch 'source-refactoring' 2013-02-03 22:51:14 +00:00
Agustin Carrasco 3079af48bc Fixed the issue when the genre ListWindow would get destroyed after pressing [ESC] (issue #123). 2013-02-03 18:45:48 -03:00
Agustin Carrasco 67f22db97a Modified the ListWindow and the OptionsListViewWidget to recreate the liststore only when necesary (issue #123).
* Removed the activate method from the ListWindow and moved the relevant code to other places to help with the flow of the OptionsWidget.
* Correctly implemented the update_options and update_current_key callbacks to do what they should (recreate the list and select the currently selected item respectively)
* TODO: maybe the ListWindow should be merged with OptionListViewWidget? Right now they seem like two layers of the same thing, maybe it could be simplified by merging.
* TODO: the "mouse" flag for the window position seems to put the center of the windo on the mouse, instead of the corner (like the poups). Should look into that.
2013-02-03 17:15:46 -03:00
Agustin Carrasco b0a87d9d53 Adapted some syntax to PEP8 2013-02-03 17:15:46 -03:00
fossfreedom 16be637fd0 latest translations - also translates separate search providers plugin 2013-02-03 18:15:40 +00:00
Agustin Carrasco 784de34081 Fixed some stuff on the "Cover" tab of the bottom pane:
* Fixed the hiding of the show more results links. Now it hides when it haves to.
* Floated the Google branding div to the right. This way the show more link is more visible and easily accesible, while the branding is still there but doesn't obstruct the rest of the interface.
2013-02-03 11:39:42 -03:00
Agustin Carrasco 82545062b2 Optimized search for album with a given ext_db_key to only try to match against an album with the corresponding name. 2013-02-03 11:19:30 -03:00
fossfreedom 9c6d37a285 refact search providers into separate plugin - issue #152 2013-02-03 11:32:20 +00:00
fossfreedom 37da774bd6 rework to use selection changed event - issue #144 2013-02-01 16:47:12 +00:00
fossfreedom a6e3a42fab oops change 12 to 25 for when to display listwindow - issue #144 2013-02-01 15:37:27 +00:00
fossfreedom 028a38570a remove cancel button and close when lose focus - issue #144 2013-02-01 15:35:34 +00:00
fossfreedom 718d9b6b61 Merge branch 'source-refactoring' of https://github.com/fossfreedom/coverart-browser into source-refactoring 2013-02-01 15:18:13 +00:00
fossfreedom 4229614129 refactor listview to be separate widget/button - issue #144 2013-01-31 22:42:21 +00:00
fossfreedom 348f4350f5 add latest finnish po from launchpad 2013-01-26 16:47:55 +00:00
fossfreedom 99484c016c fix global makefile and remove unneeded file 2013-01-26 16:45:00 +00:00
fossfreedom c08be9f2fc add coverart archive to preferences - issue #69 2013-01-25 23:00:55 +00:00
fossfreedom 623894d5a6 add translations for search providers plugin - issue #145 2013-01-25 22:42:37 +00:00
fossfreedom b2485b739a Merge branch 'master' into source-refactoring 2013-01-25 22:32:46 +00:00
fossfreedom da36e0c0dd rename plugin to be more obvious - issue #145 2013-01-25 20:51:38 +00:00
fossfreedom ba40924cb7 Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2013-01-25 18:55:27 +00:00
fossfreedom fbb8e137d6 bug fix - mp3 files now correctly recognised in coverart embedded search - issue #145 2013-01-25 18:55:04 +00:00
fossfreedom 04bc511509 Update README.md 2013-01-25 15:04:19 +00:00
fossfreedom 323ce36b89 Update README.md 2013-01-25 15:00:43 +00:00
fossfreedom ebb93e6098 Merge branch 'master' into source-refactoring 2013-01-24 23:34:57 +00:00
fossfreedom 5dc5e6f83f latest launchpad translations 2013-01-24 23:30:45 +00:00
fossfreedom d28fc234e2 Update README.md 2013-01-24 14:59:11 +00:00
fossfreedom df1119b2a7 Update README.md 2013-01-24 14:20:58 +00:00
fossfreedom f0e3f9e658 Update README.md 2013-01-24 14:20:43 +00:00
fossfreedom ef1722ea4b Update README.md 2013-01-24 14:02:55 +00:00
fossfreedom c2c4d0f9e0 Update README.md 2013-01-24 13:51:29 +00:00
fossfreedom 440ff178b6 Merge branch 'master' into source-refactoring 2013-01-24 12:51:39 +00:00
fossfreedom 4b11d1a6c8 rework to be a dialog - issue #126 2013-01-23 15:41:03 +00:00
fossfreedom b13d370f19 remove plugin function and now depend on plugin property - issue #126 2013-01-23 13:13:35 +00:00
fossfreedom 6d2397bcf7 initial implementation of list window - issue #126 2013-01-22 23:52:22 +00:00
fossfreedom dd175bc366 fixed various issues - issue #142 2013-01-20 23:18:47 +00:00
Agustin Carrasco a3cda5d976 Readded the property decorator for the calc_artist property (issue #142) 2013-01-20 19:01:18 -03:00
Agustin Carrasco 0e4d722f2d Initial implementation of an enhanced icon view that manages it's own popup and haves a method to retrieve the selected objects, given that the column number of the object that represent each models row is setted 2013-01-20 18:29:09 -03:00
fossfreedom 4008db5d5b more sensible searching algorithm for discogs - issue #94 2013-01-18 17:03:40 +00:00
Agustin Carrasco 75bf6d4d27 Use of search_fold on all search parameters. Fix for issue #140 2013-01-18 01:52:49 -03:00
fossfreedom 08d3b775b9 latest launchpad translations 2013-01-17 21:51:01 +00:00
fossfreedom 567533dcf1 ensure genre 'all' is translated - issue #121 2013-01-17 21:22:13 +00:00
fossfreedom b2ea2c1c0e fix for sort - issue #139 2013-01-17 10:54:30 +00:00
fossfreedom e5a3f36cf2 provisionally add coverartarchive.org as a potential cover provider - not 'user' enabled as yet except through dconf - issue #69 2013-01-16 23:09:33 +00:00
fossfreedom 5f9797a433 latest launchpad translations 2013-01-16 21:27:07 +00:00
fossfreedom 66fdb07a43 use alternative URI format and also ensure data is correctly flushed before reading - issue #135 2013-01-16 14:25:59 +00:00
fossfreedom d86d64d146 remove use of deprecated webview API call and split titles that may have a % in them - issue #135 2013-01-15 23:30:05 +00:00
fossfreedom ce770b59db add google branding - issue #136 2013-01-15 22:39:29 +00:00
fossfreedom d76fe8ad67 few tidyups - double click issue still to be understood - issue #135 2013-01-15 21:39:45 +00:00
fossfreedom 29af04bf14 remove second call to activate_markup - issue #123 2013-01-14 19:48:54 +00:00
fossfreedom 9fcc9a9f3f remove conflicting file 2013-01-13 23:23:21 +00:00
fossfreedom be50d77ea2 Update README.md 2013-01-13 15:21:10 +00:00
fossfreedom 333f6ab314 remove genre default from list to be translated 2013-01-09 15:17:00 +00:00
fossfreedom 3a47f5899e fix error in popups.xml.in that stop translation and added Launchpad translation instructions 2013-01-08 21:33:56 +00:00
fossfreedom 791483db09 add in jrbastien french alternatives and split alternatives to be english and french 2013-01-08 21:13:10 +00:00
fossfreedom 982ee1d9ab temporarily prevent generation of popups.xml - waiting for refactor code to be merged in before uncommenting 2013-01-08 20:43:01 +00:00
fossfreedom 335cc699f4 generate popups.xml containing translations 2013-01-08 20:22:58 +00:00
fossfreedom 1b30a2abd6 latest fr.po from launchpad 2013-01-08 10:00:43 +00:00
fossfreedom 166164f7b8 rework translation to use xgettext instead of deprecated pygettext and also add support for translation of popups.xml 2013-01-08 00:06:11 +00:00
fossfreedom 61d0c4046f rename package.pot to coverartbrowser.pot 2013-01-07 13:55:25 +00:00
fossfreedom fb0cd7172a use artists instead of retrieving track artists separately again - issue #125 2013-01-06 22:44:15 +00:00
fossfreedom c4e460d3bd add natural search for track artists - issue #125 2013-01-06 22:29:34 +00:00
fossfreedom c88413b7bb Merge branch 'naturalsort' of https://github.com/fossfreedom/coverart-browser into naturalsort 2013-01-06 15:13:24 +00:00
fossfreedom cd716a2824 Merge branch 'master' of https://github.com/fossfreedom/coverart-browser into naturalsort 2013-01-06 15:13:03 +00:00
Agustin Carrasco d5e95f08e7 Removed superfluous variable _string and tidied up the NaturalString class a little 2013-01-06 11:44:07 -03:00
fossfreedom 9078f6c778 Natural String should inherit from str - issue #124 2013-01-06 13:32:25 +00:00
fossfreedom 8845226251 minor refresh - issue #121 2013-01-06 11:10:20 +00:00
fossfreedom aec56ccbcc removed unneeded print statement 2013-01-06 00:21:01 +00:00
fossfreedom 116fbc9a16 add natural sort - issue #124 2013-01-06 00:13:49 +00:00
fossfreedom fde36f8af7 tidyup - do not clear calculated album & artist when album is modified 2013-01-05 22:10:08 +00:00
fossfreedom c3b0e34a6b remove unneeded files 2013-01-05 15:26:46 +00:00
fossfreedom c2a70c112b use rhythmbox search for album artist and album name filter & sort 2013-01-05 15:24:10 +00:00
fossfreedom aa700342d1 Merge pull request #127 from jrbastien/patch-1
Updated French Canadian translation for issue #121
2013-01-04 23:11:33 -08:00
Jean-Rene Bastien 5be3fb6c95 Update po/fr_CA.po
Fixed missing msgid on coverart_browser_prefs.ui.h:13
2013-01-04 19:15:50 -05:00
Jean-Rene Bastien 9e9b8cb8b1 Update po/fr_CA.po
More updates done.
2013-01-04 09:14:49 -05:00
Jean-Rene Bastien 75f8e8cf0f Update po/fr_CA.po
First commit
2013-01-04 08:48:36 -05:00
Agustin Carrasco 29eab89366 Only create one renderer and reuse it later if needed 2013-01-04 04:40:56 -03:00
Agustin Carrasco e0a8b573cf Replaced the IconView's internal text cell renderer for a custom that behaves exactly the same way, as a workaround for an bug introduced on Gtk 3.6. This should resolve issue #118 2013-01-03 22:35:43 -03:00
Agustin Carrasco 7d439b20de Bugfix: correctly manage the AlbumModel when:
* A second sort request is made when a first one is already being processed. The implemented behavior in this commit is to cancel the previous process and initiate a new one, with the new sorting order.
* A filter is requested when the model is being sorted. Previously, this led to the model trying to set the 'show' column to albums that weren't currently in the tree model, given that they were removed to reorder them, and thus causing a lot of "invalid tree iter" assert fails. The current behavior is to check if a tree iter is valid before trying to asign the 'show' value.
2013-01-03 01:28:08 -03:00
fossfreedom d4c992e59e Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2013-01-02 23:38:20 +00:00
fossfreedom f69e5b3050 more translation stuff 2013-01-02 23:37:43 +00:00
Agustin Carrasco f0d1d3474a Bugfix: now, even if the album is in the last row, it's made visible in the case the entry view shadows it (issue #104) 2013-01-02 19:52:55 -03:00
Agustin Carrasco 22c7f46d3b Cleaned and added some comments to mouseclick_callback 2013-01-02 19:52:55 -03:00
Agustin Carrasco fc587622e7 Removed unneeded lines on item_activated_callback 2013-01-02 19:52:55 -03:00
fossfreedom d6180601c1 slightly resized decade icon to make better use of available space 2013-01-02 20:47:19 +00:00
fossfreedom af3e7c9e1c removed unneeded files 2013-01-02 20:23:53 +00:00
fossfreedom c0b777b807 removed unneeded files 2013-01-02 20:23:41 +00:00
fossfreedom 20fb628f67 regenerated all po's and fixed en_US.po 2013-01-02 20:22:38 +00:00
Agustin Carrasco 433c03e4eb Bugfix: only update the iconview item distribution (to use the allocated space) **only when the width is updated**. There's no need to update the iconview on height changes, since it's vertically scrollable; also, updating on height changes slows down the UI when working with the bottom expander. 2013-01-02 16:03:04 -03:00
Agustin Carrasco ecb20e7efc Updated spansish translation 2013-01-02 15:14:43 -03:00
Agustin Carrasco 1005531f65 Removed translatable property from settings keys that should not be translated (issue #121) 2013-01-02 15:01:12 -03:00
Agustin Carrasco e58964e65d Bugfixes:
* Move the iconview cursor when right clicking to the clicked album.
* Ignore context menu triggers (right clicks) on the coverview to hide/show entry view.
2013-01-02 14:51:17 -03:00
fossfreedom e6b091df34 fix some minor fr.po translation issues 2013-01-02 13:47:54 +00:00
fossfreedom 293557342a updated translation files with current 2013-01-02 11:32:36 +00:00
fossfreedom f0de06c10c use expand -t 4 to change tabs to spaces 2013-01-02 11:28:38 +00:00
Agustin Carrasco b69168b157 Some bugfixes:
* Correctly update the last selected album when using keyboard cursor movement, which allows the entry view to expand when clicking the currently focused album, doesn't matter how it got focused in the first place.
* Fixed some bugs arround rapidly clicking different albums, which caused a incorrectly interpreted double click.
2013-01-01 21:03:45 -03:00
Agustin Carrasco 3ef2ff2eb1 Introduced the viewport scrolling when the album is not visible to the manual expansion too 2013-01-01 20:01:39 -03:00
Agustin Carrasco 0072871853 Simplified the second-click-expand and third-click-collapse behaviors, using a couple of local variables and removing the Expansion related code. Also, removed the manual expanded/collapsed indicators, since it doesn't make sense in the second-click expand schema 2013-01-01 19:45:15 -03:00
Agustin Carrasco 99ea410269 Style fixes acording to PEPs 2013-01-01 17:53:07 -03:00
fossfreedom 0203d4a8e4 replaced custom async with gdk timeout equiv - issue #104 2013-01-01 15:39:57 +00:00
fossfreedom 986d20d1fc minor timing changes - issue #104 2013-01-01 14:45:44 +00:00
fossfreedom 15a1e37e39 remove unneeded files 2013-01-01 10:43:09 +00:00
fossfreedom ff6c2249ef manual merge from master 2013-01-01 10:41:20 +00:00
fossfreedom 0fb8926717 minor calculation changes on spritesheet 2013-01-01 10:21:47 +00:00
fossfreedom a38da1dfe3 revised decade spritesheet - issue #120 2013-01-01 10:17:02 +00:00
fossfreedom e3b5c798fa add decade spritesheet and code - issue #120 2012-12-31 18:44:00 +00:00
fossfreedom 610dccd35a remove orig file - issue #120 2012-12-31 13:40:23 +00:00
fossfreedom 77c7b9b5b7 add new icon - issue #120 2012-12-31 13:37:29 +00:00
fossfreedom 38a0a4784e add new button plus code for decade filter - issue #120 2012-12-31 12:04:03 +00:00
Agustin Carrasco 1f9779cb1d Merge branch 'quick-search'
This introduces the quick search feature to the master branch. This feature allows the user to do a quick search by album name through the visible albums, quickly selecting the first match and allowing the user to navigate through the matches using the up and down arrow keys (close #99)
2012-12-30 20:12:31 -03:00
fossfreedom 8c509334d7 bug fix - remove use of deprecated VBOX 2012-12-30 22:38:34 +00:00
fossfreedom 009b4bc03b Merge branch 'master' into click 2012-12-30 18:19:42 +00:00
Agustin Carrasco 5cdf587e96 Change timeout from 3 to 4 seconds and fix an issue with the match selection (issue #99) 2012-12-30 14:19:13 -03:00
Agustin Carrasco b0b4aafe74 Added the posibility to navigate through the quick search matches using the up and down arrows. (issue #99) 2012-12-30 12:44:17 -03:00
Agustin Carrasco 764df82454 Fixed bug on ReversedSortedCollection class' index method 2012-12-30 12:38:56 -03:00
Agustin Carrasco 057ef59d24 Hide the quick search entry when it loses focus 2012-12-30 11:42:10 -03:00
Agustin Carrasco 59dcf4ac19 Added a timeout of 3 seconds after the last input on the quick search entry before automatically hiding it (issue #99). 2012-12-30 00:14:28 -03:00
Agustin Carrasco d4f13ee7de Added a visible border around the text quick search entry to make it blend better with it's surroundings (issue #99) 2012-12-29 23:47:53 -03:00
Agustin Carrasco 76e0cc2661 Clean the widgets module:
* Some minor style adjustments following python PEPs
* Removed the superflous ```initial_label``` PopupButton's property and its accesors, since it was confusing and didn't add any real value. The value saved on it was something only used on the subclasses (the superclass only used it on initialization) and is really easy to access it using other mediums instead (other object properties). Plus, using accesors on python isn't really necesary. For plain variables there aren't needed at all, and if you need to do some proccessing before setting/getting a variable, you can replace accesors with a property.
* Fixed bug on the SortPopupButton that caused that the radio button would, on initialization, select the 'Sort by album name' item, even if that wasn't the current sorting key.
2012-12-29 19:49:49 -03:00
Agustin Carrasco 008bda1f8c Bugfix: the toolbar positions radiobuttons were firing an extra signal when the pref window was opened, forcing the toolbar to dissapear if it was positioned in one of the sidebars. Changed the radiobuttons for a more suitable (in my opinion) combobox, fixing this bug at the same time (issue #117). 2012-12-29 19:17:57 -03:00
Agustin Carrasco d75e4ff8b6 Bugfix: instead of removing and readding an item into the sorted collection, defined a reorder method which internally checks if the items needs to be reordered and return the new index in case it does (issue #119). Removed the pre-modified signal since it's no longer needed. 2012-12-29 18:26:47 -03:00
fossfreedom 36c2e15e0b rework to introduce second/third click show hide options - issue #104 2012-12-29 17:03:34 +00:00
fossfreedom 922300a0a6 add shift or ctrl click override - issue #104 2012-12-27 11:29:26 +00:00
fossfreedom bab3e9c0dc scroll if selected album would be hidden by entry view - issue #104 2012-12-26 22:12:04 +00:00
fossfreedom d13a1919c7 initial implementation - issue #104 2012-12-23 22:33:06 +00:00
Agustin Carrasco 45d23660ce Minor bugfixes:
- Prevent the quick search to gain focus if a modifier key is being pressed, since they have an special meaning on the cover view.
- Move the cover view cursor to the album selected by the quick search mechanism.
2012-12-23 03:57:41 -03:00
Agustin Carrasco 64c9003d15 Added functionality to the quick search (issue #99). TODO: timeout to hide the quick searcn entry 2012-12-23 02:48:27 -03:00
Agustin Carrasco 6bbaa0a4e4 Initial implementation of the quick entry on a overlay for the iconview 2012-12-23 00:49:32 -03:00
fossfreedom c05977d224 Merge branch 'master' into split 2012-12-22 22:57:19 +00:00
Agustin Carrasco d2211d6af2 Changed radio buttons for combobox issue #116 2012-12-22 18:37:30 -03:00
Agustin Carrasco 58fef049d5 Fixed memory usage issue #116 2012-12-22 13:27:28 -03:00
fossfreedom 4f0b0739f8 add shadow options - issue #116 2012-12-22 07:54:50 +00:00
fossfreedom f6d4a11c1a add gothic as alt and removed alt world beat - issue #115 2012-12-20 23:50:11 +00:00
fossfreedom df9e24bd31 typo for ragtime - issue #115 2012-12-20 16:14:25 +00:00
fossfreedom 900f2daf77 karaoke typo - issue #115 2012-12-20 16:11:54 +00:00
fossfreedom 72d1c0ca84 get library and queue names from source - issue #112 2012-12-20 15:07:31 +00:00
fossfreedom 4f619dd869 Merge branch 'spritesheet' 2012-12-19 20:03:39 +00:00
fossfreedom 8b6a7e623b set start-position for genres to zero - issue #109 2012-12-19 20:02:23 +00:00
fossfreedom 788c7c450d Merge branch 'master' into split 2012-12-19 16:36:43 +00:00
fossfreedom 93ec578647 merge from spritesheet 2012-12-19 16:21:37 +00:00
fossfreedom bc7239f1aa oops now latest files issue #109 2012-12-19 16:12:31 +00:00
fossfreedom 7917d05fb1 remember sort order - issue #114 2012-12-19 15:41:32 +00:00
fossfreedom 7322ad5060 removed unneeded file 2012-12-19 14:59:33 +00:00
fossfreedom 90603e7e02 revised positioning of genre icons - issue #109 2012-12-19 14:59:08 +00:00
fossfreedom 4aa6c29d63 revert default genre icon - issue #109 2012-12-19 12:33:18 +00:00
fossfreedom b250ce98e2 latest icons and add x_start and y_start on spritesheets - issue #109 2012-12-19 10:00:28 +00:00
fossfreedom 1e8da857f8 merge from master 2012-12-19 09:45:43 +00:00
Agustin Carrasco 8662fec1da Removed unnecesary query reasigment of query model 2012-12-19 01:04:02 -03:00
Agustin Carrasco ae0420b130 Bugfix: had to create a new prop model instead of using the library source one. The reason: the library source genre prop model is linked to the library source filtered query instead of the base query, which means that each time you filter the library source, all genres are being filtered too. This caused two problems: 1º the genre popup would get filtered (when it shuldn't); 2º in the case of big libraries (like mine) the UI would hang since the row-deleted/row-inserted would get fired once for each genre. 2012-12-19 00:45:55 -03:00
Agustin Carrasco 3ba609b760 Bugfix: fixed internalization error on genre filter (issue #110) 2012-12-18 22:00:42 -03:00
fossfreedom 09a3e7022d workaround for playlist popup issue #111 2012-12-18 23:50:00 +00:00
fossfreedom 00d6ef8ae5 copes with r&b which is mapped to blues - issue #109 2012-12-18 13:53:37 +00:00
fossfreedom a00de52d77 ensure partial matching is done on longest genre strings first - issue #109 2012-12-18 13:37:46 +00:00
fossfreedom 244a12e205 fix sizing and positioning of genre icons - issue #109 2012-12-18 10:53:36 +00:00
fossfreedom 9d2ff14c3a add partial and alternate genre checks - issue #109 2012-12-18 00:07:48 +00:00
fossfreedom 80dce0529b latest genre icons issue #109 2012-12-17 23:00:08 +00:00
fossfreedom a5835c660c backout proxy stuff 2012-12-17 20:25:23 +00:00
fossfreedom 9f02f8befc Merge branch 'master' into split 2012-12-17 15:57:50 +00:00
fossfreedom 665d9dc8d8 add proxy for discogs 2012-12-17 15:55:53 +00:00
Agustin Carrasco 7a2d4fbb6d Changed album_artists for artists on the popup.xml file 2012-12-17 11:52:29 -03:00
Agustin Carrasco 55af65c571 Bugfix: changed some places that still referenced the Album's album_artist property instead of the artist attribute (issue #83) 2012-12-17 11:46:26 -03:00
fossfreedom 8dc26baa19 Update README.md 2012-12-17 14:17:20 +00:00
fossfreedom b0470c04fe creative license for genre icons 2012-12-17 10:11:36 +00:00
fossfreedom 27dc0d0c74 make shadow the default and discogs an optional 2012-12-17 10:01:31 +00:00
fossfreedom 59c6d68210 Merge branch 'master' into spritesheet 2012-12-17 09:53:15 +00:00
fossfreedom bb76e3a720 latest icons issue #109 2012-12-17 09:52:29 +00:00
Agustin Carrasco f96729e2e8 Merge branch 'album-artists'
This introduces a the album artists as a identity parameter for albums. This means that an album is unique given it's name AND artist, effectivelly dividing albums with the same name but different artists. It's responsability of the user to correctly tag their collection for this to work correctly, since a missing or wrong album artist tag may produce an incorrect view of the collections albums.
2012-12-17 02:06:15 -03:00
Agustin Carrasco a31fdecfea Bugfix: added a check and a resize on the cover creation if the pixbuf loaded from a file isn't a square. This should avoid albums with ugly shadows since it doesn't cover the whole area 2012-12-17 01:55:03 -03:00
Agustin Carrasco 226291602b Bugfix: changed the strategy to indicate to the cover_view that it should update to something more blunt. The old method was generating some ugly bugs on my view 2012-12-17 01:32:10 -03:00
fossfreedom 50d0a7b682 new iconset #109 2012-12-16 23:39:48 +00:00
Agustin Carrasco 15464534b6 Bugfix: fix cover search to work with all the providers (artsearch plugin, our providers and the cover search pane) (issue #83) 2012-12-16 18:24:07 -03:00
Agustin Carrasco 2fa11460c2 Modified the album managment to include the album artist as an identity parameter for the album itself. This way, each album is unique given it's name AND artist, effectivelly dividing albums with the same name but diferent album artist. (issue #83) 2012-12-16 15:36:24 -03:00
fossfreedom 9d7a2ad49c tidied sort and playlist images - issue #109 2012-12-16 18:35:57 +00:00
fossfreedom d556c21f5a update popups.xml with new sort and playlist sprites 2012-12-16 14:39:49 +00:00
fossfreedom f95d24fac6 merge from upstream spritesheet 2012-12-16 12:33:31 +00:00
fossfreedom 4edd210f2f convert spr to xml 2012-12-16 12:29:39 +00:00
Agustin Carrasco e7c14a93d7 Added the posibillity to indicate the icon 'size' when creating a spritesheet, this is, scalling the original icons to a new size (squared) 2012-12-15 23:29:45 -03:00
Agustin Carrasco 313e80571d Merge branch 'idle-call'
Abstracted idle calls iterations into an utility class (plus a decorator) to avoid code duplication and make the code more redeable.
2012-12-15 23:19:32 -03:00
Agustin Carrasco 539f52a51d Reimplemented the idle iter methods creating with a decorator 2012-12-15 23:15:21 -03:00
Agustin Carrasco b9627a7499 Modified the rest of the idle calls to use the IdleCallIterator 2012-12-15 22:41:24 -03:00
fossfreedom fb8348874d Merge branch 'spritesheet' of https://github.com/fossfreedom/coverart-browser into spritesheet 2012-12-15 12:19:28 +00:00
fossfreedom a27c31d7f6 Merge branch 'master' of https://github.com/fossfreedom/coverart-browser into split 2012-12-15 12:18:36 +00:00
Agustin Carrasco d7fe753ba3 Oops, forgot about the image and the spr file. Also added __contains__ to the ConfiguredSpriteSheet 2012-12-15 03:12:41 -03:00
Agustin Carrasco 76dd4b5687 Created an utility class to manage spritesheets. Added a genres spritesheet togheter with a spr spritesheet config file for it. (issue #109) 2012-12-15 03:06:42 -03:00
Agustin Carrasco 0642edd593 Implemented idle calls on the AlbumsModel using the IdleCallIterator 2012-12-15 01:53:28 -03:00
Agustin Carrasco 5619c19182 Created an utility class to abstract the idle calls. 2012-12-15 01:51:40 -03:00
Agustin Carrasco 2272cb9880 Bugfix: Unknown cover not updating correctly when enabling/disabling the add-shadow setting (issue #68) 2012-12-14 18:29:25 -03:00
fossfreedom 39a62baade Merge branch 'master' into split 2012-12-14 20:41:42 +00:00
fossfreedom f1324fa2b7 threading seems to work now 2012-12-14 20:39:05 +00:00
Agustin Carrasco 06f50ad843 Merge branch 'shadow'
Added the posibility to add a shadow to the album's cover, adding some depth to the coverview. (closes #68)
2012-12-14 17:26:20 -03:00
Agustin Carrasco 1341ec239e Added option to enable/disable the shadow (issue #68) 2012-12-14 17:14:15 -03:00
fossfreedom 763058e1b5 trying lastfm async method - not working as yet 2012-12-14 16:41:26 +00:00
fossfreedom b2131b285a Merge branch 'master' into split 2012-12-14 14:15:55 +00:00
Agustin Carrasco f66d662c8b Bugfix: blank cover view on autostart with big library (issue #108) 2012-12-13 23:09:55 -03:00
Agustin Carrasco 2a0d431976 Just changed the place of some lines since it looked better xD 2012-12-13 18:20:06 -03:00
Agustin Carrasco 0d69463945 Fixed exception on CoverManager initialisation (issue #68) 2012-12-13 18:17:49 -03:00
Agustin Carrasco 5ae748638c Change the page increment for the cover size to 5 pixels 2012-12-13 17:39:38 -03:00
Agustin Carrasco bf7faefcf2 Bugfix: change item_width on cover resize 2012-12-13 17:39:25 -03:00
Agustin Carrasco 118a36ffc1 Bugfix: since the album manager isn't longer a singleton, it should be passed as a parameter to the coversearch pane 2012-12-13 16:50:46 -03:00
Agustin Carrasco a8f5a1bcf6 Some bugfixes and little changes:
- Bugfix: resizing the ShadowedCover failed because the Shadow dimensions weren't being updated.
- Change: reduce padding between items.
- Change: reduced shadow width by 1 pixel.
2012-12-13 16:41:01 -03:00
Agustin Carrasco 07c35ecf99 Initial modifications to add shadow to the coverview (issue #68) 2012-12-13 09:19:39 -03:00
Agustin Carrasco 2d01e0fd12 Bugfix: iconview wouldn't expand to max available width 2012-12-13 09:19:30 -03:00
fossfreedom e388afcbef merge from master 2012-12-13 11:31:56 +00:00
fossfreedom eea7dac481 remove extraneous file 2012-12-13 10:10:14 +00:00
fossfreedom c4a594bb5f Merge branch 'master' of https://github.com/fossfreedom/coverart-browser 2012-12-13 10:00:29 +00:00
fossfreedom e399b257f8 move pictures to img folder - issue #106 2012-12-13 10:00:15 +00:00
Agustin Carrasco 41a80bedca Changed chunk sizes for different data processing. The cover_load_chunk is smaller than the album_load_chunk to make the UI more responsive when loading covers (I/O to disk) 2012-12-13 06:55:06 -03:00
Agustin Carrasco dd68e6b096 Bugfix: avoid the firing of the update_iconview callback when we are moving the bottom pane handle. There's no need for the signal to be fired and it only makes the UI choppy 2012-12-13 00:15:28 -03:00
Agustin Carrasco 912c84d44e Some style clean up. If anyone's wondering, I clean up acording to python PEPs on code style. Yeah, kinda OCD but w/e 2012-12-12 23:56:01 -03:00
Agustin Carrasco 94b181a21c Bugfix: entryview popup shouldn't fire when no entries are selected 2012-12-12 23:51:16 -03:00
Agustin Carrasco 787599858e Sync our entryview sort order with the library source's entryview sort order 2012-12-12 23:48:43 -03:00
Agustin Carrasco df46d6d0f1 Added the posibility to sort by column on the entryview. Closes #38 2012-12-12 22:27:38 -03:00
Agustin Carrasco e1831d5e2e Removed rating update, doesn't make much sense since it's supposed to be the album's rating, not it's entries. 2012-12-12 22:21:44 -03:00
fossfreedom 94d71c14c6 imagetogglebutton widget #106 2012-12-12 23:29:28 +00:00
fossfreedom f019a1c4c1 fix sort order button to have custom images rather than stock icons - issue #106 2012-12-12 23:25:44 +00:00
Agustin Carrasco 3f3829393e Actually these should be object properties, not class properties 2012-12-12 12:21:45 -03:00
fossfreedom 174fe76371 resolve merge issues
Merge branch 'genre'

Conflicts:
	ui/coverart_browser_prefs.ui
2012-12-12 13:22:37 +00:00
Agustin Carrasco d32b1b6ea7 Still had some imperfections, now I think it's done 2012-12-11 20:30:40 -03:00
Agustin Carrasco 347a837dca Cleaned up the sort image a little more since there were still some traces of white where it should've been trasnparent 2012-12-11 20:27:55 -03:00
fossfreedom 5843ff594d remove legacy playlist menu from other sources - issue #101 2012-12-11 23:09:03 +00:00
fossfreedom 0106b422f7 add music queue + arrows for sort order - issue #101 2012-12-11 22:55:46 +00:00
fossfreedom c21f864a7a make sort button grey/black and background transparent - issue #101 2012-12-11 21:34:12 +00:00
fossfreedom a973526de7 ensure initialise is only runnable once - issue #101 2012-12-11 21:04:56 +00:00
fossfreedom 304e7906b8 merge and fix sidebar toolbar - issue #101 2012-12-11 15:20:33 +00:00
Agustin Carrasco 01fc7552d9 Readded signals to the bottom pane. They were deleted somewhere along the way. issue #101 2012-12-10 23:31:52 -03:00
Agustin Carrasco 1c7462e246 Removes actions from the PopupButton, use the radio_items directly. This fixes the issue with the genres (I don't know yet why it was happening) issue #101 2012-12-10 23:18:34 -03:00
Agustin Carrasco 0717352161 Fixed genre filter removal when no more albums on the genre 2012-12-10 21:17:08 -03:00
fossfreedom f9bab2eeff make menus radiomenuitems - issue #101 2012-12-10 23:51:57 +00:00
Agustin Carrasco 8a85ee0843 Moved the creation of the popup for the genres to the button initialization. Added signal handlers for model changes to update the genres. (issue #101) 2012-12-10 15:08:24 -03:00
fossfreedom 5c6a92db62 cleanup obsolete commented out code 2012-12-10 16:24:51 +00:00
fossfreedom 4fec0615b8 convert menuitems to radiomenuitems 2012-12-10 00:09:00 +00:00
Agustin Carrasco 9973f8fd90 Fixed problem on line 270 and some style fixes (sorry, I got carried away xD) issue #101 2012-12-09 19:58:09 -03:00
fossfreedom 8e3c7452cc sort by button - issue #101 2012-12-09 22:35:04 +00:00
Agustin Carrasco dcd531fb3e Readded missing 'pixbuf_column' property for the incoview (issue #101) 2012-12-09 16:53:53 -03:00
Agustin Carrasco 89ce607a9f Merge branch 'clean-settings'
This changes the ui for the preferences window, that now uses a notebook with separated tabs for different options. closes #105
2012-12-09 16:48:33 -03:00
Agustin Carrasco 8a8ef6dc80 Replaced some strings on the settings ui 2012-12-09 16:48:21 -03:00
fossfreedom fbaa034d40 add playlist and genre buttons - issue #101 and #102 2012-12-09 18:37:25 +00:00
Agustin Carrasco 6dd414a2d5 Since the new AlbumManager keeps a reference of the cover_view (an various managers keep signals connected to it) it's easier to make the source to be a singleton instead of the AlbumManager. It probably is wrong, but it works for now; I plan to decouple the coverart_browser_source module a lot more later on, so this is a workaround for now 2012-12-09 00:24:32 -03:00
Agustin Carrasco fd3711a7e3 Added new commetns to the coverart_album module 2012-12-08 23:49:05 -03:00
Agustin Carrasco 91955c3c83 Fix some problems on entry deletion/allocation: check for duration before allocation; delete the track from the tracks list on deletion 2012-12-08 22:29:43 -03:00
Agustin Carrasco 879a4b4147 Redistributed the settings on the preferences window using a GtkNotebook #105 2012-12-08 20:16:47 -03:00
Agustin Carrasco 3ba07eb628 Merge branch 'loading'
This merge implies a change on the coverart_album module. A somewhat fullfilling explanation of the changes can be found on #44
2012-12-08 19:17:13 -03:00
Agustin Carrasco 08b37dda42 Cleaned up the settings 2012-12-08 19:13:00 -03:00
Agustin Carrasco 1afccba41c Changed the 'cover-updated' model's signal for 'visual-updated' since it may be also the markup text. Fixed the text settings (ellipsizing, font, etc) #44 2012-12-08 13:19:13 -03:00
Agustin Carrasco 93a3e42747 Changed the way the filter text is checked when empty. There may be filters than don't use text, like the model filter, then it's easier to pass a None 2012-12-08 12:24:36 -03:00
Agustin Carrasco bebcd0e51a Lower the searchtext too to make the search case insensitive 2012-12-08 12:16:09 -03:00
Agustin Carrasco c4f51ed10e Use map to disconnect signals, is more concise 2012-12-08 12:13:21 -03:00
Agustin Carrasco 72120f010b Avoid signaling twice on track modification 2012-12-08 12:11:34 -03:00
Agustin Carrasco f30aafb4fe Modified rating calculation. There's no need to cast since it's already a float. All tracks should be taken into account when calculating the mean value, even those whit rating 0. 2012-12-08 12:08:13 -03:00
Agustin Carrasco d7b4b05c73 Fixed one last index on _iters, forgot to change it for it's name 2012-12-08 05:22:03 -03:00
Agustin Carrasco 067f5681a7 Separated the signal 'cover-updated' from the 'album-updated' on the AlbumsModel. The source doesn't need to get informed when the cover is updated, only the ShowingPolicy 2012-12-08 05:19:15 -03:00
Agustin Carrasco b695ca5dea Changed the way the _iters internal structure is used to work with a dict instead of a list, that way the code is more redeable e.g. you know what you are referencing using words instead of numbers 2012-12-08 05:12:48 -03:00
Agustin Carrasco 27b161d5f2 Fixed error when trying to show a source on the coverview when it hasn't been initialized yet 2012-12-08 05:04:55 -03:00
Agustin Carrasco 98739e44e0 Changed for loop to use map instead. Idk if it's faster, but at least is more pretty 2012-12-08 05:04:26 -03:00
Agustin Carrasco ee5af5cd3b Changed the loading procedure to use a python iterator instead of manually iterating with the model's iters 2012-12-08 00:57:06 -03:00
Agustin Carrasco 2c4d28f7ac Made the model filtering a little more safe and fast. Still think that the posible sources to filter by should be another filter-like list on the main coverview source 2012-12-08 00:50:36 -03:00
Agustin Carrasco 678eeee2f3 Transformed the playlist album limiting into a filter. It doesn't filter it's *tracks* and I don't think it should, since our plugin manages albums mostly. #44 2012-12-08 00:43:11 -03:00
fossfreedom c49c26feea fix coverart view from playlists - #44 2012-12-07 15:48:34 +00:00
fossfreedom 70d66121e2 remove the old prefs code from provider plugin 2012-12-07 10:10:23 +00:00
fossfreedom 3e7ecf70e8 add version info to plugins 2012-12-07 10:03:29 +00:00
fossfreedom b572aa64a1 re-add search providers key code stuff to main preferences 2012-12-07 09:52:56 +00:00
fossfreedom 00ec100037 remove separate prefs for providers plugin 2012-12-07 09:42:55 +00:00
Agustin Carrasco 6c7acdbcfc Fixed persistence of toolbar position. Don't know exactl why it works now #44 2012-12-07 02:06:14 -03:00
Agustin Carrasco 3673f80fd5 Fixed favourites. I think this was the problem. #44 2012-12-07 01:42:22 -03:00
Agustin Carrasco 1f94adc564 Fixed double-click play. My bad, though the query_model could be treated as a liststore. #44 2012-12-07 01:34:19 -03:00
Agustin Carrasco f053591127 Fixed track title filter #44 2012-12-06 22:41:18 -03:00
Agustin Carrasco dd03e229a8 Fixed album reordering when changed. Had to add a new signal to inform before the album is updated so the model can take care of reordering it. #44 2012-12-06 22:37:48 -03:00
Agustin Carrasco b617e86632 Fixed sorting issues making the sort_order changes to fail, together with some glitches in some types of sorting #44 2012-12-06 21:37:35 -03:00
Agustin Carrasco 1894e96d56 Fixed handler warning on startup 2012-12-06 20:52:14 -03:00
fossfreedom a830617f1b split to two plugins 2012-12-06 10:06:11 +00:00
Agustin Carrasco d27ed24a65 Changed the strategy to obtain the album_artist to a one more comprehensive 2012-12-06 04:03:01 -03:00
Agustin Carrasco 3f7a9e474e Fixed the genres combobox behavior 2012-12-06 03:14:42 -03:00
Agustin Carrasco 78396a5c41 Fixed some little issues on the Album class, fixed the path retrieval on the AblumsModel previous the throw of the album-update signal, and removed the genres manager since it's not needed. 2012-12-06 03:14:06 -03:00
Agustin Carrasco 1ab8bb5f6e Fixed behavior on album update 2012-12-06 02:09:23 -03:00
Agustin Carrasco 1167245871 Fixed album rating 2012-12-06 01:55:35 -03:00
Agustin Carrasco 16016a73b7 Fixed cover resize 2012-12-05 22:38:02 -03:00
Agustin Carrasco 00367c465b Fixed sort_order, now it does get saved on the settings 2012-12-05 22:37:37 -03:00
Agustin Carrasco 951332a3ac Changed the style of the idle_loads 2012-12-05 21:57:13 -03:00
Agustin Carrasco 1a9526108e Fixed sorting 2012-12-05 21:30:18 -03:00
Agustin Carrasco fd978fe8a6 Fixed some typos 2012-12-05 18:57:46 -03:00
Agustin Carrasco 343595b04d Fixed autoupdate of albums 2012-12-05 18:45:24 -03:00
Agustin Carrasco 2ea67bf72c Fixed some style and minor issues 2012-12-05 18:18:22 -03:00
Agustin Carrasco b0ebc7a67f Fixed all cover search aspects 2012-12-05 18:18:22 -03:00
Agustin Carrasco a5986e9998 Fixed entryview to use the new Album interface 2012-12-05 18:18:22 -03:00
Agustin Carrasco 61c6ffb112 Fixed play/queue album 2012-12-05 18:18:22 -03:00
Agustin Carrasco 48d798c838 Removed the covers_model from the source. It now should be accesed from the manager 2012-12-05 18:18:22 -03:00
Agustin Carrasco 11dc1edfe0 Fixed filters. It allows multiples filters e.g: genre + another filter 2012-12-05 18:18:22 -03:00
Agustin Carrasco c3134519dd Fixed some typos on Album's properties. Added a param to the remove_filter method of the AlbumsModel class to determine if this should fire a refilter or not; this is because in some cases, you want to remove a filter to add another one, which causes two refilters. 2012-12-05 18:18:22 -03:00
Agustin Carrasco c3010e4f84 Back to old default_load_chunk. Change the direction of the progress for the second bar to make it more pretty 2012-12-05 18:18:22 -03:00
Agustin Carrasco 3ba1e71461 Fixed issue of loading images when the model is filtered, now the images updated even in that case 2012-12-05 18:18:22 -03:00
Agustin Carrasco 8ac49fa006 Added check before changing visible paths. Changed the filtering procedure using signals now 2012-12-05 18:18:22 -03:00
Agustin Carrasco 299b4578be Some more modifications/refinements:
- Removed all the prop changed functions and just inform that the track is modified, so the album cleans itself and is readded to the model.
- Made the album_loading asynchronous and added progress information. In total there are three phases to the loading: album loading, model loading and cover loading.
- Made the duration property of the albums precalculated, to avoid recalculation each time it's selected. This implies that we should listen to changes on track duration too to update the album, but is not that hard with the new events model.
2012-12-05 18:18:22 -03:00
Agustin Carrasco d3a3a7b6a7 Refined the module:
- The Album listen to changes on it's Tracks and informs when gets modified. This way there's no need to follow each specific change; instead, we update the model when the album gets updated. Neverthless, the managers are responsible of emiting the tracks signals (given that is more cost effective to listen to db changes in a centralized way).
- Fixed some issues with the album filters.
- Refined the AlbumsModel. Now it takes care of a couple more things, principally, updating corresponding treestore row when an album or it's cover gets changed. Added the posibility to add filters and sorting (although not fully tested yet).
- Changed the purpouse of the ShowingPolicy. For now it just takes care of some workarounds concerning the cover_view: it takes care of updating it when necesary. After some changes, there seems that there's no need to use a progressive strategy since it's working faster now.
- The TextManager now takes care of prividing the tooltip and markups for the model.
- Made some changes on the CoverManager and AlbumLoader, but idk if they will be permanent yet, just testing stuff.
- There's still tons of stuff to test and fix, but so far it's working better than before and is waay more organized.
2012-12-05 18:18:22 -03:00
Agustin Carrasco 5299bda35b Added an utility module to put there whatever we could need that isn't related with the plugin perse, but that are aditions that we use on different parts of it 2012-12-05 18:18:22 -03:00
Agustin Carrasco c2519efdfc Removed model definition from ui file 2012-12-05 18:18:22 -03:00
Agustin Carrasco 783959f230 Rewrote the Album class to be more small and don't work with things that aren't related to it (for instance, working with the model). Added a Track class to wrap the rhythmbox entry and make it more accesible. Initial draft of the AlbumsCoversModel that will intend to abstract the tree store 2012-12-05 18:18:22 -03:00
Agustin Carrasco 8d4f639c64 Fixed filtering 2012-12-05 18:18:22 -03:00
Agustin Carrasco b33f2d8e34 Removed the sort_by parameter since it's not used anywhere. Gotta keep working on fixing all the other functionalitty with the new managers model. 2012-12-05 18:18:21 -03:00
Agustin Carrasco 12a790a7b0 Added some changes to allow the album to insert itself in a determined position 2012-12-05 18:18:21 -03:00
Agustin Carrasco fd3761b886 Initial behavior working now, it shows the albums on the screen. It takes a while to fully load the albums but it works 2012-12-05 18:18:21 -03:00
Agustin Carrasco 74f507677d Massive cleanup + reimplementing some stuff to get along with the new application model 2012-12-05 18:18:21 -03:00
Agustin Carrasco a0e7ca9746 Some fixes on the overall structure, almost ready to test the changes to watch for bugs and then implement the showing policies 2012-12-05 18:18:21 -03:00
Agustin Carrasco ca9219b76d Working on refactoring the album module 2012-12-05 18:18:21 -03:00
Agustin Carrasco d55b43e6c3 Style fixes 2012-12-05 18:18:21 -03:00
fossfreedom 6bb47bb05e remove thread causing hang - issue #94 2012-12-03 21:42:14 +00:00
fossfreedom 10f49f336b re-enable discogs 2012-12-03 19:46:21 +00:00
fossfreedom 16824baa95 wrap up discogs and embedded searching in main plugin 2012-12-03 16:31:24 +00:00
fossfreedom d61b612c4a Update coverart_album.py 2012-11-28 08:09:14 +00:00
fossfreedom 17d74b323a when adding to entry view - sort by track number, fix for issue #97 2012-11-27 23:48:01 +00:00
fossfreedom 7adcf7d21e translation stuff 2012-11-27 14:15:46 +00:00
fossfreedom 5ca9d70613 latest translations 2012-11-27 13:02:01 +00:00
fossfreedom 0afd8ee44c update for installation instructions and use - issue #94 2012-11-26 23:32:18 +00:00
fossfreedom 7bea7a8cea add locally embedded cover search - issue #94 2012-11-26 23:24:00 +00:00
fossfreedom 586b46ed24 Update README.md 2012-11-24 22:33:17 +00:00
257 arquivos alterados com 113891 adições e 7008 exclusões
+3
Ver Arquivo
@@ -7,6 +7,7 @@ dist
build
eggs
parts
.idea
bin
var
sdist
@@ -25,3 +26,5 @@ pip-log.txt
#Mr Developer
.mr.developer.cfg
img/usericons/popups.xml
+621
Ver Arquivo
@@ -0,0 +1,621 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. {http://fsf.org/}
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
+28 -2
Ver Arquivo
@@ -13,14 +13,40 @@ clean:
install:
install -d $(DESTDIR)$(SUBDIR)
install -m 644 *.py $(DESTDIR)$(SUBDIR)
install -m 644 LICENSE.txt $(DESTDIR)$(SUBDIR)
install -d $(DESTDIR)$(DATADIR)img
install -m 644 img/*.png $(DESTDIR)$(DATADIR)img/
install -m 644 img/*.svg $(DESTDIR)$(DATADIR)img/
install -m 644 img/*.xml $(DESTDIR)$(DATADIR)img/
install -d $(DESTDIR)$(DATADIR)img/darker
install -m 644 img/darker/*.png $(DESTDIR)$(DATADIR)img/darker
install -d $(DESTDIR)$(DATADIR)img/dark
install -m 644 img/dark/*.png $(DESTDIR)$(DATADIR)img/dark
install -d $(DESTDIR)$(DATADIR)img/light
install -m 644 img/light/*.png $(DESTDIR)$(DATADIR)img/light
install -d $(DESTDIR)$(DATADIR)img/lighter
install -m 644 img/lighter/*.png $(DESTDIR)$(DATADIR)img/lighter
install -d $(DESTDIR)$(DATADIR)img/standard
install -m 644 img/standard/*.png $(DESTDIR)$(DATADIR)img/standard
install -d $(DESTDIR)$(DATADIR)img/links
install -m 644 img/links/*.png $(DESTDIR)$(DATADIR)img/links
install -d $(DESTDIR)$(DATADIR)template
install -m 644 template/*.xml $(DESTDIR)$(DATADIR)template
install -d $(DESTDIR)$(DATADIR)ui
install -m 644 ui/*.ui $(DESTDIR)$(DATADIR)ui/
install -m 644 coverart_browser.plugin $(DESTDIR)$(SUBDIR)
install -m 644 ui/*.xml $(DESTDIR)$(DATADIR)ui/
install -m 644 ui/*.css $(DESTDIR)$(DATADIR)ui/
install -d $(DESTDIR)$(DATADIR)coverflow
install -d $(DESTDIR)$(DATADIR)coverflow/img
install -m 644 coverflow/*.css $(DESTDIR)$(DATADIR)coverflow/
install -m 644 coverflow/*.js $(DESTDIR)$(DATADIR)coverflow/
install -m 644 coverflow/*.html $(DESTDIR)$(DATADIR)coverflow/
install -m 644 coverflow/LICENSE $(DESTDIR)$(DATADIR)coverflow/
install -m 644 coverflow/img/* $(DESTDIR)$(DATADIR)coverflow/img/
install -m 644 coverart_browser.plugin* $(DESTDIR)$(SUBDIR)
install -d $(DESTDIR)$(DATADIR)tmpl
install -m 644 tmpl/* $(DESTDIR)$(DATADIR)tmpl/
install -d $(DESTDIR)$(GLIB_DIR)
install -m 644 schema/$(GLIB_SCHEME) $(DESTDIR)$(GLIB_DIR)
cd po;./lang.sh $(DESTDIR)$(LOCALEDIR)
cd po;./install_all.sh $(DESTDIR)$(LOCALEDIR)
+94 -154
Ver Arquivo
@@ -1,192 +1,113 @@
coverart-browser v0.6
================
#coverart-browser v2.0
Browse your coverart albums in Rhythmbox v2.96 and later
Browse your coverart albums in Rhythmbox v3 and later.
![Imgur](http://i.imgur.com/XM7KW.png)
![Imgur](http://i.imgur.com/sUgX7d7.png)
-----------
Summary: whats new in this release
##Authors
- Playlist support: display coverart from a playlist, queue & main library
- Playlist support: add albums and selected tracks from the coverart browser to playlists
- Ratings: update album and track ratings from easy-to-use Star visual object
- Sort & Search bar: optional display of these options on the left or right side of Rhythmbox panes
- Sort & Search bar: optional year and rating sort options
- Sort & Search bar: optional genre filter list
- Covers pane: simplified view showing album art first hiding initial search options until requested.
- Plugin Preferences: revamped the preferences dialog to tailor what your see and use.
- Plugin Preferences: both album and artist text under icons are ellipsized to save space
- Plugin Preferences: both album and artist text can use a user defined fixed font size
- asermax <asermax@gmail.com>, website - https://github.com/asermax
How it works:
[![Flattr Button](http://api.flattr.com/button/button-compact-static-100x17.png "Flattr This!")](http://flattr.com/thing/1262052/asermax-on-GitHub "asermax")
- Click the new CoverArt source button (left hand side of screen)
- fossfreedom <foss.freedom@gmail.com>, website - https://github.com/fossfreedom
[![Flattr Button](http://api.flattr.com/button/button-compact-static-100x17.png "Flattr This!")](http://flattr.com/thing/1811704/ "fossfreedom") [![paypaldonate](https://www.paypalobjects.com/en_GB/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KBV682WJ3BDGL)
-----------
##Summary: whats new in this release
- Compact & Full track view toggle option:
1. Compact: fixed track view showing the key album track fields & ratings together with an enlarged album cover
1. Full: Standard track view configurable via the preferences window
- Smart continuous playlist: Play Similar Artists as recommended by EchoNest
- Smart continuous playlist: Play Similar Genres as recommended by EchoNest
- Smart continuous playlist: Play Similar Tracks as recommended by LastFM
- New optional theme: darker toolbar icons by the graphics artist [jrbastien](https://github.com/jrbastien)
- New subtle hover, play & queue icons by the graphics artist [jrbastien](https://github.com/jrbastien)
- Introduce more modern look & feel through subtle animations
- Views button ("…") now can navigate to Play Queue. Allows navigation of key views without the side-bar (F9) being visible
- Artist & Album information pane with LastFM & EchoNest data
- Artist & Album information is fully localised (in your native language) if LastFM returns information in your locale
- Information pane can be made visible or hidden by dragging the pane-handle
- Track Artist Filter and Artist & Album Information panes individually displayed per view
- Support Jump To Playing CTRL+J to scroll to the playing album
- Follow playing song option automatically selects playing album
- Custom Genres are now saved in an alternative folder location to survive re-installation of the plugin
- Search Filter by Composer
- Introduce type-ahead Search filtering to improve searching usability
- Single click to append album to list of playing albums
- Right-click to append album to list of playing albums
- View and modify the list of album tracks being played
- Optional support for [SmallWindow](https://github.com/fossfreedom/smallwindow) plugin - allows Rhythmbox to be toggled between its standard application window and its smaller counterpart
- Translated into 24 languages and locales
- for developers - doxygen documentation: http://fossfreedom.github.io/coverart-browser/classes.html
*How it works:*
- Click the new CoverArt Browser source button (left hand side of screen)
- Albums are displayed as clickable buttons containing their album cover
- Right click menu option to play, queue & search for coverart for an album.
- Download Album & artist artwork via the properties toolbar button
- https://github.com/fossfreedom/coverart-browser/wiki/how-to-for-version-2.0
- https://github.com/fossfreedom/coverart-browser/wiki/Screenshots
![Imgur](http://i.imgur.com/cGUTr.png)
*How to install - Rhythmbox 3.0 and later:*
- multi-select albums to play, queue, add to playlists, search for covers and edit properties
N.B. for earlier Rhythmbox versions use version 1.x
![Imgur](http://i.imgur.com/Od0Bc.png)
Prerequisite is to use a distribution supporting GTK 3.10 or later - for example, Ubuntu 14.04, Arch or Fedora 20
IMPORTANT: For GTK 3.14 users (Fedora 21/Arch etc) please look at https://github.com/fossfreedom/coverart-browser/issues/338 first
- Right click to see and edit the properties for an album:
for debian & debian-based distros such as Ubuntu & Mint:
![Imgur](http://i.imgur.com/U1YyX.png)
sudo apt-get install git gettext python3-mako python3-lxml python3-gi-cairo python3-cairo gstreamer1.0-plugins-ugly gstreamer1.0-plugins-good gstreamer1.0-plugins-bad rhythmbox-plugins
*hint* - Select multiple albums - change both the album name and album artist and all the tracks for
the selected albums will be combined under one album.
for fedora and similar:
- locale support has been added to display text in your native language.
sudo yum install git gettext python3-mako python3-lxml python3-cairo
**HELP WANTED** - We need your help to translate - please help out! (see below)
- Filter your albums
![Imgur](http://i.imgur.com/1QEfH.png)
- Open Coverart Browser from playlist, music queue or music library views
![Imgur](http://i.imgur.com/i7rGj.png)
- Right click to search for missing covers
![Imgur](http://i.imgur.com/QmHzi.png)
- Right click to add albums or tracks to Playlists
![Imgur](http://i.imgur.com/gN6Xd.png)
- Display tracks for an album
![Imgur](http://i.imgur.com/TFzgM.png)
- Find additional coverart
![Imgur](http://i.imgur.com/swQ7R.png)
Either double-click the picture or drag-and-drop to update the coverart for the selected album
Tailor the search for album covers:
![Imgur](http://i.imgur.com/N7cy6.png)
The cover-view accept images dropped from nautilus (for example) or URLs from the web.
- Display Filter and sort options in left or right side of Rhythmbox
![Imgur](http://i.imgur.com/MDfCP.png)
- Optional extra genre filter & new sort options by Favourite Rating and Album Year
![Imgur](http://i.imgur.com/xJIN9.png)
- Rate whole albums & individual tracks to play your favourite albums & tracks from albums
Rate your albums:
![Imgur](http://i.imgur.com/k0rTU.png)
Rate your favourite tracks:
![Imgur](http://i.imgur.com/JWNVH.png)
Then use the Favourites Threshold...
![Imgur](http://i.imgur.com/SMyrL.png)
... to enable you to play your favourite albums and tracks in those albums:
![Imgur](http://i.imgur.com/0fnzv.png)
Lets say you have given some albums a rating - 5 stars, 4 stars, 3 stars etc.
You select lots of albums - right click and chose "add to rated playlist" - only those albums that have a rating are added to the playlist.
You can adjust this in the preferences - favourites threshold. If you set a threshold of - for example - 3 stars, then only albums rated 3 stars and higher are added.
You can fine-tune this with individual tracks - lets say you have 10 tracks in an album. You like only 5 of them. Give the 5 tracks a rating. Then you can play, queue or add just those "favourite" tracks to a playlist.
- Define your own preferences
![Imgur](http://i.imgur.com/rro0A.png)
- Display the name and artist for covers
![Imgur](http://i.imgur.com/3xDfI.png)
- icon-tooltip shows all the track artists for multi-artist albums
- ... and a number of bug-fixes as well.
*How to install:*
1. install *git*
N.B. for debian based distros - `sudo apt-get install git`
2. install the package *gettext*
N.B. for debian based distros - `sudo apt-get install gettext`
NOTE: it is assumed that you have separately installed the patent encumbered codecs found in the good/bad & ugly packages
To install the plugin:
<pre>
rm -rf ~/.local/share/rhythmbox/plugins/coverart_browser
git clone https://github.com/fossfreedom/coverart-browser.git
cd coverart-browser
sh ./install.sh
./install.sh
</pre>
*For Ubuntu 12.04 & 12.10:*
To uninstall the plugin:
This is now available in my rhythmbox PPA - installation instructions in this AskUbuntu Q&A:
<pre>
cd coverart-browser
./install.sh --uninstall
</pre>
Note 1 - the CoverArt Browser plugin also requires installing the following plugin:
- https://github.com/fossfreedom/coverart-search-providers
*For Ubuntu 14.04 and later:*
V2.0 is now available in my rhythmbox PPA - installation instructions in this AskUbuntu Q&A:
http://askubuntu.com/questions/147942/how-do-i-install-third-party-rhythmbox-plugins
**IMPORTANT NOTE**
For Ubuntu 12.04 users that have upgraded to Rhythmbox v2.98 using the webupd8 PPA, this version
of rhythmbox crashes when used with python plugins such as coverart-browser and replaygain.
It is strongly recommended that you either upgrade to 12.10 where v2.98 works great, or
downgrade to v2.96 or v2.97 as per:
- http://askubuntu.com/questions/201093/how-do-i-downgrade-rhythmbox-v2-98
You can use this plugin with v2.98 but you'll need to change the file *coverart_album.py* and
comment out the following lines (i.e. add a # as shown)
<pre>
#self.entry_changed_id = self.db.connect('entry-changed',
# self._entry_changed_callback)
</pre>
If you do this, rhythmbox does not crash - however, if you change and album or track details
via the properties option in the music pane, these changes are not updated in the plugin until
you restart rhythmbox.
Note - installing the package `rhythmbox-plugin-coverart-browser` will also install `rhythmbox-plugin-coverart-search`
**Please help out with translating**
We need you to help us translate the english text to your native language.
Don't worry - it is easier that you think.
Don't worry - it is easier that you think. Just visit:
Instructions are in the file TRANSLATE_README. Post a link to the file as a new issue, or
if you are feeling generous - fork and push a pull-request. Thanks!
- https://translations.launchpad.net/coverartbrowser
If they look scary - just email me (foss dot freedom at gmail dot com) and I'll send you the
file that needs to be translated - it is less than 20 text strings so it should only take a
few minutes.
When emailing - tell me your locale & language. You can find these by typing:
echo $LANG
echo $LANGUAGE
-------
Authors:
The authors of this plugin are fossfreedom <foss.freedom@gmail.com>, Agustín Carrasco <asermax@gmail.com>
Remember to set your preferred language and then just submit your translation.
-------
@@ -194,6 +115,25 @@ Credits:
- thanks to Luqman Aden <laden@uwaterloo.ca> for the coverart-search plugin which our cover-search pane is based upon
- thanks to Canonical for the Star widget which the ratings capabilities use
- our Translators: jrbastien (fr_CA), asermax (es), mateuswetah (pt_BR), jrbastien & lannic (fr.po)
- our Translators: Launchpad Translation team - individual credits for each locale is shown in the plugin preferences dialog
- Button Icons - [jrbastien](https://github.com/jrbastien) for the five toolbar icon-sets
- Flow view is based upon [Contentflow](http://jacksasylum.eu/ContentFlow)
- Chief Tester and all-round good egg - jrbastien!
Licenses:
This plugin code is released under the GPL3+ license.
Contentflow source is released under the MIT license
All translations are released under the BSD license
Genre icon-set:
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/3.0/deed.en_US"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by-nc-nd/3.0/80x15.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/StillImage" property="dct:title" rel="dct:type">Music Genre Icons</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="http://meghnlofing.com" property="cc:attributionName" rel="cc:attributionURL">Meghn Lofing</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/3.0/deed.en_US">Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License</a>
Contrast of the iconset has been altered as agreed by the author. Thanks Meghn!
------
GTK3 port of code.google.com/p/rhythmbox-cover-art-browser
+35 -10
Ver Arquivo
@@ -5,27 +5,52 @@
# Section 1: Developers Only
#+++++++++++++++++++++++++++
#for each ui file run the following to create translation .h files
intltool-extract --type=gettext/glade ui/coverart_browser_prefs.ui
intltool-extract --type=gettext/glade ui/coverart_browser.ui
intltool-extract --type=gettext/glade ui/coverart_entryview.ui
intltool-extract --type=gettext/glade ui/coverart_sidebar.ui
intltool-extract --local --type=gettext/glade ui/coverart_artistview.ui
intltool-extract --local --type=gettext/glade ui/coverart_artist_pop_rb3.ui
intltool-extract --local --type=gettext/glade ui/coverart_browser_pop_rb3.ui
intltool-extract --local --type=gettext/glade ui/coverart_browser_prefs.ui
intltool-extract --local --type=gettext/glade ui/coverart_browser.ui
intltool-extract --local --type=gettext/glade ui/coverart_entryview_compact_pop_rb3.ui
intltool-extract --local --type=gettext/glade ui/coverart_entryview_full_pop_rb3.ui
intltool-extract --local --type=gettext/glade ui/coverart_exportembed.ui
intltool-extract --local --type=gettext/glade ui/coverart_iconview.ui
intltool-extract --local --type=gettext/glade ui/coverart_leftsidebar.ui
intltool-extract --local --type=gettext/glade ui/coverart_listwindow.ui
intltool-extract --local --type=gettext/glade ui/coverart_play_pop_rb3.ui
intltool-extract --local --type=gettext/glade ui/coverart_rightsidebar.ui
intltool-extract --local --type=gettext/glade ui/coverart_topbar.ui
intltool-extract --local --type=gettext/xml img/popups.xml.in
#create a new template file called po/package.pot by running
pygettext -d ui -k _ -k N_ -p po *.py ui/*.h
#create a new template file called po/package.pot by running below
#create a template file for the mako templates
pybabel extract -F babel.cfg -o po/tmpl.pot .
pybabel extract -F babel.cfg -o po/coverartbrowser.pot -c "TRANSLATORS:" .
#now combine the pot files
cd po
msgcat ui.pot tmpl.pot -o package.pot
# po/files_to_be_translated are all the .h files generated by intltool
xgettext -c -a -j -f po/files_to_be_translated -o po/coverartbrowser.pot
# po/py_files_to_be_translated are all the python files to be translated
xgettext -c -j -f po/py_files_to_be_translated -o po/coverartbrowser.pot
#now update existing po's with changes in the template file package.pot
cd po
./update_all_po.sh
#cleanup
cd ..
rm -rf tmp
# Section 2: Translators
#+++++++++++++++++++++++
The best way to create translations is to visit our Launchpad site
- https://translations.launchpad.net/coverartbrowser
Set your preferred language and then just begin translating
If you dont want to use Launchpad, use the following instructions
# Carry out the following - one or the other (or both!)
#create a new locale file from this template:
cd po; msginit --no-translator
+1710 -1181
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+45 -7
Ver Arquivo
@@ -1,16 +1,54 @@
[Plugin]
Loader=python
Loader=python3
Module=coverart_browser
IAge=2
Depends=rb
Depends=rb;coverart_search_providers
Name=CoverArt Browser
Name[es]=Navegador de Portadas
Name[fr]=Navigateur de Jaquettes
Name[ast]=Navegador de portaes
Name[az]=CoverArt gəzgini
Name[bg]=CoverArt Browser
Name[ca]=Navegador de Portades
Name[cs]=Prohlížeč CoverArt
Name[de]=CoverArt Browser
Name[en_GB]=CoverArt Browser
Name[en_US]=CoverArt Browser
Name[es]=Navegador de carátulas
Name[fi]=Kansikuvaselain
Name[fr]=Navigateur de jaquettes
Name[fr_CA]=Navigateur de pochettes
Name[gl]=Navegador de cubertas
Name[hr]=CoverArt Preglednik
Name[it]=Gestore di CoverArt
Name[ko]=앨범 표지 찾아보기
Name[ms]=Pelayar CoverArt
Name[pl]=Przeglądarka okładek
Name[pt]=Arte das Capas
Name[pt_BR]=Arte das Capas
Name[ro]=Navigator CoverArt
Name[zh_CN]=封面浏览器
Description=Browse and play your albums through their covers
Description[es]=Navega y reproduce tu librería de álbums a través de sus portadas
Description[fr]=Parcourer les jaquettes de vos albums et jouer les
Description[ast]=Navegar y reproducir los álbumes per aciu de les portaes
Description[az]=Üz şəklinə görə albomlara bax və oxud
Description[bg]=Търсете и слушайте албумите си посредством обложките им
Description[ca]=Navega i reprodueix els àlbums mitjançant les seues portades
Description[en_GB]=Browse and play your albums through their covers
Description[en_US]=Browse and play your albums through their covers
Description[es]=Navegue y reproduzca sus álbumes mediante sus carátulas
Description[fi]=Selaa ja toista levyjäsi kansikuvien kautta
Description[fr]=Parcourir les jaquettes de vos albums et les jouer
Description[fr_CA]=Parcourir les pochettes de vos albums et les jouer
Description[gl]=Examinar e reproducir os seus álbums a través das súas cubertas
Description[hr]=Pregledavajte i slušajte albume preko omota
Description[it]=Sfoglia e riproduce gli album per copertina
Description[ko]=앨범을 표지로 찾아보고 연주합니다.
Description[ms]=Layar dan mainkan album anda menerusi kulit album mereka
Description[pl]=Przeglądaj i odtwarzaj Twoje albumy według ich okładek
Description[pt]=Navegue e toque seus álbums através de suas capas
Description[pt_BR]=Navegue e toque seus álbums através de suas capas
Description[ro]=Parcurge și redă albumele prin intermediul coperților lor
Description[zh_CN]=浏览封面并播放专辑
Authors=fossfreedom <foss.freedom@gmail.com>, Agustín Carrasco <asermax@gmail.com>
Copyright=© 2012 fossfreedom, Agustín Carrasco © 2007 Alexandre Rosenfeld
Website=http://github.com/fossfreedom
Website=http://github.com/fossfreedom/coverart-browser
Help=https://github.com/fossfreedom/coverart-browser/blob/release-2.0/README.md
Version=2.0
+5 -3
Ver Arquivo
@@ -1,10 +1,12 @@
[Plugin]
Loader=python
Loader=python3
Module=coverart_browser
IAge=2
Depends=rb
Depends=rb;coverart_search_providers
_Name=CoverArt Browser
_Description=Browse and play your albums through their covers
Authors=fossfreedom <foss.freedom@gmail.com>, Agustín Carrasco <asermax@gmail.com>
Copyright=© 2012 fossfreedom, Agustín Carrasco © 2007 Alexandre Rosenfeld
Website=http://github.com/fossfreedom
Website=http://github.com/fossfreedom/coverart-browser
Help=https://github.com/fossfreedom/coverart-browser/blob/release-2.0/README.md
Version=2.0
+188 -78
Ver Arquivo
@@ -18,34 +18,34 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
# define plugin
import rb
import locale
import gettext
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import RB
from gi.repository import GdkPixbuf
from gi.repository import Peas
from gi.repository import Gio
from gi.repository import GLib
from coverart_browser_prefs import Preferences
import rb
from coverart_browser_prefs import GSetting
from coverart_browser_prefs import CoverLocale
from coverart_browser_prefs import Preferences
from coverart_browser_source import CoverArtBrowserSource
from coverart_listview import ListView
from coverart_queueview import QueueView
from coverart_playsourceview import PlaySourceView
from coverart_toolbar import TopToolbar
class CoverArtBrowserEntryType(RB.RhythmDBEntryType):
'''
Entry type for our source.
'''
def __init__(self):
'''
Initializes the entry type.
'''
RB.RhythmDBEntryType.__init__(self, name='CoverArtBrowserEntryType')
class CoverArtBrowserPlugin(GObject.Object, Peas.Activatable):
'''
Main class of the plugin. Manages the activation and deactivation of the
@@ -53,13 +53,12 @@ class CoverArtBrowserPlugin(GObject.Object, Peas.Activatable):
'''
__gtype_name = 'CoverArtBrowserPlugin'
object = GObject.property(type=GObject.Object)
def __init__(self):
'''
Initialises the plugin object.
'''
GObject.Object.__init__(self)
GObject.threads_init()
def do_activate(self):
'''
@@ -68,100 +67,58 @@ class CoverArtBrowserPlugin(GObject.Object, Peas.Activatable):
preferences.
'''
#define .plugin text strings used for translation
plugin = _('CoverArt Browser')
desc = _('Browse and play your albums through their covers')
print "CoverArtBrowser DEBUG - do_activate"
print("CoverArtBrowser DEBUG - do_activate")
self.shell = self.object
self.db = self.shell.props.db
try:
entry_type = CoverArtBrowserEntryType()
self.db.register_entry_type(entry_type)
except NotImplementedError:
entry_type = self.db.entry_register_type(
'CoverArtBrowserEntryType')
self.entry_type = CoverArtBrowserEntryType()
self.db.register_entry_type(self.entry_type)
cl = CoverLocale()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
entry_type.category = RB.RhythmDBEntryCategory.NORMAL
self.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')
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)
iconfile = Gio.File.new_for_path(
rb.find_plugin_file(self, 'img/covermgr_rb3.png'))
group = RB.DisplayPageGroup.get_by_id('library')
self.source = CoverArtBrowserSource(shell=self.shell,
name=_("CoverArt"), entry_type=entry_type,
plugin=self, pixbuf=pxbf,
self.source = CoverArtBrowserSource(
shell=self.shell,
name=_("CoverArt"),
entry_type=self.entry_type,
plugin=self,
icon=Gio.FileIcon.new(iconfile),
query_model=self.shell.props.library_source.props.base_query_model)
self.shell.register_entry_type_for_source(self.source, entry_type)
self.shell.register_entry_type_for_source(self.source, self.entry_type)
self.shell.append_display_page(self.source, group)
self.shell.props.db.connect('load-complete', self.load_complete)
self.source.props.query_model.connect('complete', self.load_complete)
self._externalmenu = ExternalPluginMenu(self)
uim = self.shell.props.ui_manager
self.cover_ui = uim.add_ui_from_file(rb.find_plugin_file(self,
'ui/coverart_plugin.ui'))
action = Gtk.Action(name="PlaylistCover", label=_("CoverArt"),
tooltip=_("Display Covers for Playlist"),
stock_id='gnome-mime-text-x-python')
action.connect('activate', self.display_covers_for_source)
self.action_group = Gtk.ActionGroup(name="PlayListCoverActions")
self.action_group.add_action(action)
uim.insert_action_group(self.action_group, 0)
uim.ensure_update()
print "CoverArtBrowser DEBUG - end do_activate"
def display_covers_for_source(self, action):
'''
Called by Rhythmbox when the user select coverart from popup
menu from a playlist, music or queue source.
This resets the coverart query model to what was chosen before
switching to the coverart browser
'''
print "CoverArtBrowser DEBUG - display_covers_for_source"
page = self.shell.props.selected_page
try:
model = page.get_query_model()
except:
model = self.shell.props.library_source.props.base_query_model
self.source.reset_coverview(model)
self.shell.props.display_page_tree.select(self.source)
print "CoverArtBrowser DEBUG - display_covers_for_source"
cl.switch_locale(cl.Locale.RB)
print("CoverArtBrowser DEBUG - end do_activate")
def do_deactivate(self):
'''
Called by Rhythmbox when the plugin is deactivated. It makes sure to
free all the resources used by the plugin.
'''
print "CoverArtBrowser DEBUG - do_deactivate"
self.shell.props.ui_manager.remove_ui(self.cover_ui)
self.shell.props.ui_manager.remove_action_group(self.action_group)
self.shell.props.ui_manager.ensure_update()
print("CoverArtBrowser DEBUG - do_deactivate")
self.source.delete_thyself()
del self.cover_ui
if self._externalmenu:
self._externalmenu.cleanup()
del self.shell
del self.db
del self.source
del self.action_group
print "CoverArtBrowser DEBUG - end do_deactivate"
print("CoverArtBrowser DEBUG - end do_deactivate")
def load_complete(self, *args, **kwargs):
'''
@@ -171,5 +128,158 @@ class CoverArtBrowserPlugin(GObject.Object, Peas.Activatable):
'''
gs = GSetting()
setting = gs.get_setting(gs.Path.PLUGIN)
if setting[gs.PluginKey.AUTOSTART]:
self.shell.props.display_page_tree.select(self.source)
GLib.idle_add(self.shell.props.display_page_tree.select,
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 %:')
# stop PyCharm removing the Preference import on optimisation
pref = Preferences()
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)
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()
for location in self.locations:
app.remove_plugin_menu_item(location, self.app_id)
self.app_id = None
def _create_menu(self):
app = Gio.Application.get_default()
self.app_id = 'coverart-browser'
self.locations = ['library-toolbar', 'queue-toolbar', 'playsource-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)
toolbar_item.set_label('')
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:
self.action.set_state(self._views.get_action_name(QueueView.name))
#elif page == self.source.playlist_source:
# self.action.set_state(self._views.get_action_name(PlaySourceView.name))
def view_change_cb(self, action, current):
'''
Called when the view state on a page is changed. Sets the new
state.
'''
action.set_state(current)
view_name = self._views.get_view_name_for_action(current)
if view_name != ListView.name and \
view_name != QueueView.name:# and \
#view_name != PlaySourceView.name:
gs = GSetting()
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)
elif view_name == ListView.name:
GLib.idle_add(self.shell.props.display_page_tree.select,
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)
#elif view_name == PlaySourceView.name:
# if not hasattr(self.source, 'playlist_source'):
# return
# print ("test selectable")
# path = self.shell.props.display_page_tree.props.model
# #self.source.activate()
# overlay = self.source.get_children()[0]
# GLib.idle_add(self.shell.props.display_page_tree.select,
# self.source.playlist_source)
+514 -70
Ver Arquivo
@@ -16,16 +16,34 @@
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
import locale
import gettext
import os
import shutil
import webbrowser
from gi.repository import Gio
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import PeasGtk
from gi.repository import Peas
from gi.repository import RB
from gi.repository import Gdk
from gi.repository import GLib
import rb
import locale
import gettext
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
'''
gs = GSetting()
settings = gs.get_setting(gs.Path.PLUGIN)
return settings[gs.PluginKey.WEBKIT]
class CoverLocale:
@@ -40,12 +58,12 @@ class CoverLocale:
# below public variables and methods that can be called for CoverLocale
def __init__(self):
'''
Initializes the singleton interface, asigning all the constants
Initializes the singleton interface, assigning all the constants
used to access the plugin's settings.
'''
self.Locale = self._enum(
RB='rhythmbox',
LOCALE_DOMAIN = 'coverart_browser')
LOCALE_DOMAIN='coverart_browser')
def switch_locale(self, locale_type):
'''
@@ -58,6 +76,14 @@ class CoverLocale:
gettext.textdomain(locale_type)
gettext.install(locale_type)
def get_locale(self):
'''
return the string representation of the users locale
for example
en_US
'''
return locale.getdefaultlocale()[0]
def _enum(self, **enums):
'''
Create an enumn.
@@ -82,9 +108,10 @@ class CoverLocale:
""" Delegate access to implementation """
return setattr(self.__instance, attr, value)
class GSetting:
'''
This class manages the differentes settings that the plugins haves to
This class manages the different settings that the plugin has to
access to read or write.
'''
# storage for the instance reference
@@ -95,7 +122,7 @@ class GSetting:
# below public variables and methods that can be called for GSetting
def __init__(self):
'''
Initializes the singleton interface, asigning all the constants
Initializes the singleton interface, assigning all the constants
used to access the plugin's settings.
'''
self.Path = self._enum(
@@ -106,25 +133,45 @@ class GSetting:
self.PluginKey = self._enum(
CUSTOM_STATUSBAR='custom-statusbar',
DISPLAY_BOTTOM='display-bottom',
DISPLAY_TEXT='display-text',
DISPLAY_TEXT_POS='display-text-pos',
RANDOM='random-queue',
DISPLAY_TEXT_LOADING='display-text-loading',
DISPLAY_TEXT_ELLIPSIZE='display-text-ellipsize',
DISPLAY_TEXT_ELLIPSIZE_LENGTH='display-text-ellipsize-length',
DISPLAY_FONT_SIZE='display-font-size',
COVER_SIZE='cover-size',
ADD_SHADOW='add-shadow',
SHADOW_IMAGE='shadow-image',
PANED_POSITION='paned-position',
SORT_BY_ALBUM='sort-by-album',
SORT_BY_ARTIST='sort-by-artist',
SORT_BY_RATING='sort-by-rating',
SORT_BY_YEAR='sort-by-year',
SORT_BY='sort-by',
SORT_ORDER='sort-order',
YEAR_SORT_VISIBLE='year-sort-visible',
RATING_SORT_VISIBLE='rating-sort-visible',
GENRE_FILTER_VISIBLE='genre-filter-visible',
SORT_BY_ARTIST='sort-by-artist',
SORT_ORDER_ARTIST='sort-order-artist',
RATING='rating-threshold',
AUTOSTART='autostart',
TOOLBAR_POS='toolbar-pos')
TOOLBAR_POS='toolbar-pos',
BUTTON_RELIEF='button-relief',
THEME='theme',
NEW_GENRE_ICON='new-genre-icon',
ICON_PADDING='icon-padding',
ICON_SPACING='icon-spacing',
ICON_AUTOMATIC='icon-automatic',
VIEW_NAME='view-name',
FLOW_APPEARANCE='flow-appearance',
FLOW_HIDE_CAPTION='flow-hide-caption',
FLOW_SCALE='flow-scale',
FLOW_BACKGROUND_COLOUR='flow-background-colour',
FLOW_AUTOMATIC='flow-automatic',
FLOW_WIDTH='flow-width',
FLOW_MAX='flow-max-albums',
WEBKIT='webkit-support',
ARTIST_PANED_POSITION='artist-paned-pos',
USE_FAVOURITES='use-favourites',
ARTIST_INFO_PANED_POSITION='artist-info-paned-pos',
LAST_GENRE_FOLDER='last-genre-folder',
ENTRY_VIEW_MODE='entry-view-mode',
FOLLOWING='following')
self.setting = {}
@@ -135,7 +182,7 @@ class GSetting:
try:
setting = self.setting[path]
except:
self.setting[path] = Gio.Settings(path)
self.setting[path] = Gio.Settings.new(path)
setting = self.setting[path]
return setting
@@ -185,6 +232,9 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
__gtype_name__ = 'CoverArtBrowserPreferences'
object = GObject.property(type=GObject.Object)
GENRE_POPUP = 1
GENRE_LIST = 2
def __init__(self):
'''
Initialises the preferences, getting an instance of the settings saved
@@ -194,115 +244,509 @@ class Preferences(GObject.Object, PeasGtk.Configurable):
gs = GSetting()
self.settings = gs.get_setting(gs.Path.PLUGIN)
self._first_run = True
self._cover_size = 0
self._cover_size_delay = 0
def do_create_configure_widget(self):
'''
Creates the plugin's preferences dialog
'''
return self._create_display_contents(self)
def display_preferences_dialog(self, plugin):
print ("DEBUG - display_preferences_dialog")
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'))
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()
print ("shown")
while True:
response = self._dialog.run()
print ("and run")
if response != Gtk.ResponseType.HELP:
break
self._dialog.hide()
print ("DEBUG - display_preferences_dialog end")
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):
print ("DEBUG - create_display_contents")
# create the ui
self._first_run = True
cl = CoverLocale()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
builder = Gtk.Builder()
builder.add_from_file(rb.find_plugin_file(self,
'ui/coverart_browser_prefs.ui'))
builder.set_translation_domain(cl.Locale.LOCALE_DOMAIN)
builder.add_from_file(rb.find_plugin_file(plugin,
'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.
translators = _('translator-credits')
if translators != "translator-credits":
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_bottom = builder.get_object('display_bottom_checkbox')
self.settings.bind(gs.PluginKey.DISPLAY_BOTTOM, toggle_bottom,
'active', Gio.SettingsBindFlags.DEFAULT)
toggle_statusbar, '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)
toggle_text_loading = builder.get_object(
'display_text_loading_checkbox')
self.settings.bind(gs.PluginKey.DISPLAY_TEXT_LOADING,
toggle_text_loading, 'active', Gio.SettingsBindFlags.DEFAULT)
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')
if self.display_text_pos:
self.display_text_under_radiobutton.set_active(True)
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)
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)
#self.settings.bind(gs.PluginKey.COVER_SIZE, cover_size_scale, 'value',
# Gio.SettingsBindFlags.DEFAULT)
self._cover_size = self.settings[gs.PluginKey.COVER_SIZE]
cover_size_scale.set_value(self._cover_size)
cover_size_scale.connect('value-changed', self.on_cover_size_scale_changed)
add_shadow = builder.get_object('add_shadow_checkbox')
self.settings.bind(gs.PluginKey.ADD_SHADOW, add_shadow, 'active',
Gio.SettingsBindFlags.DEFAULT)
rated_box = builder.get_object('rated_box')
self.stars = ReactiveStar()
self.stars = ReactiveStar(size=StarSize.BIG)
self.stars.connect('changed', self.rating_changed_callback)
rated_box.pack_start(self.stars, False, False, 1)
align = Gtk.Alignment.new(0.5, 0, 0, 0.1)
align.add(self.stars)
rated_box.add(align)
self.stars.set_rating(self.settings[gs.PluginKey.RATING])
autostart = builder.get_object('autostart_checkbox')
self.settings.bind(gs.PluginKey.AUTOSTART,
autostart, 'active', Gio.SettingsBindFlags.DEFAULT)
autostart, 'active', Gio.SettingsBindFlags.DEFAULT)
year_sort_visible = builder.get_object('year_sort_checkbox')
self.settings.bind(gs.PluginKey.YEAR_SORT_VISIBLE,
year_sort_visible, '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)
rating_sort_visible = builder.get_object('rating_sort_checkbox')
self.settings.bind(gs.PluginKey.RATING_SORT_VISIBLE,
rating_sort_visible, 'active', 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)
genre_filter_visible = builder.get_object('genre_filter_checkbox')
self.settings.bind(gs.PluginKey.GENRE_FILTER_VISIBLE,
genre_filter_visible, 'active', Gio.SettingsBindFlags.DEFAULT)
combo_liststore = builder.get_object('combo_liststore')
self.toolbar_left_radio=builder.get_object('toolbar_left_radio')
self.toolbar_right_radio=builder.get_object('toolbar_right_radio')
self.toolbar_main_radio=builder.get_object('toolbar_main_radio')
from coverart_utils import Theme
toolbar_pos = self.settings[gs.PluginKey.TOOLBAR_POS]
if toolbar_pos == 0:
self.toolbar_main_radio.set_active(True)
if toolbar_pos == 1:
self.toolbar_left_radio.set_active(True)
if toolbar_pos == 2:
self.toolbar_right_radio.set_active(True)
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)
button_relief = builder.get_object('button_relief_checkbox')
self.settings.bind(gs.PluginKey.BUTTON_RELIEF, button_relief, 'active',
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]
if not os.path.exists(folder):
os.makedirs(folder)
shutil.copyfile(template, popup)
# now prepare the genre tab
from coverart_utils import GenreConfiguredSpriteSheet
from coverart_utils import get_stock_size
self._sheet = GenreConfiguredSpriteSheet(plugin, "genre", get_stock_size())
self.alt_liststore = builder.get_object('alt_liststore')
self.alt_user_liststore = builder.get_object('alt_user_liststore')
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
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._iters[(key.name, self.GENRE_LIST)] = store_iter
self.amend_mode = False
self.blank_iter = None
self.genre_combobox = builder.get_object('genre_combobox')
self.genre_entry = builder.get_object('genre_entry')
self.genre_view = builder.get_object('genre_view')
self.save_button = builder.get_object('save_button')
self.filechooserdialog = builder.get_object('filechooserdialog')
last_genre_folder = self.settings[gs.PluginKey.LAST_GENRE_FOLDER]
if last_genre_folder != "":
self.filechooserdialog.set_current_folder(last_genre_folder)
padding_scale = builder.get_object('padding_adjustment')
self.settings.bind(gs.PluginKey.ICON_PADDING, padding_scale, 'value',
Gio.SettingsBindFlags.DEFAULT)
spacing_scale = builder.get_object('spacing_adjustment')
self.settings.bind(gs.PluginKey.ICON_SPACING, spacing_scale, 'value',
Gio.SettingsBindFlags.DEFAULT)
icon_automatic = builder.get_object('icon_automatic_checkbox')
self.settings.bind(gs.PluginKey.ICON_AUTOMATIC,
icon_automatic, 'active', Gio.SettingsBindFlags.DEFAULT)
#flow tab
flow_combo = builder.get_object('flow_combobox')
renderer = Gtk.CellRendererText()
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)
flow_hide = builder.get_object('hide_caption_checkbox')
self.settings.bind(gs.PluginKey.FLOW_HIDE_CAPTION,
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)
flow_width = builder.get_object('cover_width_adjustment')
self.settings.bind(gs.PluginKey.FLOW_WIDTH, flow_width, 'value',
Gio.SettingsBindFlags.DEFAULT)
flow_max = builder.get_object('flow_max_adjustment')
self.settings.bind(gs.PluginKey.FLOW_MAX, flow_max, 'value',
Gio.SettingsBindFlags.DEFAULT)
flow_automatic = builder.get_object('automatic_checkbox')
self.settings.bind(gs.PluginKey.FLOW_AUTOMATIC,
flow_automatic, 'active', Gio.SettingsBindFlags.DEFAULT)
self.background_colour = self.settings[gs.PluginKey.FLOW_BACKGROUND_COLOUR]
self.white_radiobutton = builder.get_object('white_radiobutton')
self.black_radiobutton = builder.get_object('black_radiobutton')
if self.background_colour == 'W':
self.white_radiobutton.set_active(True)
else:
self.black_radiobutton.set_active(True)
# return the dialog
return builder.get_object('maingrid')
self._first_run = False
print ("end create dialog contents")
return builder.get_object('main_notebook')
def toolbar_callback( self, radio ):
def on_cover_size_scale_changed(self, scale):
self._cover_size = scale.get_value()
def delay(*args):
print ('delay')
print (self._cover_size_delay)
self._cover_size_delay = self._cover_size_delay + 1
if self._cover_size_delay >=8:
gs = GSetting()
self.settings[gs.PluginKey.COVER_SIZE] = self._cover_size
self._cover_size_delay = 0
return False
return True
if self._cover_size_delay == 0:
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 100, delay, None)
else:
self._cover_size_delay = 1
def on_flow_combobox_changed(self, combobox):
current_val = combobox.get_model()[combobox.get_active()][0]
gs = GSetting()
if radio == self.toolbar_main_radio:
self.settings[gs.PluginKey.TOOLBAR_POS] = 0
if radio == self.toolbar_left_radio:
self.settings[gs.PluginKey.TOOLBAR_POS] = 1
if radio == self.toolbar_right_radio:
self.settings[gs.PluginKey.TOOLBAR_POS] = 2
if self.settings[gs.PluginKey.FLOW_APPEARANCE] != current_val:
if current_val == 'flow-vert':
default_size = 150
else:
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()
if button == self.white_radiobutton:
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 self._first_run:
return
if button.get_active():
gs = GSetting()
if button == self.display_text_under_radiobutton:
self.settings[gs.PluginKey.DISPLAY_TEXT_POS] = True
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()
#self.settings[gs.PluginKey.DISPLAY_TEXT_POS] = True
self.display_text_under_radiobutton.set_active(True)
def rating_changed_callback(self, stars):
print "rating_changed_callback"
print("rating_changed_callback")
gs = GSetting()
self.settings[gs.PluginKey.RATING] = self.stars.get_rating()
def on_save_button_clicked(self, button):
'''
action when genre edit area is saved
'''
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)
self.alt_user_liststore[self._iters[(self.current_genre,
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
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)
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._iters[(key.name, self.GENRE_LIST)] = store_iter
selection = self.genre_view.get_selection()
selection.select_iter(store_iter)
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())
store_iter = self.alt_liststore.append([key.name, self._sheet[key.name]])
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())
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
'''
model, genre_iter = view.get_selected()
if genre_iter:
self.genre_entry.set_text(model[genre_iter][0])
index = model[genre_iter][2]
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')
else:
self.genre_entry.set_text('')
self.genre_combobox.set_active_iter(None)
self.amend_mode = False
if self.blank_iter and self.amend_mode:
try:
index = model[self.blank_iter][0]
if index == '':
model.remove(self.blank_iter)
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
'''
self.genre_entry.set_text('')
self.genre_combobox.set_active(-1)
self.amend_mode = False
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
'''
selection = self.genre_view.get_selection()
model, genre_iter = selection.get_selected()
if genre_iter:
index = rb3compat.unicodestr(model[genre_iter][0], 'utf-8')
model.remove(genre_iter)
if index:
del self._iters[(index, self.GENRE_LIST)]
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
upon the values entered in the genre edit fields
'''
entry_value = self.genre_entry.get_text()
treeiter = self.genre_combobox.get_active_iter()
entry_value = rb3compat.unicodestr(entry_value, 'utf-8')
enable = False
try:
test = self._iters[(entry_value, self.GENRE_LIST)]
if RB.search_fold(self.current_genre) == RB.search_fold(entry_value):
#if the current entry is the same then could save
enable = True
except:
# reach here if this is a brand new entry
enable = True
if treeiter == None or entry_value == None or entry_value == "":
# no icon chosen, or no entry value then nothing to save
enable = False
self.save_button.set_sensitive(enable)
def _toggle_new_genre_state(self):
'''
fire an event - uses gsettings and an object such as a
controller connects to receive the signal that a new or amended
genre has been made
'''
gs = GSetting()
test = self.settings[gs.PluginKey.NEW_GENRE_ICON]
if test:
test = False
else:
test = True
self.settings[gs.PluginKey.NEW_GENRE_ICON] = test
def on_show_launchpad_toggled(self, button):
self.launchpad_label.set_visible(button.get_active())
+997 -1275
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+849
Ver Arquivo
@@ -0,0 +1,849 @@
# -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
#
# Copyright (C) 2012 - fossfreedom
# Copyright (C) 2012 - Agustin Carrasco
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
from datetime import date
from collections import OrderedDict
from collections import namedtuple
from gi.repository import GObject
from gi.repository import Gdk
from gi.repository import RB
from gi.repository import Gio
from gi.repository import GLib
from coverart_browser_prefs import CoverLocale
from coverart_browser_prefs import GSetting
from coverart_utils import create_pixbuf_from_file_at_size
from coverart_utils import GenreConfiguredSpriteSheet
from coverart_utils import ConfiguredSpriteSheet
from coverart_utils import get_stock_size
from coverart_utils import CaseInsensitiveDict
from coverart_utils import Theme
import rb
MenuNodeT = namedtuple('MenuNode', 'label menutype typevalue')
def MenuNode(label, menutype=None, typevalue=None):
return MenuNodeT(label, menutype, typevalue)
class OptionsController(GObject.Object):
# properties
options = GObject.property(type=object, default=None)
current_key = GObject.property(type=str, default=None)
update_image = GObject.property(type=bool, default=False)
enabled = GObject.property(type=bool, default=True)
def __init__(self):
super(OptionsController, self).__init__()
# connect the variations on the current key to the controllers action
self.connect('notify::current-key', self._do_action)
def get_current_key_index(self):
return self.options.index(self.current_key)
def option_selected(self, key):
if key != self.current_key:
# update the current value
self.current_key = key
def _do_action(self, *args):
self.do_action()
def do_action(self):
pass
def get_current_image(self):
return None
def get_current_description(self):
return self.current_key
def update_images(self, *args):
pass
def create_spritesheet(self, plugin, sheet, typestr):
'''
helper function to create a specific spritesheet
'''
if sheet:
del sheet
return ConfiguredSpriteSheet(plugin, typestr, get_stock_size())
def create_button_image(self, plugin, image, icon_name):
'''
helper function to create a button image
'''
if image:
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):
def __init__(self, plugin, album_model):
super(PlaylistPopupController, self).__init__()
self._album_model = album_model
shell = plugin.shell
self.plugin = plugin
# get the library name and initialize the superclass with it
self._library_name = shell.props.library_source.props.name
# get the queue name
self._queue_name = shell.props.queue_source.props.name
if " (" in self._queue_name:
self._queue_name = self._queue_name[0:self._queue_name.find(" (")]
self._spritesheet = None
self._update_options(shell)
# get the playlist model so we can monitor changes
playlist_model = shell.props.display_page_model
# connect signals to update playlists
playlist_model.connect('row-inserted', self._update_options, shell)
playlist_model.connect('row-deleted', self._update_options, shell)
playlist_model.connect('row-changed', self._update_options, shell)
def update_images(self, *args):
self._spritesheet = self.create_spritesheet(self.plugin,
self._spritesheet, 'playlist')
if args[-1]:
self.update_image = True
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
# retrieve the options
values = OrderedDict()
# library and play queue sources
values[self._library_name] = None
values[self._queue_name] = shell.props.queue_source
# playlists
playlists_entries = playlist_manager.get_playlists()
for playlist in playlists_entries:
if playlist.props.is_local:
name = playlist.props.name
values[name] = playlist
still_exists = still_exists or name == self.current_key
self.values = values
self.options = list(values.keys())
self.current_key = self.current_key if still_exists else \
self._library_name
def do_action(self):
playlist = self.values[self.current_key]
if not playlist:
self._album_model.remove_filter('model')
else:
self._album_model.replace_filter('model',
playlist.get_query_model())
def get_current_image(self):
playlist = self.values[self.current_key]
if self.current_key == self._library_name:
image = self._spritesheet['music']
elif self._queue_name in self.current_key:
image = self._spritesheet['queue']
elif isinstance(playlist, RB.StaticPlaylistSource):
image = self._spritesheet['playlist']
else:
image = self._spritesheet['smart']
return image
class GenrePopupController(OptionsController):
# properties
new_genre_icon = GObject.property(type=bool, default=False)
def __init__(self, plugin, album_model):
super(GenrePopupController, self).__init__()
cl = CoverLocale()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
self._album_model = album_model
shell = plugin.shell
self.plugin = plugin
# create a new property model for the genres
genres_model = RB.RhythmDBPropertyModel.new(shell.props.db,
RB.RhythmDBPropType.GENRE)
query = shell.props.library_source.props.base_query_model
genres_model.props.query_model = query
# initial genre
self._initial_genre = _('All Genres')
self._spritesheet = None
self._default_image = None
self._unrecognised_image = None
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')
if args[-1]:
self.update_image = True
def _connect_signals(self, query, genres_model):
# connect signals to update genres
self.connect('notify::new-genre-icon', self._update_options, genres_model)
query.connect('row-inserted', self._update_options, genres_model)
query.connect('row-deleted', self._update_options, genres_model)
query.connect('row-changed', self._update_options, genres_model)
def _connect_properties(self):
gs = GSetting()
setting = gs.get_setting(gs.Path.PLUGIN)
setting.bind(gs.PluginKey.NEW_GENRE_ICON, self, 'new_genre_icon',
Gio.SettingsBindFlags.GET)
def _update_options(self, *args):
genres_model = args[-1]
self.update_images(False)
still_exists = False
# retrieve the options
options = []
row_num = 0
for row in genres_model:
if row_num == 0:
cl = CoverLocale()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
genre = _('All Genres')
row_num = row_num + 1
else:
genre = row[0]
options.append(genre)
still_exists = still_exists or genre == self.current_key
self.options = options
self.current_key = self.current_key if still_exists else \
self._initial_genre
def do_action(self):
'''
called when genre popup menu item chosen
return None if the first entry in popup returned
'''
if self.current_key == self._initial_genre:
self._album_model.remove_filter('genre')
else:
self._album_model.replace_filter('genre', self.current_key)
def get_current_image(self):
test_genre = self.current_key.lower()
if test_genre == self._initial_genre.lower():
image = self._default_image
else:
image = self._find_alternates(test_genre)
if image == self._unrecognised_image and \
test_genre in self._spritesheet:
image = self._spritesheet[test_genre]
return image
def _find_alternates(self, test_genre):
# the following genre checks are required
# 1. if we have user defined genres
# 2. then check locale specific system genres
# 3. then check local specific alternates
# 4. then check if we system genres
# where necessary check if any of the genres are a substring
# of test_genre - check in reverse order so that we
# test largest strings first (prevents spurious matches with
# short strings)
# N.B. we use RB.search_fold since the strings can be
# 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)):
if RB.search_fold(genre) in test_genre:
return self._spritesheet[self._spritesheet.locale_names[genre]]
# next check locale alternates
ret, sprite = self._match_genres(test_genre, self._spritesheet.GENRE_LOCALE)
if ret:
return sprite
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)):
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 test_genre in case_search:
return (True, self._spritesheet[case_search[test_genre]])
else:
return (False, None)
def get_current_description(self):
cl = CoverLocale()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
if self.current_key == self._initial_genre:
return _('All Genres')
else:
return self.current_key
class SortPopupController(OptionsController):
def __init__(self, plugin, viewmgr):
super(SortPopupController, self).__init__()
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')])
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]
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')
if args[-1]:
self.update_image = True
def do_action(self):
sort = self.values[self.current_key]
gs = GSetting()
settings = gs.get_setting(gs.Path.PLUGIN)
settings[gs.PluginKey.SORT_BY] = sort
self._viewmgr.current_view.get_default_manager().emit('sort', "album")
def get_current_image(self):
sort = self.values[self.current_key]
return self._spritesheet[sort]
class ArtistSortPopupController(OptionsController):
def __init__(self, plugin, viewmgr):
super(ArtistSortPopupController, self).__init__()
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_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")
value = 'name_artist'
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)
def update_images(self, *args):
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]
gs = GSetting()
settings = gs.get_setting(gs.Path.PLUGIN)
settings[gs.PluginKey.SORT_BY_ARTIST] = sort
self._viewmgr.current_view.get_default_manager().emit('sort', "artist")
def get_current_image(self):
sort = self.values[self.current_key]
return self._spritesheet[sort]
class PropertiesMenuController(OptionsController):
favourites = GObject.property(type=bool, default=False)
follow = GObject.property(type=bool, default=False)
def __init__(self, plugin, source):
super(PropertiesMenuController, self).__init__()
self._source = source
self.plugin = plugin
self._connect_properties()
# sorts dictionary
cl = CoverLocale()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
# options
self.values = OrderedDict()
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'
self.values[MenuNode('separator1', 'separator')] = ''
self.values[MenuNode(_('Use favourites only'), 'check',
(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')
if self.follow:
self._source.propertiesbutton_callback('follow')
self.current_key = None
def _connect_properties(self):
gs = GSetting()
setting = gs.get_setting(gs.Path.PLUGIN)
setting.bind(
gs.PluginKey.USE_FAVOURITES,
self,
'favourites',
Gio.SettingsBindFlags.DEFAULT)
setting.bind(
gs.PluginKey.FOLLOWING,
self,
'follow',
Gio.SettingsBindFlags.DEFAULT)
def _change_key(self, dict, old, new):
for i in range(len(dict)):
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')
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
if self.current_key == _('Follow playing song'):
self.follow = not self.follow
self._source.propertiesbutton_callback(self.values[key[0]])
self.current_key = None
def get_current_image(self):
return self._image
def get_current_description(self):
return _('Properties')
class DecadePopupController(OptionsController):
def __init__(self, plugin, album_model):
super(DecadePopupController, self).__init__()
self._album_model = album_model
self.plugin = plugin
self._spritesheet = None
# decade options
cl = CoverLocale()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
self.values = OrderedDict()
self.values[_('All Decades')] = [-1, 'All Decades']
#'20s' as in the decade 2010
self.values[_('20s')] = [2020, '20s']
#'10s' as in the decade 2010
self.values[_('10s')] = [2010, '10s']
#'00s' as in the decade 2000
self.values[_('00s')] = [2000, '00s']
#'90s' as in the decade 1990
self.values[_('90s')] = [1990, '90s']
#'80s' as in the decade 1980
self.values[_('80s')] = [1980, '80s']
#'70s' as in the decade 1970
self.values[_('70s')] = [1970, '70s']
#'60s' as in the decade 1960
self.values[_('60s')] = [1960, '60s']
#'50s' as in the decade 1950
self.values[_('50s')] = [1950, '50s']
#'40s' as in the decade 1940
self.values[_('40s')] = [1940, '40s']
#'30s' as in the decade 1930
self.values[_('30s')] = [1930, '30s']
#'Older' as in 'older than the year 1930'
self.values[_('Older')] = [-1, 'Older']
self.options = list(self.values.keys())
# if we aren't on the 20s yet, remove it
if date.today().year < 2020:
self.options.remove(_('20s'))
# 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')
if args[-1]:
self.update_image = True
def do_action(self):
if self.current_key == self._initial_decade:
self._album_model.remove_filter('decade')
else:
self._album_model.replace_filter('decade',
self.values[self.current_key][0])
def get_current_image(self):
decade = self.values[self.current_key][1]
return self._spritesheet[decade]
def get_current_description(self):
return self.current_key
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)])
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)
self.key = self.get_key()
sort_order = self.settings[self.key]
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'))
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='')
def __init__(self, album_model):
super(AlbumSearchEntryController, self).__init__()
self._album_model = album_model
self._filter_type = 'all'
# options
self.values = OrderedDict()
self.values[_('Search all fields')] = 'all'
self.values[_('Search album artists')] = 'album_artist'
self.values[_('Search track artists')] = 'artist'
self.values[_('Search composers')] = 'composers'
self.values[_('Search albums')] = 'album_name'
self.values[_('Search titles')] = 'track'
self.options = list(self.values.keys())
self.current_key = list(self.values.keys())[0]
self._typing = False
self._typing_counter = 0
self._current_search_text = ""
def do_action(self):
# remove old filter
self._album_model.remove_filter(self._filter_type, False)
# assign the new filter
self._filter_type = self.values[self.current_key]
self.do_search(self.search_text, True)
def _search_typing(self, *args):
self._typing_counter = self._typing_counter + 1
if self._typing_counter >= 4 and self._typing:
self._typing = False
self._change_filter(self._current_search_text, False)
return self._typing
def _change_filter(self, search_text, force):
#self.search_text = search_text
self._current_search_text = search_text
if search_text:
self._album_model.replace_filter(self._filter_type,
search_text)
elif not force:
self._album_model.remove_filter(self._filter_type)
def do_search(self, search_text, force=False):
'''
if self.search_text != search_text or force:
self.search_text = search_text
if search_text:
self._album_model.replace_filter(self._filter_type,
search_text)
elif not force:
self._album_model.remove_filter(self._filter_type)
'''
#self.search_text = search_text
if force:
self._typing_counter = 99
self._typing = False
self._change_filter(search_text, force)
return
if self._current_search_text != search_text:
#self.search_text = search_text
self._current_search_text = search_text
self._typing_counter = 0
if not self._typing:
self._typing = True
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 100,
self._search_typing)
class AlbumQuickSearchController(object):
def __init__(self, album_manager):
self._album_manager = album_manager
def connect_quick_search(self, quick_search):
quick_search.connect('quick-search', self._on_quick_search)
quick_search.connect('arrow-pressed', self._on_arrow_pressed)
quick_search.connect('hide', self._on_hide)
def _on_quick_search(self, quick_search, search_text, *args):
album = self._album_manager.model.find_first_visible('album_name',
search_text)
if album:
path = self._album_manager.model.get_path(album)
self._album_manager.current_view.select_and_scroll_to_path(path)
def _on_arrow_pressed(self, quick_search, key, *args):
current = self._album_manager.current_view.get_selected_objects()[0]
search_text = quick_search.get_text()
album = None
if key == Gdk.KEY_Up:
album = self._album_manager.model.find_first_visible(
'album_name', search_text, current, True)
elif key == Gdk.KEY_Down:
album = self._album_manager.model.find_first_visible(
'album_name', search_text, current)
if album:
path = self._album_manager.model.get_path(album)
self._album_manager.current_view.select_and_scroll_to_path(path)
def _on_hide(self, quick_search, *args):
self._album_manager.current_view.grab_focus()
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)
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:
self.current_key = key
def do_action(self):
if self._viewmgr.view_name != self.values[self.current_key]:
self._viewmgr.view_name = self.values[self.current_key]
+436
Ver Arquivo
@@ -0,0 +1,436 @@
# -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
#
# Copyright (C) 2012 - fossfreedom
# Copyright (C) 2012 - Agustin Carrasco
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
import json
import os
from xml.sax.saxutils import escape
from gi.repository import Gdk
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import GObject
from gi.repository import Gio
from coverart_browser_prefs import GSetting
from coverart_browser_prefs import webkit_support
from coverart_widgets import AbstractView
from coverart_widgets import PanedCollapsible
import rb
class FlowShowingPolicy(GObject.Object):
'''
Policy that mostly takes care of how and when things should be showed on
the view that makes use of the `AlbumsModel`.
'''
def __init__(self, flow_view):
super(FlowShowingPolicy, self).__init__()
self._flow_view = flow_view
self.counter = 0
self._has_initialised = False
def initialise(self, album_manager):
if self._has_initialised:
return
self._has_initialised = True
self._album_manager = album_manager
self._model = album_manager.model
class CoverFlowView(AbstractView):
__gtype_name__ = "CoverFlowView"
name = 'coverflowview'
#properties
flow_background = GObject.property(type=str, default='W')
flow_automatic = GObject.property(type=bool, default=False)
flow_scale = GObject.property(type=int, default=100)
flow_hide = GObject.property(type=bool, default=False)
flow_width = GObject.property(type=int, default=600)
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)
settings.bind(gs.PluginKey.FLOW_HIDE_CAPTION, self,
'flow_hide', Gio.SettingsBindFlags.GET)
settings.bind(gs.PluginKey.FLOW_SCALE, self,
'flow_scale', Gio.SettingsBindFlags.GET)
settings.bind(gs.PluginKey.FLOW_AUTOMATIC, self,
'flow_automatic', Gio.SettingsBindFlags.GET)
settings.bind(gs.PluginKey.FLOW_BACKGROUND_COLOUR, self,
'flow_background', Gio.SettingsBindFlags.GET)
settings.bind(gs.PluginKey.FLOW_WIDTH, self,
'flow_width', Gio.SettingsBindFlags.GET)
settings.bind(gs.PluginKey.FLOW_MAX, self,
'flow_max', Gio.SettingsBindFlags.GET)
def _connect_signals(self, source):
self.connect('notify::flow-background',
self.filter_changed)
self.connect('notify::flow-scale',
self.filter_changed)
self.connect('notify::flow-hide',
self.filter_changed)
self.connect('notify::flow-width',
self.filter_changed)
self.connect('notify::flow-appearance',
self.filter_changed)
self.connect('notify::flow-max',
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:
return
self._filter_changed_inprogress = True
def filter_events(*args):
if not self._filter_changed_event:
self._filter_changed()
self._filter_changed_inprogress = False
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:
foreground_colour = 'white'
else:
foreground_colour = 'black'
else:
background_colour = 'black'
if len(self.album_manager.model.store) <= self.flow_max:
foreground_colour = 'black'
else:
foreground_colour = 'white'
string = string.replace('#BACKGROUND_COLOUR', background_colour)
string = string.replace('#FOREGROUND_COLOUR', foreground_colour)
string = string.replace('#FACTOR', str(float(self.flow_scale) / 100))
if self.flow_hide:
caption = ""
else:
caption = '<div class="globalCaption"></div>'
string = string.replace('#GLOBAL_CAPTION', caption)
addon = background_colour
if self.flow_appearance == 'flow-vert':
addon += " vertical"
elif self.flow_appearance == 'carousel':
addon += " carousel"
elif self.flow_appearance == 'roundabout':
addon += " roundabout"
string = string.replace('#ADDON', addon)
string = string.replace('#WIDTH', str(self.flow_width))
identifier = self.flow.get_identifier(self.last_album)
if not identifier:
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>')
items = self.flow.initialise(self.album_manager.model, self.flow_max)
string = string.replace('#ITEMS', items)
base = os.path.dirname(path) + "/"
Gdk.threads_enter()
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))
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)
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:
self.album_manager.cover_man.connect('load-finished', self._covers_loaded)
else:
self._covers_loaded()
def _covers_loaded(self, *args):
self.flow = FlowControl(self)
self.view.connect("notify::title", self.flow.receive_message_signal)
#self.album_manager.model.connect('album-updated', self.flow.update_album, self.view)
#self.album_manager.model.connect('visual-updated', self.flow.update_album, self.view)
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
def last_album(self):
return self._last_album
@last_album.setter
def last_album(self, new_album):
if self._last_album != new_album:
self._last_album = new_album
self.source.click_count = 0
self.selectionchanged_callback()
def item_rightclicked_callback(self, album):
self.last_album = album
self.popup.popup(self.source, 'popup_menu', 3, Gtk.get_current_event_time())
def item_clicked_callback(self, album):
'''
Callback called when the user clicks somewhere on the flow_view.
Along with source "show_hide_pane", takes care of showing/hiding the bottom
pane after a second click on a selected album.
'''
# 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)
def item_activated_callback(self, album):
'''
Callback called when the flow view is double clicked. It plays the selected album
'''
self.last_album = album
self.source.play_selected_album()
return True
def item_drop_callback(self, album, webpath):
'''
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)
self.album_manager.cover_man.update_cover(album, uri=webpath)
def get_selected_objects(self):
if self.last_album:
return [self.last_album]
else:
return []
def select_and_scroll_to_path(self, path):
album = self.source.album_manager.model.get_from_path(path)
self.flow.scroll_to_album(album, self.view)
self.item_clicked_callback(album)
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:
if self.album_identifier[row] == album:
index = row
break
if index == -1:
return None
else:
return row
def update_album(self, model, album_path, album_iter, webview):
album = model.get_from_path(album_path)
index = -1
for row in self.album_identifier:
if self.album_identifier[row] == album:
index = row
break
if index == -1:
return
obj = {}
obj['filename'] = album.cover.original
obj['title'] = album.artist
obj['caption'] = album.name
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
args = json.loads(title)
try:
signal = args["signal"]
except:
print("unhandled: %s " % title)
return
if signal == 'clickactive':
self.callback_view.item_clicked_callback(self.album_identifier[int(args['param'][0])])
elif signal == 'rightclickactive':
self.callback_view.item_rightclicked_callback(
self.album_identifier[int(args['param'][0])])
elif signal == 'doubleclickactive':
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])
else:
print("unhandled signal: %s" % signal)
def scroll_to_album(self, album, webview):
for row in self.album_identifier:
if self.album_identifier[row] == album:
webview.execute_script("scroll_to_identifier('%s')" % str(row))
break
def initialise(self, model, max_covers):
album_col = model.columns['album']
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>'
for row in model.store:
cover = row[album_col].cover.original
cover = cover.replace(
'rhythmbox-missing-artwork.svg',
'rhythmbox-missing-artwork.png') ## need a white vs black when we change the background colour
self.album_identifier[index] = row[album_col]
items += html_elements(
fullfilename=cover,
caption=row[album_col].name,
title=row[album_col].artist,
identifier=str(index))
index += 1
if index == max_covers:
break
if index != 0:
#self.callback_view.last_album = self.album_identifier[0]
pass
else:
self.callback_view.last_album = None
return items
+690
Ver Arquivo
@@ -0,0 +1,690 @@
# -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
#
# Copyright (C) 2012 - fossfreedom
# Copyright (C) 2012 - Agustin Carrasco
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
from gi.repository import Gdk
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import GObject
from gi.repository import Gio
from gi.repository import Pango
from gi.repository import PangoCairo
from gi.repository import GdkPixbuf
from coverart_widgets import EnhancedIconView
from coverart_browser_prefs import GSetting
from coverart_browser_prefs import CoverLocale
from coverart_album import AlbumsModel
from coverart_widgets import AbstractView
from coverart_widgets import PanedCollapsible
import coverart_rb3compat as rb3compat
import rb
import gettext
PLAY_SIZE_X = 30
PLAY_SIZE_Y = 30
class CellRendererThumb(Gtk.CellRendererPixbuf):
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):
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)
Gdk.cairo_set_source_pixbuf(cr, pixbuf, x_offset, y_offset)
cr.paint()
alpha = 0.40
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)
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):
return
#PANGO LAYOUT
layout_width = cell_area.width - 2
pango_layout = PangoCairo.create_layout(cr)
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_wrap(Pango.WrapMode.WORD_CHAR)
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):
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))
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,
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)
)
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, "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)
setting.bind(gs.PluginKey.DISPLAY_TEXT_POS, self, 'display-text-pos',
Gio.SettingsBindFlags.GET)
setting.bind(gs.PluginKey.DISPLAY_TEXT, self, 'display-text',
Gio.SettingsBindFlags.GET)
class AlbumShowingPolicy(GObject.Object):
'''
Policy that mostly takes care of how and when things should be showed on
the view that makes use of the `AlbumsModel`.
'''
def __init__(self, cover_view):
super(AlbumShowingPolicy, self).__init__()
self._cover_view = cover_view # this will need to be reworked for all views
self._visible_paths = None
self._has_initialised = False
def initialise(self, album_manager):
if self._has_initialised:
return
self._album_manager = album_manager
self._model = album_manager.model
self._connect_signals()
self._has_initialised = True
def _connect_signals(self):
self._cover_view.props.vadjustment.connect('value-changed',
self._viewport_changed)
self._model.connect('album-updated', self._album_updated)
self._model.connect('visual-updated', self._album_updated)
def _viewport_changed(self, *args):
visible_range = self._cover_view.get_visible_range()
if visible_range:
init, end = visible_range
# i have to use the tree iter instead of the path to iterate since
# for some reason path.next doesn't work with the filtermodel
tree_iter = self._model.store.get_iter(init)
self._visible_paths = []
while init and init != end:
self._visible_paths.append(init)
tree_iter = self._model.store.iter_next(tree_iter)
init = self._model.store.get_path(tree_iter)
self._visible_paths.append(end)
def _album_updated(self, model, album_path, album_iter):
# get the currently showing paths
if not self._visible_paths:
self._viewport_changed()
if (album_path and self._visible_paths) and album_path in self._visible_paths:
# 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"
icon_spacing = GObject.property(type=int, default=0)
icon_padding = GObject.property(type=int, default=0)
icon_automatic = GObject.property(type=bool, default=True)
display_text_enabled = GObject.property(type=bool, default=False)
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"):
super(CoverIconView, self).__init__(cell_area=AlbumArtCellArea(), *args, **kwargs)
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.gs = GSetting()
# custom text renderer
self._text_renderer = None
self.show_policy = AlbumShowingPolicy(self)
self.view = self
self._has_initialised = False
self._last_path = None
self._calc_motion_step = 0
def initialise(self, source):
if self._has_initialised:
return
self._has_initialised = True
self.view_name = "covers_view"
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)
self.drag_dest_add_image_targets()
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.source.paned.connect("expanded", self.bottom_expander_expanded_callback)
# lastly support drag-drop from coverart to devices/nautilus etc
self.connect('drag-begin', self.on_drag_begin)
self.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK,
[], 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)])
# 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,
}
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._connect_properties()
self._connect_signals()
self._activate_markup()
self.on_notify_icon_padding()
self.on_notify_icon_spacing()
def _connect_properties(self):
setting = self.gs.get_setting(self.gs.Path.PLUGIN)
setting.bind(
self.gs.PluginKey.ICON_SPACING,
self,
'icon_spacing',
Gio.SettingsBindFlags.GET)
setting.bind(
self.gs.PluginKey.ICON_PADDING,
self,
'icon_padding',
Gio.SettingsBindFlags.GET)
setting.bind(self.gs.PluginKey.DISPLAY_TEXT, self,
'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)
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('notify::icon-spacing',
self.on_notify_icon_spacing)
self.connect('notify::icon-padding',
self.on_notify_icon_padding)
self.connect('notify::display-text-enabled',
self._activate_markup)
self.connect('notify::display-text-pos',
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):
'''
Callback called when to resize the icon
[common to all views]
'''
self.set_item_width(cover_size)
def on_drag_drop(self, widget, context, x, y, time):
'''
Callback called when a drag operation finishes over the cover view
of the source. It decides if the dropped item can be processed as
an image to use as a cover.
'''
# stop the propagation of the signal (deactivates superclass callback)
widget.stop_emission_by_name('drag-drop')
# obtain the path of the icon over which the drag operation finished
path, pos = widget.get_dest_item_at_pos(x, y)
result = path is not None
if result:
target = self.drag_dest_find_target(context, None)
widget.drag_get_data(context, target, time)
return result
def on_drag_data_received(self, widget, drag_context, x, y, data, info,
time):
'''
Callback called when the drag source has prepared the data (pixbuf)
for us to use.
'''
# stop the propagation of the signal (deactivates superclass callback)
widget.stop_emission_by_name('drag-data-received')
# get the album and the info and ask the loader to update the cover
path, pos = widget.get_dest_item_at_pos(x, y)
album = widget.get_model()[path][2]
pixbuf = data.get_pixbuf()
if pixbuf:
self.album_manager.cover_man.update_cover(album, pixbuf)
else:
uri = data.get_text()
self.album_manager.cover_man.update_cover(album, uri=uri)
# call the context drag_finished to inform the source about it
drag_context.finish(True, False, time)
def on_drag_data_get(self, widget, drag_context, data, info, time):
'''
Callback called when the drag destination (playlist) has
requested what album (icon) has been dragged
'''
uris = []
for album in widget.get_selected_objects():
for track in album.get_tracks():
uris.append(track.location)
sel = data.set_uris(uris)
# stop the propagation of the signal (deactivates superclass callback)
widget.stop_emission_by_name('drag-data-get')
def on_drag_begin(self, widget, context):
'''
Callback called when the drag-drop from coverview has started
Changes the drag icon as appropriate
'''
album_number = len(widget.get_selected_objects())
if album_number == 1:
item = Gtk.STOCK_DND
else:
item = Gtk.STOCK_DND_MULTIPLE
widget.drag_source_set_icon_stock(item)
widget.stop_emission_by_name('drag-begin')
def _cover_play_hotspot(self, path, in_vacinity=False):
if path:
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 - (self.icon_padding / 2) - (self.icon_spacing / 2)
c_y = cursor_y - rect.y - (self.icon_padding / 2) - (self.icon_spacing / 2)
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 > 0 and \
c_y > 0:
return True
return False
def on_pointer_motion(self, widget, event):
self._current_mouse_x = event.x
self._current_mouse_y = event.y
if self._calc_motion_step == 0:
self._calc_motion_step = 1
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 100,
self._calculate_hotspot)
else:
path = self.get_path_at_pos(self._current_mouse_x,
self._current_mouse_y)
if not self._last_path or self._last_path != path:
self._display_icon(None, self._last_path)
def _display_icon(self, icon, path):
self.props.cell_area.hover_pixbuf = icon
if path:
valid, rect = self.get_cell_rect(path, None)
self.props.window.invalidate_rect(rect, True)
self.queue_draw()
def _calculate_hotspot(self, *args):
path = self.get_path_at_pos(self._current_mouse_x,
self._current_mouse_y)
# if the current path was not the same as the last path then
# reset the counter
if not self._last_path or self._last_path != path:
self._display_icon(None, self._last_path)
self._last_path = path
self._calc_motion_step = 0
return False
self._calc_motion_step = self._calc_motion_step + 1
# if havent yet reached the requisite number of steps then
# let the thread roll to the next increment
if self._calc_motion_step < 8:
return True
if not self._cover_play_hotspot(path, in_vacinity = True):
# we are not near the hot-spot so decrement the counter
# hoping next time around we are near
self._calc_motion_step = self._calc_motion_step - 1
self._display_icon(None, self._last_path)
return True
# from here on in, we are going to display a hotspot icon
# so lets decide which one
(_, playing) = self.shell.props.shell_player.get_playing()
calc_path = -1
if playing:
entry = self.shell.props.shell_player.get_playing_entry()
album = self.album_manager.model.get_from_dbentry(entry)
calc_path = self.album_manager.model.get_path(album)
if playing and calc_path == path:
icon = 'button_playpause'
elif playing:
icon = 'button_queue'
else:
icon = 'button_play'
# now we've got the icon - lets double check that we are
# actually hovering exactly on the hotspot because the icon will visually change
exact_hotspot = self._cover_play_hotspot(path)
if exact_hotspot:
icon = icon + '_hover'
hover = self.hover_pixbufs[icon]
self._display_icon(hover, path)
self._calc_motion_step = self._calc_motion_step - 1
return True
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:
self._last_path = path
self.shell.props.shell_player.pause()
self.on_pointer_motion(self, event)
return
# 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
self.source.queue_selected_album(None, self.source.favourites)
album = self.get_selected_objects()[0]
cl = CoverLocale()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
message = gettext.gettext('Album has added to list of playing albums')
self.display_notification(album.name,
message,
album.cover.original)
else: # otherwise just play it
self._last_path = path
self.source.play_selected_album(self.source.favourites)
icon = 'button_play_hover'
self.props.cell_area.hover_pixbuf = \
self.hover_pixbufs[icon]
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 250,
delay, None)
return
# to expand the entry view
ctrl = event.state & Gdk.ModifierType.CONTROL_MASK
shift = event.state & Gdk.ModifierType.SHIFT_MASK
if self.icon_automatic:
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) \
if path else None
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 250,
self.source.show_hide_pane, album)
def item_activated_callback(self, iconview, path):
'''
Callback called when the cover view is double clicked or space-bar
is pressed. It plays the selected album
'''
self.source.play_selected_album(self.source.favourites)
return True
def on_notify_icon_padding(self, *args):
'''
Callback called when the icon-padding gsetting value is changed
'''
self.set_item_padding(self.icon_padding)
def on_notify_icon_spacing(self, *args):
'''
Callback called when the icon-spacing gsetting value is changed
'''
self.set_row_spacing(self.icon_spacing)
self.set_column_spacing(self.icon_spacing)
def _create_and_configure_renderer(self):
#Add own cellrenderer
self._text_renderer = Gtk.CellRendererText()
self._text_renderer.props.alignment = Pango.Alignment.CENTER
self._text_renderer.props.wrap_mode = Pango.WrapMode.WORD
self._text_renderer.props.xalign = 0.5
self._text_renderer.props.yalign = 0
self._text_renderer.props.width = \
self.album_manager.cover_man.cover_size
self._text_renderer.props.wrap_width = \
self.album_manager.cover_man.cover_size
def _activate_markup(self, *args):
'''
Utility method to activate/deactivate the markup text on the
cover view.
'''
if self.display_text_enabled and self.display_text_pos:
if not self._text_renderer:
# create and configure the custom cell renderer
self._create_and_configure_renderer()
# set the renderer
self.pack_end(self._text_renderer, False)
self.add_attribute(self._text_renderer,
'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
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
'''
if expand:
# accommodate the viewport if there's an album selected
if self.source.last_selected_album:
def scroll_to_album(*args):
# accommodate the viewport if there's an album selected
path = self.album_manager.model.get_path(
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)
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):
super(EnhancedIconView, self).grab_focus()
+728 -109
Ver Arquivo
@@ -17,16 +17,399 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
from collections import OrderedDict
from gi.repository import RB
from gi.repository import Gtk
from gi.repository import GObject
from gi.repository import GdkPixbuf
from gi.repository import Gdk
from gi.repository import GLib
import rb
from coverart_rb3compat import Menu
from coverart_rb3compat import ActionGroup
from coverart_browser_prefs import GSetting
from coverart_browser_prefs import CoverLocale
from coverart_external_plugins import CreateExternalPluginMenu
from coverart_playlists import LastFMTrackPlaylist
from coverart_playlists import EchoNestPlaylist
from coverart_playlists import EchoNestGenrePlaylist
from coverart_utils import create_button_image
from coverart_external_plugins import ExternalPlugin
from stars import ReactiveStar
from coverart_search import CoverSearchPane
from coverart_widgets import PixbufButton
class CoverArtEntryView(RB.EntryView):
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()
self.entry_view_grid = entry_view_grid
self.shell = shell
self.viewmgr = viewmgr
self.plugin = plugin
self.source = source
# setup entry-view objects and widgets
self.stack = Gtk.Stack()
self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
self.stack.set_transition_duration(750)
# create entry views. Don't allow to reorder until the load is finished
self.entry_view_compact = CoverArtCompactEntryView(self.shell, self.source)
self.entry_view_full = CoverArtEntryView(self.shell, self.source)
self.entry_view = self.entry_view_compact
self.shell.props.library_source.get_entry_view().set_columns_clickable(
False)
self.entry_view_results = ResultsGrid()
self.entry_view_results.initialise(self.entry_view_grid)
self.stack.add_titled(self.entry_view_results, "notebook_tracks", _("Tracks"))
self.entry_view_grid.attach(self.stack, 0, 0, 3, 1)
def setup_source(self):
colour = self.viewmgr.get_selection_colour()
self.cover_search_pane = CoverSearchPane(self.plugin, colour)
self.stack.add_titled(self.cover_search_pane, "notebook_covers", _("Covers"))
# define entry-view toolbar
self.stars = ReactiveStar()
self.stars.set_rating(0)
self.stars.connect('changed', self.rating_changed_callback)
self.stars.props.valign = Gtk.Align.CENTER
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)
viewtoggle = PixbufButton()
viewtoggle.set_image(create_button_image(self.plugin, "entryview.png"))
self.viewtoggle_id = None
setting = self.gs.get_setting(self.gs.Path.PLUGIN)
viewtoggle.set_active(not setting[self.gs.PluginKey.ENTRY_VIEW_MODE])
self.entry_view_toggled(viewtoggle, True)
viewtoggle.connect('toggled', self.entry_view_toggled)
smallwindowbutton = PixbufButton()
smallwindowbutton.set_image(create_button_image(self.plugin, "view-restore.png"))
smallwindowbutton.connect('toggled', self.smallwindowbutton_callback)
self.smallwindowext = ExternalPlugin()
self.smallwindowext.appendattribute('plugin_name', 'smallwindow')
self.smallwindowext.appendattribute('action_group_name', 'small window actions')
self.smallwindowext.appendattribute('action_name', 'SmallWindow')
self.smallwindowext.appendattribute('action_type', 'app')
whatsplayingtoggle = PixbufButton()
whatsplayingtoggle.set_image(create_button_image(self.plugin, "whatsplaying.png"))
whatsplayingtoggle.connect('toggled', self.whatsplayingtoggle_callback)
rightgrid = Gtk.Grid()
rightgrid.props.halign = Gtk.Align.END
#rightgrid.attach(whatsplayingtoggle, 0, 0, 1, 1)
rightgrid.attach(viewtoggle, 1, 0, 1, 1)
rightgrid.attach(smallwindowbutton, 2, 0, 1, 1)
self.entry_view_grid.attach_next_to(rightgrid, self.stars, Gtk.PositionType.RIGHT, 1, 1)
self.stack.set_visible_child(self.entry_view_results)
self.stack.connect('notify::visible-child-name', self.notebook_switch_page_callback)
self.entry_view_grid.show_all()
smallwindowbutton.set_visible(self.smallwindowext.is_activated())
def whatsplayingtoggle_callback(self, widget):
self.entry_view_results.emit('whats-playing', widget.get_active())
def smallwindowbutton_callback(self, widget):
if widget.get_active():
self.smallwindowext.activate(self.shell)
widget.emit('clicked')
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
if self.viewtoggle_id:
self.shell.props.window.disconnect(self.viewtoggle_id)
self.viewtoggle_id = None
else:
next_view = self.entry_view_compact
show_coverart = True
self.viewtoggle_id = self.shell.props.window.connect('check_resize', self.entry_view_results.window_resize)
setting = self.gs.get_setting(self.gs.Path.PLUGIN)
setting[self.gs.PluginKey.ENTRY_VIEW_MODE] = not widget.get_active()
self.entry_view_results.change_view(next_view, show_coverart)
self.entry_view = next_view
if not initialised:
self.source.update_with_selection()
def notebook_switch_page_callback(self, *args):
'''
Callback called when the notebook page gets switched. It initiates
the cover search when the cover search pane's page is selected.
'''
print("CoverArtBrowser DEBUG - notebook_switch_page_callback")
if self.stack.get_visible_child_name() == 'notebook_covers':
self.viewmgr.current_view.switch_to_coverpane(self.cover_search_pane)
else:
entries = self.entry_view.get_selected_entries()
if entries and len(entries) > 0:
self.entry_view_results.emit('update-cover', self.source, entries[0])
else:
selected = self.viewmgr.current_view.get_selected_objects()
tracks = selected[0].get_tracks()
self.entry_view_results.emit('update-cover', self.source, tracks[0].entry)
print("CoverArtBrowser DEBUG - end notebook_switch_page_callback")
def rating_changed_callback(self, widget):
'''
Callback called when the Rating stars is changed
'''
print("CoverArtBrowser DEBUG - rating_changed_callback")
rating = widget.get_rating()
for album in self.viewmgr.current_view.get_selected_objects():
album.rating = rating
print("CoverArtBrowser DEBUG - end rating_changed_callback")
def get_entry_view(self):
return self.entry_view
def update_cover(self, album_artist, manager):
if not self.stack.get_visible_child_name() == "notebook_covers":
return
self.cover_search_pane.clear()
self.cover_search(album_artist, manager)
def cover_search(self, album_artist, manager):
self.cover_search_pane.do_search(album_artist,
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")
selected = self.viewmgr.current_view.get_selected_objects()
# clear the entry view
self.entry_view.clear()
cover_search_pane_visible = self.stack.get_visible_child_name() == "notebook_covers"
if not selected:
# clean cover tab if selected
if cover_search_pane_visible:
self.cover_search_pane.clear()
self.entry_view_results.emit('update-cover', self.source, None)
return last_selected_album, click_count
elif len(selected) == 1:
self.stars.set_rating(selected[0].rating)
if selected[0] is not last_selected_album:
# when the selection changes we've to take into account two
# things
if not click_count:
# we may be using the arrows, so if there is no mouse
# involved, we should change the last selected
last_selected_album = selected[0]
else:
# we may've doing a fast change after a valid second click,
# so it shouldn't be considered a double click
click_count -= 1
else:
self.stars.set_rating(0)
if len(selected) == 1:
self.source.artist_info.emit('selected',
selected[0].artist,
selected[0].name)
self.entry_view.set_sorting_order('track-number', Gtk.SortType.ASCENDING)
for album in selected:
# add the album to the entry_view
self.entry_view.add_album(album)
if len(selected) > 0:
def cover_update(*args):
print ("emitting")
self.entry_view_results.emit('update-cover',
self.source,
selected[0].get_tracks()[0].entry)
# add a short delay to give the entry-pane time to expand etc.
Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 250, cover_update, None)
# 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)
return last_selected_album, click_count
class ResultsGrid(Gtk.Grid):
# signals
__gsignals__ = {
'update-cover': (GObject.SIGNAL_RUN_LAST, None, (GObject.Object, RB.RhythmDBEntry)),
'whats-playing': (GObject.SIGNAL_RUN_LAST, None, (bool,))
}
image_width = 0
def __init__(self, *args, **kwargs):
super(ResultsGrid, self).__init__(*args, **kwargs)
def initialise(self, entry_view_grid):
self.pixbuf = None
self.entry_view_grid = entry_view_grid
self.oldval = 0
self.stack = Gtk.Stack()
self.stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
self.stack.set_transition_duration(350)
self.image1 = Gtk.Image()
self.image1.props.hexpand = True
self.image1.props.vexpand = True
self.stack.add_named(self.image1, "image1")
self.image2 = Gtk.Image()
self.image2.props.hexpand = True
self.image2.props.vexpand = True
self.stack.add_named(self.image2, "image2")
self.frame = Gtk.Frame.new() #"", 0.5, 0.5, 1, False)
self.update_cover(None, None, None)
self.scroll = Gtk.ScrolledWindow()
self.scroll.add(self.stack)
self.scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
self.scroll.set_resize_mode(Gtk.ResizeMode.QUEUE)
self.frame.add(self.scroll)
self._signal_connected = None
self.attach(self.frame, 6, 0, 1, 1)
self.connect('update-cover', self.update_cover)
self.connect('whats-playing', self.display_whats_playing)
#lets fix the situation where some-themes background colour is incorrectly defined
#in these cases the background colour is black
context = self.get_style_context()
bg_colour = context.get_background_color(Gtk.StateFlags.NORMAL)
if bg_colour == Gdk.RGBA(0, 0, 0, 0):
color = context.get_color(Gtk.StateFlags.NORMAL)
self.override_background_color(Gtk.StateType.NORMAL, color)
def update_cover(self, widget, source, entry):
print ('update_cover')
self.oldval = 0 # force a redraw
if entry:
print ('entry')
album = source.album_manager.model.get_from_dbentry(entry)
self.pixbuf = GdkPixbuf.Pixbuf().new_from_file(album.cover.original)
self.window_resize(None)
self.frame.set_shadow_type(Gtk.ShadowType.NONE)
else:
print ('no pixbuf')
self.pixbuf = None
self.frame.set_shadow_type(Gtk.ShadowType.ETCHED_OUT)
if self.stack.get_visible_child_name() == "image1":
self.image1.queue_draw()
else:
self.image2.queue_draw()
def display_whats_playing(self, show_playing):
view = self.get_child_at(0, 0)
view.display_playing_tracks(show_playing)
def window_resize(self, widget):
alloc = self.get_allocation()
if alloc.height < 10:
return
entry_grid_alloc = self.entry_view_grid.get_allocation()
if (alloc.width / 3) <= (MIN_IMAGE_SIZE + 30) or \
(entry_grid_alloc.height) <= (MIN_IMAGE_SIZE + 30):
self.frame.props.visible = False
return
else:
self.frame.props.visible = True
vbar = self.scroll.get_vscrollbar()
vbar.set_visible(False)
hbar = self.scroll.get_hscrollbar()
hbar.set_visible(False)
minval = min((alloc.width / 3), alloc.height)
if self.oldval == minval:
return
self.oldval = minval
if minval < MIN_IMAGE_SIZE:
minval = MIN_IMAGE_SIZE
if self.pixbuf:
p = self.pixbuf.scale_simple(minval, minval, GdkPixbuf.InterpType.BILINEAR)
else:
p = None
if self.stack.get_visible_child_name() == "image1":
self.image2.set_from_pixbuf(p)
self.stack.set_visible_child_name("image2")
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")
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):
def __init__(self, shell, source):
'''
@@ -37,145 +420,205 @@ class CoverArtEntryView(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.append_column(RB.EntryViewColumn.TRACK_NUMBER, False) #'track-number'
self.append_column(RB.EntryViewColumn.TITLE, True) #'title' - n.b. default and never manually defined
self.append_column(RB.EntryViewColumn.GENRE, False) #'genre'
self.append_column(RB.EntryViewColumn.ARTIST, False) #'artist'
self.append_column(RB.EntryViewColumn.ALBUM, False) #'album'
self.append_column(RB.EntryViewColumn.DURATION, False) #'duration'
self.append_column(RB.EntryViewColumn.COMMENT, False) #'comment'
self.append_column(RB.EntryViewColumn.RATING, False) #'rating'
self.append_column(RB.EntryViewColumn.QUALITY, False) #'bitrate'
self.append_column(RB.EntryViewColumn.PLAY_COUNT, False) #'play-count'
self.append_column(RB.EntryViewColumn.LAST_PLAYED, False) #'last-played'
self.append_column(RB.EntryViewColumn.YEAR, False) #'date'
self.append_column(RB.EntryViewColumn.FIRST_SEEN, False) #'first-seen':
self.append_column(RB.EntryViewColumn.LOCATION, False) #'location'
self.append_column(RB.EntryViewColumn.BPM, False) #'beats-per-minute'
self.display_columns()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
self.set_columns_clickable(False)
# UI elements need to be imported.
ui = Gtk.Builder()
ui.set_translation_domain(cl.Locale.LOCALE_DOMAIN)
ui.add_from_file(rb.find_plugin_file(self.plugin,
'ui/coverart_entryview.ui'))
ui.connect_signals(self)
self.define_menu()
self.popup_menu = ui.get_object('entryview_popup_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.connect('selection-changed', self.selection_changed_callback)
self.actiongroup = ActionGroup(self.shell, 'coverentryplaylist_submenu')
self.playlist_sub_menu_item = ui.get_object('playlist_sub_menu_item')
self.actiongroup = Gtk.ActionGroup('coverentryplaylist_submenu')
uim = self.shell.props.ui_manager
uim.insert_action_group(self.actiongroup)
# 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(rhythm_settings, 'visible-columns')
self.external_plugins = None
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)
self.connect_library_signals()
self.echonest_similar_playlist = None
self.echonest_similar_genre_playlist = None
self.lastfm_similar_playlist = None
self.connect('selection-changed', self.selection_changed)
self.artists = ""
print ("end constructor")
def __del__(self):
uim = self.shell.props.ui_manager
uim.remove_action_group(self.action_group)
uim.remove_ui(self.ui_id)
uim.ensure_update()
del self.action_group
del self.play_action
del self.queue_action
def on_visible_columns_changed(self, settings, key):
print "CoverArtBrowser DEBUG - on_visible_columns_changed()"
#reset current columns
self.props.visible_columns = settings[key]
print "CoverArtBrowser DEBUG - end on_visible_columns_changed()"
def connect_library_signals(self):
# 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(library_view,
library_view.props.sort_order)
# connect to the sort-order property
self.connect('notify::sort-order', self._notify_sort_order,
library_view)
self.set_columns_clickable(False)
def display_playing_tracks(self, show_playing):
pass
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:
self.source.entryviewpane.entry_view_results.emit('update-cover', self.source, entries[0])
def add_album(self, album):
print "CoverArtBrowser DEBUG - add_album()"
album.get_entries(self.qm)
print("CoverArtBrowser DEBUG - add_album()")
tracks = album.get_tracks()
for track in tracks:
self.qm.add_entry(track.entry, -1)
(_, playing) = self.shell.props.shell_player.get_playing()
self.playing_changed(self.shell.props.shell_player, playing)
print "CoverArtBrowser DEBUG - add_album()"
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):
print "CoverArtBrowser DEBUG - clear()"
#self.set_model(RB.RhythmDBQueryModel.new_empty(self.shell.props.db))
print("CoverArtBrowser DEBUG - clear()")
for row in self.qm:
self.qm.remove_entry(row[0])
print "CoverArtBrowser DEBUG - clear()"
self.artists = ""
print("CoverArtBrowser DEBUG - clear()")
def do_entry_activated(self, entry):
print "CoverArtBrowser DEBUG - do_entry_activated()"
print("CoverArtBrowser DEBUG - do_entry_activated()")
self.select_entry(entry)
self.play_track_menu_item_callback(entry)
print "CoverArtBrowser DEBUG - do_entry_activated()"
print("CoverArtBrowser DEBUG - do_entry_activated()")
return True
def pre_popup_menu_callback(self, *args):
pass
def do_show_popup(self, over_entry):
print "CoverArtBrowser DEBUG - do_show_popup()"
self.popup_menu.popup(None, None, None, None, 0,
Gtk.get_current_event_time())
if over_entry:
print("CoverArtBrowser DEBUG - do_show_popup()")
print "CoverArtBrowser DEBUG - do_show_popup()"
return True
self.popup.popup(self.source,
'entryview_popup_menu', 0, Gtk.get_current_event_time())
def play_track_menu_item_callback(self, entry):
print "CoverArtBrowser DEBUG - play_track_menu_item_callback()"
return over_entry
# clear the queue
play_queue = self.shell.props.queue_source
for row in play_queue.props.query_model:
play_queue.remove_entry(row[0])
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)
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)
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)
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()")
for row in self.source_query_model:
self.source_query_model.remove_entry(row[0])
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
#library_view = self.shell.props.library_source.get_entry_view()
#library_view.set_sorting_order('track-number', Gtk.SortType.ASCENDING)
#self.set_sorting_order('track-number', Gtk.SortType.ASCENDING)
self.add_tracks_to_source(self.shell.props.queue_source)
# Start the music
player = self.shell.props.shell_player
player.stop()
player.set_playing_source(self.shell.props.queue_source)
player.play_entry(entry, self.source)
player.playpause(True)
print "CoverArtBrowser DEBUG - play_track_menu_item_callback()"
print("CoverArtBrowser DEBUG - play_track_menu_item_callback()")
def queue_track_menu_item_callback(self, entry):
print "CoverArtBrowser DEBUG - queue_track_menu_item_callback()"
def queue_track_menu_item_callback(self, *args):
print("CoverArtBrowser DEBUG - queue_track_menu_item_callback()")
self.add_tracks_to_source(self.shell.props.queue_source)
def add_to_playing_menu_item_callback(self, *args):
print("CoverArtBrowser DEBUG - add_to_playing_menu_item_callback()")
self.add_tracks_to_source(None)
def add_tracks_to_source(self, source):
if source == None:
source = self.source_query_model
selected = self.get_selected_entries()
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)
print "CoverArtBrowser DEBUG - queue_track_menu_item_callback()"
print("CoverArtBrowser DEBUG - queue_track_menu_item_callback()")
def love_track(self, rating):
'''
@@ -185,31 +628,30 @@ class CoverArtEntryView(RB.EntryView):
for entry in selected:
self.shell.props.db.entry_set(entry, RB.RhythmDBPropType.RATING,
rating)
rating)
self.shell.props.db.commit()
def show_properties_menu_item_callback(self, entry):
print "CoverArtBrowser DEBUG - show_properties_menu_item_callback()"
def show_properties_menu_item_callback(self, *args):
print("CoverArtBrowser DEBUG - show_properties_menu_item_callback()")
info_dialog = RB.SongInfo(source=self.source, entry_view=self)
info_dialog.show_all()
print "CoverArtBrowser DEBUG - show_properties_menu_item_callback()"
print("CoverArtBrowser DEBUG - show_properties_menu_item_callback()")
def playing_song_changed(self, shell_player, entry):
print "CoverArtBrowser DEBUG - playing_song_changed()"
print("CoverArtBrowser DEBUG - playing_song_changed()")
if entry is not None and self.get_entry_contained(entry):
self.set_state(RB.EntryViewState.PLAYING)
else:
self.set_state(RB.EntryViewState.NOT_PLAYING)
print "CoverArtBrowser DEBUG - playing_song_changed()"
print("CoverArtBrowser DEBUG - playing_song_changed()")
def playing_changed(self, shell_player, playing):
print "CoverArtBrowser DEBUG - playing_changed()"
print("CoverArtBrowser DEBUG - playing_changed()")
entry = shell_player.get_playing_entry()
if entry is not None and self.get_entry_contained(entry):
@@ -220,33 +662,210 @@ class CoverArtEntryView(RB.EntryView):
else:
self.set_state(RB.EntryViewState.NOT_PLAYING)
print "CoverArtBrowser DEBUG - playing_changed()"
print("CoverArtBrowser DEBUG - playing_changed()")
def add_playlist_menu_item_callback( self, menu_item ):
print "CoverArtBrowser DEBUG - add_playlist_menu_item_callback"
def add_playlist_menu_item_callback(self, *args):
print("CoverArtBrowser DEBUG - add_playlist_menu_item_callback")
playlist_manager = self.shell.props.playlist_manager
playlist = playlist_manager.new_playlist( '', False )
playlist = playlist_manager.new_playlist(_('New Playlist'), False)
self.add_tracks_to_source(playlist)
def playlist_menu_item_callback( self, menu_item ):
print "CoverArtBrowser DEBUG - playlist_menu_item_callback"
def playlist_menu_item_callback(self, *args):
pass
self.source.playlist_fillmenu( self.playlist_sub_menu_item,
self.actiongroup,
self.add_to_static_playlist_menu_item_callback)
def add_to_static_playlist_menu_item_callback(self, action, param, args):
print("CoverArtBrowser DEBUG - " + \
"add_to_static_playlist_menu_item_callback")
def add_to_static_playlist_menu_item_callback(self, action, playlist, favourite):
print "CoverArtBrowser DEBUG - add_to_static_playlist_menu_item_callback"
playlist = args['playlist']
self.add_tracks_to_source(playlist)
def selection_changed_callback(self,entry_view):
print "CoverArtBrowser DEBUG - selection_changed_callback"
def _on_library_sorting_changed(self, view, _):
self._old_sort_order = self.props.sort_order
if len(entry_view.get_selected_entries()) == 1:
entry = entry_view.get_selected_entries()[0]
self.source.stars.set_rating(entry.get_double(RB.RhythmDBPropType.RATING))
self.set_sorting_type(view.props.sort_order)
def _notify_sort_order(self, view, _, library_view):
if self.props.sort_order != self._old_sort_order:
self.resort_model()
# 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)
])
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.source.stars.set_rating(0)
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}
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)
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()
if not state:
sensitive = False
self.popup.set_sensitive('ev_compact_add_to_playing_menu_item', sensitive)
if not self.external_plugins:
self.external_plugins = \
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)
])
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(rhythm_settings, 'visible-columns')
def on_visible_columns_changed(self, settings, key):
print("CoverArtBrowser DEBUG - on_visible_columns_changed()")
# reset current columns
print("CoverArtBrowser DEBUG - end on_visible_columns_changed()")
for entry in self.col_map:
col = self.get_column(self.col_map[entry])
if entry in settings[key]:
col.set_visible(True)
else:
if entry != 'title':
col.set_visible(False)
print("CoverArtBrowser DEBUG - end on_visible_columns_changed()")
def define_menu(self):
popup = Menu(self.plugin, self.shell)
popup.load_from_file('N/A',
'ui/coverart_entryview_full_pop_rb3.ui')
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_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}
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)
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()
if not state:
sensitive = False
self.popup.set_sensitive('ev_full_add_to_playing_menu_item', sensitive)
if not self.external_plugins:
self.external_plugins = \
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)
+329
Ver Arquivo
@@ -0,0 +1,329 @@
# -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
#
# Copyright (C) 2012 - fossfreedom
# Copyright (C) 2012 - Agustin Carrasco
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of thie GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
import shutil
import os
import sys
import subprocess
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import RB
from gi.repository import Gdk
from gi.repository import Peas
from gi.repository import Gst
from coverart_utils import NaturalString
import rb
import coverart_rb3compat as rb3compat
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.connect_signals(self)
embeddialog = ui.get_object('exportembeddialog')
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')
bitrate_spinbutton = ui.get_object('bitrate_spinbutton')
resize_checkbutton = ui.get_object('resize_checkbutton')
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
#ok pressed - now fetch values from the dialog
final_folder_store = folderchooserbutton.get_current_folder()
use_album_name = use_album_name_checkbutton.get_active()
open_filemanager = open_filemanager_checkbutton.get_active()
convert = convert_checkbutton.get_active()
bitrate = bitrate_spinbutton.get_value()
toresize = resize_checkbutton.get_active()
if toresize:
resize = int(resize_spinbutton.get_value())
else:
resize = -1
embeddialog.destroy()
albums = {}
total = 0
for album in selected_albums:
albums[album] = album.get_tracks()
total = total + len(albums[album])
self._track_count = 1
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':
import winreg
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')
except WindowsError:
pass
else:
if regtype in (winreg.REG_SZ, winreg.REG_EXPAND_SZ):
shell = value
break
else:
shell = 'Explorer.exe'
subprocess.Popen([shell, final_folder_store])
elif sys.platform == 'darwin':
subprocess.Popen(['open', final_folder_store])
else:
subprocess.Popen(['xdg-open', final_folder_store])
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]):
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
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)
print(finalPath)
else:
shutil.copy(finalPath, folder_store)
except IOError as err:
print(err.args[0])
return False
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 !
# xingmux ! id3v2mux ! filesink location="mytrack.mp3"
converter = Gst.Pipeline.new('converter')
source = Gst.ElementFactory.make('filesrc', None)
decoder = Gst.ElementFactory.make('decodebin', 'decoder')
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('bitrate', self.TARGET_BITRATE)
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)
converter.add(decoder)
converter.add(convert)
converter.add(sample)
converter.add(encoder)
converter.add(xing)
converter.add(mux)
converter.add(sink)
Gst.Element.link(source, decoder)
#note - a decodebin cannot be linked at compile since
#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
def _calc_mp3_filename(self, filename, save_folder):
finalname = os.path.basename(filename)
finalname = finalname.rsplit('.')[0] + ".mp3"
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)
if bitrate < 32:
bitrate = self.TARGET_BITRATE
self.encoder.set_property('bitrate', int(bitrate))
print(bitrate)
# Start playing
ret = self.converter.set_state(Gst.State.PLAYING)
if ret == Gst.StateChangeReturn.FAILURE:
print("Unable to set the pipeline to the playing state.", sys.stderr)
exit(-1)
# Wait until error or EOS
bus = self.converter.get_bus()
try:
msg = bus.timed_pop_filtered(
Gst.CLOCK_TIME_NONE, Gst.MessageType.ERROR | Gst.MessageType.EOS)
except:
# 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:
err, debug = msg.parse_error()
print("Error received from element %s: %s" % (
msg.src.get_name(), err), sys.stderr)
print("Debugging information: %s" % debug, sys.stderr)
elif msg.type == Gst.MessageType.EOS:
print("End-Of-Stream reached.")
else:
print("Unexpected message received.", sys.stderr)
# Free resources
self.converter.set_state(Gst.State.NULL)
+244
Ver Arquivo
@@ -0,0 +1,244 @@
# -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
#
# Copyright (C) 2012 - fossfreedom
# Copyright (C) 2012 - Agustin Carrasco
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of thie GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
from gi.repository import Peas
from gi.repository import GObject
import lxml.etree as ET
import rb
from coverart_rb3compat import ActionGroup
from coverart_rb3compat import ApplicationShell
from coverart_utils import CaseInsensitiveDict
class ExternalPlugin(GObject.Object):
'''
class for all supported ExternalPlugins
'''
def __init__(self, **kargs):
super(ExternalPlugin, self).__init__(**kargs)
# dict of attributes associated with the external plugin
self.attributes = {}
self.attributes['is_album_menu'] = False
self.attributes['new_menu_name'] = ''
self.attributes['action_type'] = ''
self.attributes['action_group_name'] = ''
def appendattribute(self, key, val):
'''
append another attribute to the dict
:param key: `str` name of attribute
:param val: `str` value of attribute
'''
if key == 'is_album_menu':
if val == 'yes':
self.attributes[key] = True
else:
self.attributes[key] = False
else:
self.attributes[key] = val
def is_activated(self):
'''
method to test whether the plugin is actually loaded. Returns a bool
'''
peas = Peas.Engine.get_default()
loaded_plugins = peas.get_loaded_plugins()
if self.attributes['plugin_name'] in CaseInsensitiveDict(loaded_plugins):
print("found %s" % self.attributes['plugin_name'])
return True
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):
'''
method to create the menu item appropriate to the plugin.
A plugin can have many menu items - all menuitems are enclosed
in a section.
:param menubar: `str` name for the GtkMenu - ignored for RB2.99
:param section_name: `str` unique name of the section holding the menu items
:param at_position: `int` position within the GtkMenu to create menu - ignored for RB2.99
:param save_actiongroup: `ActionGroup` container for all menu-item Actions
:param save_menu: `Menu` whole popupmenu including sub-menus
:param for_album: `bool` create the menu for the album - if not given
then its assumed the menu item is appropriate just for tracks
'''
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'])
if 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()
else:
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'])
new_menu_item = save_menu.insert_menu_item(menubar, section_name,
at_position, action)
return new_menu_item
def do_deactivate(self):
pass
def set_entry_view_selected_entries(self, shell):
'''
method called just before the external plugin action is activated
Normally only called for album menus to mimic selecting all the
EntryView rows
'''
page = shell.props.selected_page
if not hasattr(page, "get_entry_view"):
return
page.get_entry_view().select_all()
def activate(self, shell):
'''
method called to initiate the external plugin action
the action is defined by defining the action_group_name, action_name and action_type
'''
action = ApplicationShell(shell).lookup_action(self.attributes['action_group_name'],
self.attributes['action_name'],
self.attributes['action_type'])
if action:
action.activate()
def menuitem_callback(self, action, param, args):
'''
method called when a menu-item is clicked. Basically, an Action
is activated by the user
:param action: `Gio.SimpleAction` or `Gtk.Action`
:param param: Not used
:param args: dict associated with the action
'''
for_album = args['album']
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
:param section_name: `str` unique name of the section holding the menu items
: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.
self.supported_plugins = []
extplugins = rb.find_plugin_file(popup.plugin, 'ui/coverart_external_plugins.xml')
root = ET.parse(open(extplugins)).getroot()
base = 'rb3/plugin'
for elem in root.xpath(base):
pluginname = elem.attrib['name']
basemenu = base + "[@name='" + pluginname + "']/menu"
for menuelem in root.xpath(basemenu):
ext = ExternalPlugin()
ext.appendattribute('plugin_name', pluginname)
label = menuelem.attrib['label']
if label != "":
ext.appendattribute('new_menu_name', label)
baseattrib = basemenu + "[@label='" + label + "']/attribute"
else:
baseattrib = basemenu + "/attribute"
for attribelem in root.xpath(baseattrib):
key = attribelem.attrib['name']
val = attribelem.text
ext.appendattribute(key, val)
self.supported_plugins.append(ext)
def create_menu(self, menu_name, for_album=False):
'''
method to create the menu items for all supported plugins
:param menu_name: `str` unique name (GtkMenu) id for the menu to create
:for_album: `bool` - create a menu applicable for Albums
by default a menu is assumed to be applicable to a track in an
EntryView
'''
self.menu_name = menu_name
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)
if (not items_added) and new_menu_item:
items_added = True
if items_added:
self.menu.insert_separator(self.menu_name, self.at_position)
+80
Ver Arquivo
@@ -0,0 +1,80 @@
# -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
#
# Copyright (C) 2012 - fossfreedom
# Copyright (C) 2012 - Agustin Carrasco
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
from gi.repository import GObject
from gi.repository import GLib
from coverart_widgets import AbstractView
class ListShowingPolicy(GObject.Object):
'''
Policy that mostly takes care of how and when things should be showed on
the view that makes use of the `AlbumsModel`.
'''
def __init__(self, list_view):
super(ListShowingPolicy, self).__init__()
self.counter = 0
self._has_initialised = False
def initialise(self, album_manager):
if self._has_initialised:
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.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)
def get_selected_objects(self):
'''
finds what has been selected
returns an array of `Album`
'''
return []
+184
Ver Arquivo
@@ -0,0 +1,184 @@
# -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
#
# Copyright (C) 2012 - fossfreedom
# Copyright (C) 2012 - Agustin Carrasco
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
# define plugin
from gi.repository import Gtk
from gi.repository import RB
from gi.repository import GObject
from coverart_rb3compat import Menu
from coverart_external_plugins import CreateExternalPluginMenu
from coverart_entryview import CoverArtEntryView
from coverart_rb3compat import ActionGroup
from coverart_rb3compat import ApplicationShell
from coverart_browser_prefs import CoverLocale
import rb
class CoverArtPlayEntryView(CoverArtEntryView):
__hash__ = GObject.__hash__
def __init__(self, shell, source):
'''
Initializes the entryview.
'''
super(CoverArtPlayEntryView, self).__init__(shell, source)
def define_menu(self):
popup = Menu(self.plugin, self.shell)
popup.load_from_file('N/A',
'ui/coverart_play_pop_rb3.ui')
signals = {
'remove_from_playlist_menu_item': self.remove_from_playlist_menu_item_callback
}
popup.connect_signals(signals)
popup.connect('pre-popup', self.pre_popup_menu_callback)
self.popup = popup
def pre_popup_menu_callback(self, *args):
'''
Callback when the popup menu is about to be displayed
'''
if not self.external_plugins:
self.external_plugins = \
CreateExternalPluginMenu("playlist_entry_view", 1, self.popup)
self.external_plugins.create_menu('play_popup_menu')
def remove_from_playlist_menu_item_callback(self, *args):
print ("remove_from_playlist_menu_item_callback")
entries = self.get_selected_entries()
for entry in entries:
print (entry)
self.source.source_query_model.remove_entry(entry)
def do_show_popup(self, over_entry):
if over_entry:
print("CoverArtBrowser DEBUG - do_show_popup()")
self.popup.popup(self.source,
'play_popup_menu', 0, Gtk.get_current_event_time())
return over_entry
def play_track_menu_item_callback(self, *args):
print("CoverArtBrowser DEBUG - play_track_menu_item_callback()")
selected = self.get_selected_entries()
entry = selected[0]
# Start the music
player = self.shell.props.shell_player
player.play_entry(entry, self.source)
print("CoverArtBrowser DEBUG - play_track_menu_item_callback()")
class CoverArtPlaySource(RB.Source):
'''
Source utilized by the plugin to show all it's ui.
'''
def __init__(self, **kwargs):
'''
Initializes the source.
'''
super(CoverArtPlaySource, self).__init__()
self.external_plugins = None
self.hasActivated = False
def initialise(self, plugin, shell, source):
self.plugin = plugin
self.shell = shell
self.source = source
def do_selected(self):
'''
Called by Rhythmbox when the source is selected. It makes sure to
create the ui the first time the source is shown.
'''
print("CoverArtBrowser DEBUG - do_selected")
# first time of activation -> add graphical stuff
if not self.hasActivated:
self.do_impl_activate()
# indicate that the source was activated before
self.hasActivated = True
print("CoverArtBrowser DEBUG - end do_selected")
def connect_library_signals(self):
pass
def do_impl_activate(self):
'''
Called by do_selected the first time the source is activated.
It creates all the source ui and connects the necessary signals for it
correct behavior.
'''
print ('do_impl_activate')
self.hasActivated = True
self.entryview = CoverArtPlayEntryView(self.shell, self.source)
self.entryview.props.hexpand = True
self.entryview.props.vexpand = True
grid = Gtk.Grid()
grid.attach(self.entryview, 0, 1, 1, 1)
self.entryview.set_model(self.source.source_query_model)
# enable sorting on the entryview
#entryview.set_columns_clickable(True)
self.shell.props.library_source.get_entry_view().set_columns_clickable(
True)
cl = CoverLocale()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
location = rb.find_plugin_file(self.plugin, 'ui/playsource-toolbar.ui')
ui = Gtk.Builder()
ui.set_translation_domain(cl.Locale.RB)
ui.add_from_file(location)
toolbar_menu = ui.get_object('playsource-toolbar')
app = self.shell.props.application
app.link_shared_menus(toolbar_menu)
bar = RB.ButtonBar.new(toolbar_menu, toolbar_menu)
grid.attach(bar, 0, 0, 1, 1)
grid.show_all()
self.pack_start(grid, True, True, 0)
appshell = ApplicationShell(self.shell)
action_group = ActionGroup(self.shell, 'PlaySourceActions')
action_group.add_action(func=self.clear_playsource,
action_name='playsource-clear', action_state=ActionGroup.STANDARD,
action_type='app')
action_group.add_action(func=self.shuffle_playsource,
action_name='playsource-shuffle', action_state=ActionGroup.STANDARD,
action_type='app')
appshell.insert_action_group(action_group)
def clear_playsource(self, *args):
for row in self.entryview.props.model:
self.entryview.props.model.remove_entry(row[0])
def shuffle_playsource(self, *args):
self.entryview.props.model.shuffle_entries()
GObject.type_register(CoverArtPlayEntryView)
+377
Ver Arquivo
@@ -0,0 +1,377 @@
# -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
#
# Copyright (C) 2012 - fossfreedom
# Copyright (C) 2012 - Agustin Carrasco
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
import urllib.parse
import json
import os
import random
from gi.repository import RB
from gi.repository import Gtk
from coverart_utils import idle_iterator
import rb
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
def __init__(self, shell, source, playlist_name):
self.shell = shell
#lets fill up the queue with artists
self.candidate_artist = {}
self.shell.props.shell_player.connect('playing-song-changed', self.playing_song_changed)
self.source = source
self.search_entry = None
self.playlist_started = False
self.played_artist = {}
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.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):
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()
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")
return
self.search_entry = seed_entry
self.played_artist[search_artist] = True
self.playlist_started = True
self._running = False
self._start_process()
def _start_process(self):
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
@idle_iterator
def _load_albums(self):
def process(row, data):
entry = data['model'][row.path][0]
lookup = entry.get_string(RB.RhythmDBPropType.ARTIST_FOLDED)
lookup_title = entry.get_string(RB.RhythmDBPropType.TITLE_FOLDED)
if lookup in self.artist and \
lookup_title in \
self.artist[lookup]:
if lookup not in self.candidate_artist:
self.candidate_artist[lookup] = []
# 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])
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})
self.tracks_not_played = self.tracks_not_played + 1
def after(data):
# update the progress
pass
def error(exception):
print(('Error processing entries: ' + str(exception)))
def finish(data):
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"))
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])
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)
entry, elements = list(row.items())[0]
element_pos, add_to_source, artist = elements
self.source.add_entry(entry, -1)
self.candidate_artist[artist][element_pos]['add-to-source'] = True
count = count + 1
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()
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"))
title = urllib.parse.quote(title.encode("utf8"))
formatted_url = url.format(urllib.parse.quote(apikey),
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")
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")
self.display_error_message()
self._clear_next()
return
for song in similar['similartracks']['track']:
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")
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.)
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
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")
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")
self.display_error_message()
self._clear_next()
return
for song in similar['response']['songs']:
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")
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.)
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
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")
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")
self.display_error_message()
self._clear_next()
return
for song in similar['response']['songs']:
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")
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.)
+80
Ver Arquivo
@@ -0,0 +1,80 @@
# -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
#
# Copyright (C) 2012 - fossfreedom
# Copyright (C) 2012 - Agustin Carrasco
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
from gi.repository import GObject
from gi.repository import GLib
from coverart_widgets import AbstractView
class PlaySourceShowingPolicy(GObject.Object):
'''
Policy that mostly takes care of how and when things should be showed on
the view that makes use of the `AlbumsModel`.
'''
def __init__(self, list_view):
super(PlaySourceShowingPolicy, self).__init__()
self.counter = 0
self._has_initialised = False
def initialise(self, album_manager):
if self._has_initialised:
return
self._has_initialised = True
class PlaySourceView(AbstractView):
__gtype_name__ = "PlaySourceView"
name = 'playsourceview'
use_plugin_window = False
def __init__(self):
super(PlaySourceView, self).__init__()
self.view = self
self._has_initialised = False
self.show_policy = PlaySourceShowingPolicy(self)
def initialise(self, source):
if self._has_initialised:
return
self._has_initialised = True
self.view_name = "playsource_view"
super(PlaySourceView, 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,
source.playlist_source)
def get_selected_objects(self):
'''
finds what has been selected
returns an array of `Album`
'''
return []
+80
Ver Arquivo
@@ -0,0 +1,80 @@
# -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
#
# Copyright (C) 2012 - fossfreedom
# Copyright (C) 2012 - Agustin Carrasco
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
from gi.repository import GObject
from gi.repository import GLib
from coverart_widgets import AbstractView
class QueueShowingPolicy(GObject.Object):
'''
Policy that mostly takes care of how and when things should be showed on
the view that makes use of the `AlbumsModel`.
'''
def __init__(self, list_view):
super(QueueShowingPolicy, self).__init__()
self.counter = 0
self._has_initialised = False
def initialise(self, album_manager):
if self._has_initialised:
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.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)
def get_selected_objects(self):
'''
finds what has been selected
returns an array of `Album`
'''
return []
+880
Ver Arquivo
@@ -0,0 +1,880 @@
# -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
#
# IMPORTANT - WHILST THIS MODULE IS USED BY SEVERAL OTHER PLUGINS
# THE MASTER AND MOST UP-TO-DATE IS FOUND IN THE COVERART BROWSER
# PLUGIN - https://github.com/fossfreedom/coverart-browser
# PLEASE SUBMIT CHANGES BACK TO HELP EXPAND THIS API
#
# Copyright (C) 2012 - fossfreedom
# Copyright (C) 2012 - Agustin Carrasco
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
import sys
import xml.etree.ElementTree as ET
from gi.repository import Gtk
from gi.repository import Gio
from gi.repository import GLib
from gi.repository import GObject
from gi.repository import RB
import rb
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])
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]):
return True
if int(split_compare[1]) < int(split[1]):
return True
return False
PYVER = sys.version_info[0]
if PYVER >= 3:
import urllib.request, urllib.parse, urllib.error
else:
import urllib
from urlparse import urlparse as rb2urlparse
if PYVER >= 3:
import http.client
else:
import httplib
def responses():
if PYVER >= 3:
return http.client.responses
else:
return httplib.responses
def unicodestr(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)
else:
return unicode(param).encode(charset)
def unicodedecode(param, charset):
if PYVER >= 3:
return param
else:
return param.decode(charset)
def urlparse(uri):
if PYVER >= 3:
return urllib.parse.urlparse(uri)
else:
return rb2urlparse(uri)
def url2pathname(url):
if PYVER >= 3:
return urllib.request.url2pathname(url)
else:
return urllib.url2pathname(url)
def urlopen(filename):
if PYVER >= 3:
return urllib.request.urlopen(filename)
else:
return urllib.urlopen(filename)
def pathname2url(filename):
if PYVER >= 3:
return urllib.request.pathname2url(filename)
else:
return urllib.pathname2url(filename)
def unquote(uri):
if PYVER >= 3:
return urllib.parse.unquote(uri)
else:
return urllib.unquote(uri)
def quote(uri, safe=None):
if PYVER >= 3:
if safe:
return urllib.parse.quote(uri, safe=safe)
else:
return urllib.parse.quote(uri)
else:
if safe:
return urllib.quote(uri, safe=safe)
else:
return urllib.quote(uri)
def quote_plus(uri):
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
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.
'''
super(Menu, self).__init__()
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
:param menubar: `str` is the name GtkMenu (or ignored for RB2.99+)
:param section_name: `str` is the name of the section to add the item to (RB2.99+)
:param action: `Action` to associate with the menu item
'''
return self.insert_menu_item(menubar, section_name, -1, action)
def insert_menu_item(self, menubar, section_name, position, action):
'''
add a new menu item to the popup
:param menubar: `str` is the name GtkMenu (or ignored for RB2.99+)
:param section_name: `str` is the name of the section to add the item to (RB2.99+)
:param position: `int` position to add to GtkMenu (ignored for RB2.99+)
: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()
action.associate_menuitem(item)
item.set_label(label)
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:
bar.insert(item, position)
bar.show_all()
uim = self.shell.props.ui_manager
uim.ensure_update()
return item
def insert_separator(self, menubar, at_position):
'''
add a separator to the popup (only required for RB2.98 and earlier)
:param menubar: `str` is the name GtkMenu (or ignored for RB2.99+)
:param position: `int` position to add to GtkMenu (ignored for RB2.99+)
'''
if not is_rb3(self.shell):
menu_item = Gtk.SeparatorMenuItem().new()
menu_item.set_visible(True)
self._rbmenu_items['separator' + str(self._unique_num)] = menu_item
self._unique_num = self._unique_num + 1
bar = self.get_menu_object(menubar)
bar.insert(menu_item, at_position)
bar.show_all()
uim = self.shell.props.ui_manager
uim.ensure_update()
def remove_menu_items(self, menubar, section_name):
'''
utility function to remove all menuitems associated with the menu section
:param menubar: `str` is the name of the GtkMenu containing the menu items (ignored for RB2.99+)
:param section_name: `str` is the name of the section containing the menu items (for RB2.99+ only)
'''
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:
return
uim = self.shell.props.ui_manager
bar = self.get_menu_object(menubar)
for menu_item in self._rbmenu_items:
bar.remove(self._rbmenu_items[menu_item])
bar.show_all()
uim.ensure_update()
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
:param rb3_ui_filename: `str` RB2.99 and higher UI file
'''
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))
def _connect_rb3_signals(self, signals):
def _menu_connect(action_name, func):
action = Gio.SimpleAction(name=action_name)
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)
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)
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
:param popup_name: `str` is the name menu-id in the UI file
'''
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)
popup_menu = Gtk.Menu.new_from_model(item)
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
:param menu_name_or_link: `str` to search for in the UI file
'''
if menu_name_or_link in self._rbmenu_objects:
return self._rbmenu_objects[menu_name_or_link]
item = self.builder.get_object(menu_name_or_link)
if is_rb3(self.shell):
if item:
popup_menu = item
else:
app = self.shell.props.application
popup_menu = app.get_plugin_menu(menu_name_or_link)
else:
popup_menu = item
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):
'''
utility function to enable/disable a menu-item
:param menu_or_action_item: `GtkMenuItem` or `Gio.SimpleAction`
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
'''
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
def __init__(self, shell, group_name):
'''
constructor
:param shell: `RBShell`
:param group_name: `str` unique name for the object to create
'''
self.group_name = group_name
self.shell = shell
self._actions = {}
if is_rb3(self.shell):
self.actiongroup = Gio.SimpleActionGroup()
else:
self.actiongroup = Gtk.ActionGroup(group_name)
uim = self.shell.props.ui_manager
uim.insert_action_group(self.actiongroup)
@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
:param action_name: `str` is the Action unique name
'''
return self._actions[action_name]
def add_action_with_accel(self, func, action_name, accel, **args):
'''
Creates an Action with an accelerator and adds it to the ActionGroup
:param func: function callback used when user activates the action
:param action_name: `str` unique name to associate with an action
:param accel: `str` accelerator
:param args: dict of arguments - this is passed to the function callback
Notes:
see notes for add_action
'''
args['accel'] = accel
return self.add_action(func, action_name, **args)
def add_action(self, func, action_name, **args):
'''
Creates an Action and adds it to the ActionGroup
:param func: function callback used when user activates the action
:param action_name: `str` unique name to associate with an action
:param args: dict of arguments - this is passed to the function callback
Notes:
key value of "label" is the visual menu label to display
key value of "action_type" is the RB2.99 Gio.Action type ("win" or "app")
by default it assumes all actions are "win" type
key value of "action_state" determines what action state to create
'''
if 'label' in args:
label = args['label']
else:
label = action_name
if 'accel' in args:
accel = args['accel']
else:
accel = None
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))
else:
action = Gio.SimpleAction.new(action_name, None)
action_type = 'win'
if 'action_type' in args:
if args['action_type'] == 'app':
action_type = 'app'
app = Gio.Application.get_default()
if action_type == 'app':
app.add_action(action)
else:
self.shell.props.window.add_action(action)
self.actiongroup.add_action(action)
if accel:
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)
else:
action = Gtk.Action(label=label,
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
: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
an Action or None if no matching Action.
:param action_group_name: `str` is the Gtk.ActionGroup name (ignored for RB2.99+)
: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)
else:
action = self.shell.props.window.lookup_action(action_name)
else:
uim = self.shell.props.ui_manager
ui_actiongroups = uim.get_action_groups()
actiongroup = None
for actiongroup in ui_actiongroups:
if actiongroup.get_name() == action_group_name:
break
action = None
if actiongroup:
action = actiongroup.get_action(action_name)
if action:
return Action(self.shell, action)
else:
return None
def add_app_menuitems(self, ui_string, group_name, menu='tools'):
'''
utility function to add application menu items.
For RB2.99 all application menu items are added to the "tools" section of the
application menu. All Actions are assumed to be of action_type "app".
For RB2.98 or less, it is added however the UI_MANAGER string
is defined.
:param ui_string: `str` is the Gtk UI definition. There is not an
equivalent UI definition in RB2.99 but we can parse out menu items since
this string is in XML format
:param group_name: `str` unique name of the ActionGroup to add menu items to
:param menu: `str` RB2.99 menu section to add to - nominally either
'tools' or 'view'
'''
if is_rb3(self.shell):
root = ET.fromstring(ui_string)
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)
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
For RB2.99 all menu items are are assumed to be of action_type "win".
For RB2.98 or less, it is added however the UI_MANAGER string
is defined.
:param ui_string: `str` is the Gtk UI definition. There is not an
equivalent UI definition in RB2.99 but we can parse out menu items since
this string is in XML format
:param group_name: `str` unique name of the ActionGroup to add menu items to
'''
if is_rb3(self.shell):
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':
plugin_type = 'browser-popup'
elif popup_name == 'PlaylistViewPopup':
plugin_type = 'playlist-popup'
elif popup_name == 'PodcastViewPopup':
plugin_type = 'podcast-episode-popup'
else:
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
else:
uim = self.shell.props.ui_manager
self._uids.append(uim.add_ui_from_string(ui_string))
uim.ensure_update()
def cleanup(self):
'''
utility remove any menuitems created.
'''
if is_rb3(self.shell):
for uid in self._uids:
Gio.Application.get_default().remove_plugin_menu_item(self._uids[uid],
uid)
else:
uim = self.shell.props.ui_manager
for uid in self._uids:
uim.remove_ui(uid)
uim.ensure_update();
def __init__(self, shell):
""" Create singleton instance """
# Check whether we already have an instance
if ApplicationShell.__instance is None:
# Create and remember instance
ApplicationShell.__instance = ApplicationShell.__impl(shell)
# Store instance reference as the only member in the handle
self.__dict__['_ApplicationShell__instance'] = ApplicationShell.__instance
def __getattr__(self, attr):
""" Delegate access to implementation """
return getattr(self.__instance, attr)
def __setattr__(self, attr, value):
""" 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.
:param shell: `RBShell`
:param action: `Gio.Action` or `Gtk.Action`
'''
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:
self.action.connect(address, func, None, args)
def _activate(self, action, *args):
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):
'''
get the menu label associated with the Action
for RB2.99+ actions dont have menu labels so this is managed
manually
'''
if not is_rb3(self.shell):
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:
self._accel = new_accelerator
else:
self._accel = ''
def get_sensitive(self):
'''
get the sensitivity (enabled/disabled) state of the Action
returns boolean
'''
if is_rb3(self.shell):
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
to RB2.99+
'''
if is_rb3(self.shell) and self.action.props.state_type:
self.action.change_state(GLib.Variant('b', value))
def activate(self):
'''
invokes the activate signal for the action
'''
if is_rb3(self.shell):
self.action.activate(None)
else:
self.action.activate()
def set_active(self, value):
'''
activate or deactivate a stateful action signal
For consistency with earlier RB versions, this will fire the
activate signal for the action
:param value: `boolean` state value
'''
if is_rb3(self.shell):
self.action.change_state(GLib.Variant('b', value))
self._current_state = value
self._do_update_state = False
self.activate()
self._do_update_state = True
else:
self.action.set_active(value)
def get_active(self):
'''
get the state of the action
returns `boolean` state value
'''
if is_rb3(self.shell):
returnval = self._current_state
else:
returnval = self.action.get_active()
return returnval
def associate_menuitem(self, menuitem):
'''
links a menu with the action
'''
if is_rb3(self.shell):
menuitem.set_detailed_action('win.' + self.action.get_name())
else:
menuitem.set_related_action(self.action)
+89 -51
Ver Arquivo
@@ -18,61 +18,65 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
import rb
import gettext
from gi.repository import Gtk
from gi.repository import RB
from gi.repository import WebKit
from mako.template import Template
from coverart_album import AlbumLoader
import rb
import coverart_rb3compat as rb3compat
from coverart_album import Album
from coverart_browser_prefs import webkit_support
class CoverSearchPane(Gtk.VBox):
class CoverSearchPane(Gtk.Box):
'''
This UI represents a pane where different album's covers can be presented
given an album to look for. It also allows to make custom image searchs,
This UI represents a pane where different covers can be presented
given an album or artist to look for. It also allows to make custom image searchs,
customize the default search and select covers from the pane and use them
as the album covers (either with a double click or draging 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.
'''
super(CoverSearchPane, self).__init__()
self.set_orientation(Gtk.Orientation.VERTICAL)
self.selection_color = selection_color
self.file = ""
self.basepath = 'file://' + plugin.plugin_info.get_data_dir()
self.load_templates(plugin)
self.init_gui()
if webkit_support():
self.init_gui()
# init the pane with the empty template
self.clear()
# init the pane with the empty template
self.clear()
def load_templates(self, plugin):
'''
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/',
output_encoding='utf-8',
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/',
output_encoding='utf-8',
encoding_errors='replace')
default_filters=['decode.utf8'],
module_directory='/tmp/',
encoding_errors='replace')
path = rb.find_plugin_file(plugin,
'tmpl/artistartsearch-tmpl.html')
self.artist_template = Template(filename=path,
default_filters=['decode.utf8'],
module_directory='/tmp/',
encoding_errors='replace')
self.styles = rb.find_plugin_file(plugin, 'tmpl/main.css')
def init_gui(self):
@@ -80,6 +84,8 @@ class CoverSearchPane(Gtk.VBox):
Initializes the pane ui.
'''
#---- 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)
@@ -92,59 +98,91 @@ class CoverSearchPane(Gtk.VBox):
self.show_all()
# connect the title changed signal
self.webview.connect('title-changed', self.set_cover)
self.webview.connect('notify::title', self.set_cover)
def do_search(self, album):
def do_search(self, coverobject, callback):
'''
When this method is called, the webview gets refreshed with the info
of the album passed.
of the album or artist passed.
'''
if album is self.current_album:
print("coverart-search do_search")
if coverobject is self.current_searchobject:
return
self.current_album = album
self.current_searchobject = coverobject
self.callback = callback
artist = album.album_artist
album_name = album.name
if isinstance(coverobject, Album):
artist = coverobject.artist
album_name = coverobject.name
if album_name.upper() == "UNKNOWN":
album_name = ""
if album_name.upper() == "UNKNOWN":
album_name = ""
if artist.upper() == "UNKNOWN":
artist = ""
if artist.upper() == "UNKNOWN":
artist = ""
if not (album_name == "" and artist == ""):
artist = rb3compat.unicodestr(artist.replace('&', '&amp;'),
'utf-8')
album_name = rb3compat.unicodestr(album_name.replace('&', '&amp;'), 'utf-8')
self.render_album_art_search(artist, album_name)
else:
artist_name = coverobject.name
if artist_name.upper() == "UNKNOWN":
artist_name = ""
if not (artist_name == ""):
artist = rb3compat.unicodestr(artist_name.replace('&', '&amp;'),
'utf-8')
self.render_artist_art_search(artist)
if not(album_name == "" and artist == ""):
artist = unicode(artist.replace('&', '&amp;'),
'utf-8')
album_name = unicode(album_name.replace('&', '&amp;'), 'utf-8')
self.render_album_art_search(artist, album_name)
def render_album_art_search(self, artist, album_name):
'''
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")
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)
print("here")
self.webview.load_string(temp_file, 'text/html', 'utf-8',
self.basepath)
def clear(self):
'''
Clears the webview of any album's specific info/covers.
Clears the webview of any specific info/covers.
'''
self.current_album = None
self.current_searchobject = None
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, frame, title):
def set_cover(self, webview, arg):
'''
Callback called when a image in the pane is double-clicked. It takes
care of asking the AlbumLoader to update the album's cover.
care of updating the searched object cover.
Some titles have spurious characters beginning with % - remove these
'''
# get the loader
loader = AlbumLoader.get_instance()
# update the cover
title = webview.get_title()
loader.update_cover(self.current_album, uri=title)
print(title)
if title:
#self.album_manager.cover_man.update_cover(self.current_searchobject,
# uri=title)
self.callback(self.current_searchobject, uri=title)
-106
Ver Arquivo
@@ -1,106 +0,0 @@
# -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
##
# Copyright (C) 2012 - fossfreedom
# Copyright (C) 2012 - Agustin Carrasco
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
#ttimer: Thread callback timer, it execute your callback function periodically.
#
#adapted from
#
#Author : H.K.Ong
#Date : 27-03-2008
#Website : http://linux.byexamples.com
#Revision : 1
#
import sys,time
from threading import Thread
import threading
class ttimer(object):
"""Threading callback timer - threading timer will callback your function periodically.
interval - interval callback periodically, in sec
retry - execute how many times? -1 is infinity
cbfunc - callback function
cbparam - parameter in list
i.e t=ttimer(1,10,myfunc,["myparam"])"""
def __init__(self, interval, retry, cbfunc, cbparam=[]):
self.is_start=False
self.is_end=False
# doing my thread stuff now.
self.thread = threading.Thread(target = self._callback, args=(interval,retry,cbfunc,cbparam,) )
self.thread.setDaemon(True)
self.thread.start()
#thread.start_new_thread(self._callback,(interval,retry,cbfunc,cbparam,))
def Start(self):
#start the thread
self.mytime=time.time()
self.is_start=True
self.is_end=False
def Stop(self):
#stop the thread.
self.mytime=time.time()
self.is_start=False
self.is_end=True
def IsStop(self):
#Is the thread already end? return True if yes.
if self.is_end:
return True
else:
return False
def _callback(self,interval,retry,cbfunc,cbparam=[]):
""" This is the private thread loop, call start() to start the threading timer."""
print "callback"
self.retry=retry
retry=0
if self.is_end:
return None
while True:
if not self.is_end:
if self.retry==-1:
pass
elif retry>=self.retry:
break
if self.is_start:
#check time
tmptime=time.time()
if tmptime >=(self.mytime + interval):
print "before"
cbfunc(cbparam) # callback your function
print "after"
self.mytime=time.time()
if not self.retry== -1:
retry+=1
else:
pass
time.sleep(0.5)
self.is_end=True
print "end callback"
+241
Ver Arquivo
@@ -0,0 +1,241 @@
# -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
#
# Copyright (C) 2012 - fossfreedom
# Copyright (C) 2012 - Agustin Carrasco
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import RB
from gi.repository import Gio
from coverart_browser_prefs import GSetting
from coverart_browser_prefs import CoverLocale
from coverart_utils import Theme
from coverart_controllers import PlaylistPopupController
from coverart_controllers import GenrePopupController
from coverart_controllers import SortPopupController
from coverart_controllers import ArtistSortPopupController
from coverart_controllers import PropertiesMenuController
from coverart_controllers import DecadePopupController
from coverart_controllers import SortOrderToggleController
from coverart_controllers import ArtistSortOrderToggleController
from coverart_controllers import AlbumSearchEntryController
from coverart_widgets import SearchEntry
from coverart_browser_prefs import webkit_support
import rb
class Toolbar(GObject.Object):
def __init__(self, plugin, mainbox, controllers):
super(Toolbar, self).__init__()
self.plugin = plugin
self.mainbox = mainbox
cl = CoverLocale()
ui_file = rb.find_plugin_file(plugin, self.ui)
# create the toolbar
builder = Gtk.Builder()
builder.set_translation_domain(cl.Locale.LOCALE_DOMAIN)
builder.add_from_file(ui_file)
# assign the controllers to the buttons
for button, controller in controllers.items():
if button != 'search':
builder.get_object(button).controller = controller
if not webkit_support():
#button = builder.get_object('flowview_button')
#button.set_visible(False)
separator = builder.get_object('properties_separator')
if separator:
separator.set_visible(False)
# workaround to translate the search entry tooltips
cl.switch_locale(cl.Locale.RB)
search_entry = SearchEntry(has_popup=True)
search_entry.show_all()
cl.switch_locale(cl.Locale.LOCALE_DOMAIN)
# add it to the ui
align = builder.get_object('entry_search_alignment')
align.add(search_entry)
# assign the controller
search_entry.controller = controllers['search']
Theme(self.plugin).connect('theme_changed', self._theme_changed,
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)
def _theme_changed(self, toolbar, controllers):
for controller in list(controllers.values()):
controller.update_images(True)
class TopToolbar(Toolbar):
ui = 'ui/coverart_topbar.ui'
name = 'main'
def hide(self):
if self.builder.get_visible():
self.builder.hide()
def show(self):
self.mainbox.pack_start(self.builder, False, True, 0)
self.mainbox.reorder_child(self.builder, 0)
self.builder.show()
class LeftToolbar(Toolbar):
ui = 'ui/coverart_leftsidebar.ui'
name = 'left'
def hide(self):
if self.builder.get_visible():
self.builder.hide()
self.plugin.shell.remove_widget(self.builder,
RB.ShellUILocation.SIDEBAR)
def show(self):
self.plugin.shell.add_widget(self.builder,
RB.ShellUILocation.SIDEBAR, expand=False, fill=False)
self.builder.show()
class RightToolbar(Toolbar):
ui = 'ui/coverart_rightsidebar.ui'
name = 'right'
def hide(self):
if self.builder.get_visible():
self.builder.hide()
self.plugin.shell.remove_widget(self.builder,
RB.ShellUILocation.RIGHT_SIDEBAR)
def show(self):
self.plugin.shell.add_widget(self.builder,
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'
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
# create the buttons controllers
controllers = self._create_controllers(plugin, viewmgr)
# initialize toolbars
self._bars = {}
self._bars[TopToolbar.name] = TopToolbar(plugin, main_box,
controllers)
self._bars[LeftToolbar.name] = LeftToolbar(plugin, main_box,
controllers)
self._bars[RightToolbar.name] = RightToolbar(plugin, main_box,
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.
:param enabled: `bool` value.
:param toolbar_object: `ToolbarObject`
None if enabled is to apply to all objects in the toolbar
'''
if toolbar_object:
self._controllers[toolbar_object].enabled = enabled
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)
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)
def _create_controllers(self, plugin, viewmgr):
controllers = {}
album_model = viewmgr.source.album_manager.model
controllers[ToolbarObject.PROPERTIES] = \
PropertiesMenuController(plugin, viewmgr.source)
controllers[ToolbarObject.SORT_BY] = \
SortPopupController(plugin, viewmgr)
controllers[ToolbarObject.SORT_ORDER] = \
SortOrderToggleController(plugin, viewmgr)
controllers[ToolbarObject.SORT_BY_ARTIST] = \
ArtistSortPopupController(plugin, viewmgr)
controllers[ToolbarObject.SORT_ORDER_ARTIST] = \
ArtistSortOrderToggleController(plugin, viewmgr)
controllers[ToolbarObject.GENRE] = \
GenrePopupController(plugin, album_model)
controllers[ToolbarObject.PLAYLIST] = \
PlaylistPopupController(plugin, album_model)
controllers[ToolbarObject.DECADE] = \
DecadePopupController(plugin, album_model)
controllers[ToolbarObject.SEARCH] = \
AlbumSearchEntryController(album_model)
controllers[ToolbarObject.VIEW] = viewmgr.controller
return controllers
def _on_notify_toolbar_pos(self, *args):
if self.last_toolbar_pos:
self._bars[self.last_toolbar_pos].hide()
self._bars[self.toolbar_pos].show()
self.last_toolbar_pos = self.toolbar_pos
+820
Ver Arquivo
@@ -0,0 +1,820 @@
# -*- Mode: python; coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
from bisect import bisect_left, bisect_right
import collections
import re
import logging
import sys
from collections import namedtuple
from gi.repository import GdkPixbuf
from gi.repository import Gdk
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import RB
from gi.repository import GObject
from gi.repository import Gio
import lxml.etree as ET
import rb
from coverart_browser_prefs import CoverLocale
from coverart_browser_prefs import GSetting
import coverart_rb3compat as rb3compat
from coverart_search_providers import lastfm_connected
from coverart_search_providers import get_search_providers
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
except ZeroDivisionError:
f = sys.exc_info()[2].tb_frame
for i in range(skip + 2):
f = f.f_back
lst = []
while f is not None:
lst.append((f, f.f_lineno))
f = f.f_back
return lst
def extend_traceback(tb, stack):
"""Extend traceback with stack info."""
head = tb
for tb_frame, tb_lineno in 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.
'''
uniques = []
for element in iterable:
if element not in uniques:
uniques.append(element)
return sorted(uniques)
GenreType = namedtuple("GenreType", ["name", "genre_type"])
class NaturalString(str):
'''
this class implements an object that can naturally compare
strings
i.e. "15 album" < "100 album"
'''
def __init__(self, string):
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)]
self._string_elements = alphanum_key(string)
def __lt__(self, other):
if type(other) is str:
return super(NaturalString, self).__lt__(other)
else:
return self._string_elements < other._string_elements
def __le__(self, other):
if type(other) is str:
return super(NaturalString, self).__le__(other)
else:
return self._string_elements <= other._string_elements
def __gt__(self, other):
if type(other) is str:
return super(NaturalString, self).__gt__(other)
else:
return self._string_elements > other._string_elements
def __ge__(self, other):
if type(other) is str:
return super(NaturalString, self).__ge__(other)
else:
return self._string_elements >= other._string_elements
class SortedCollection(object):
'''Sequence sorted by a key function.
SortedCollection() is much easier to work with than using bisect() directly.
It supports key functions like those use in sorted(), min(), and max().
The result of the key function call is saved so that keys can be searched
efficiently.
Instead of returning an insertion-point which can be hard to interpret, the
five find-methods return a specific item in the sequence. They can scan for
exact matches, the last item less-than-or-equal to a key, or the first item
greater-than-or-equal to a key.
Once found, an item's ordinal position can be located with the index() method.
New items can be added with the insert() and insert_right() methods.
Old items can be deleted with the remove() method.
The usual sequence methods are provided to support indexing, slicing,
length lookup, clearing, copying, forward and reverse iteration, contains
checking, item counts, item removal, and a nice looking repr.
Finding and indexing are O(log n) operations while iteration and insertion
are O(n). The initial sort is O(n log n).
The key function is stored in the 'key' attibute for easy introspection or
so that you can assign a new key function (triggering an automatic re-sort).
In short, the class was designed to handle all of the common use cases for
bisect but with a simpler API and support for key functions.
>>> from pprint import pprint
>>> from operator import itemgetter
>>> s = SortedCollection(key=itemgetter(2))
>>> for record in [
... ('roger', 'young', 30),
... ('angela', 'jones', 28),
... ('bill', 'smith', 22),
... ('david', 'thomas', 32)]:
... s.insert(record)
>>> pprint(list(s)) # show records sorted by age
[('bill', 'smith', 22),
('angela', 'jones', 28),
('roger', 'young', 30),
('david', 'thomas', 32)]
>>> s.find_le(29) # find oldest person aged 29 or younger
('angela', 'jones', 28)
>>> s.find_lt(28) # find oldest person under 28
('bill', 'smith', 22)
>>> s.find_gt(28) # find youngest person over 28
('roger', 'young', 30)
>>> r = s.find_ge(32) # find youngest person aged 32 or older
>>> s.index(r) # get the index of their record
3
>>> s[3] # fetch the record at that index
('david', 'thomas', 32)
>>> s.key = itemgetter(0) # now sort by first name
>>> pprint(list(s))
[('angela', 'jones', 28),
('bill', 'smith', 22),
('david', 'thomas', 32),
('roger', 'young', 30)]
'''
def __init__(self, iterable=(), key=None):
self._given_key = key
key = (lambda x: x) if key is None else key
decorated = sorted((key(item), item) for item in iterable)
self._keys = [k for k, item in decorated]
self._items = [item for k, item in decorated]
self._key = key
def _getkey(self):
return self._key
def _setkey(self, key):
if key is not self._key:
self.__init__(self._items, key=key)
def _delkey(self):
self._setkey(None)
key = property(_getkey, _setkey, _delkey, 'key function')
def clear(self):
self.__init__([], self._key)
def copy(self):
return self.__class__(self, self._key)
def __len__(self):
return len(self._items)
def __getitem__(self, i):
return self._items[i]
def __iter__(self):
return iter(self._items)
def __reversed__(self):
return ReversedSortedCollection(self)
def __repr__(self):
return '%s(%r, key=%s)' % (
self.__class__.__name__,
self._items,
getattr(self._given_key, '__name__', repr(self._given_key))
)
def __reduce__(self):
return self.__class__, (self._items, self._given_key)
def __contains__(self, item):
k = self._key(item)
i = bisect_left(self._keys, k)
j = bisect_right(self._keys, k)
return item in self._items[i:j]
def index(self, item):
'Find the position of an item. Raise ValueError if not found.'
k = self._key(item)
i = bisect_left(self._keys, k)
j = bisect_right(self._keys, k)
return self._items[i:j].index(item) + i
def count(self, item):
'Return number of occurrences of item'
k = self._key(item)
i = bisect_left(self._keys, k)
j = bisect_right(self._keys, k)
return self._items[i:j].count(item)
def insert(self, item):
'Insert a new item. If equal keys are found, add to the left'
k = self._key(item)
i = bisect_left(self._keys, k)
self._keys.insert(i, k)
self._items.insert(i, item)
return i
def reorder(self, item):
'''Reorder an item. If its key changed, then the item is
repositioned, otherwise the item stays untouched'''
index = self._items.index(item)
new_index = -1
if self._keys[index] != self._key(item):
del self._keys[index]
del self._items[index]
new_index = self.insert(item)
return new_index
def insert_all(self, items):
for item in items:
self.insert(item)
def remove(self, item):
'Remove first occurence of item. Raise ValueError if not found'
i = self.index(item)
del self._keys[i]
del self._items[i]
class ReversedSortedCollection(object):
def __init__(self, sorted_collection):
self._sorted_collection = sorted_collection
def __getattr__(self, name):
return getattr(self._sorted_collection, name)
def copy(self):
return self.__class__(self._sorted_collection)
def _getkey(self):
return self._key
def _setkey(self, key):
if key is not self._key:
self.__init__(SortedCollection(self._items, key=key))
def _delkey(self):
self._setkey(None)
key = property(_getkey, _setkey, _delkey, 'key function')
def __len__(self):
return len(self._sorted_collection)
def __getitem__(self, i):
return self._items[len(self) - i - 1]
def __iter__(self):
return iter(reversed(self._items))
def __reversed__(self):
return self._sorted_collection
def __repr__(self):
return '%s(%r, key=%s)' % (
self.__class__.__name__,
reversed(self._items),
getattr(self._given_key, '__name__', repr(self._given_key))
)
def __reduce__(self):
return self.__class__, (reversed(self._items), self._given_key)
def insert(self, item):
'Insert a new item. If equal keys are found, add to the left'
i = self._sorted_collection.insert(item)
return len(self) - i - 1
def index(self, item):
'Find the position of an item. Raise ValueError if not found.'
return len(self) - self._sorted_collection.index(item) - 1
class IdleCallIterator(object):
def __init__(self, chunk, process, after=None, error=None, finish=None):
default = lambda *_: None
self._chunk = chunk
self._process = process
self._after = after if after else default
self._error = error if error else default
self._finish = finish if finish else default
self._stop = False
def __call__(self, iterator, **data):
self._iter = iterator
Gdk.threads_add_idle(GLib.PRIORITY_DEFAULT_IDLE, self._idle_call, data)
def _idle_call(self, data):
if self._stop:
return False
for i in range(self._chunk):
try:
next_elem = next(self._iter)
self._process(next_elem, data)
except StopIteration:
self._finish(data)
return False
except Exception as e:
self._error(e)
self._after(data)
return True
def stop(self):
self._stop = True
def idle_iterator(func):
def iter_function(obj, iterator, **data):
idle_call = IdleCallIterator(*func(obj))
idle_call(iterator, **data)
return idle_call
return iter_function
class Theme:
'''
This class manages the theme details
'''
# storage for the instance reference
__instance = None
class _impl(GObject.Object):
""" Implementation of the singleton interface """
# properties
theme = GObject.property(type=str, default="standard")
# signals
'''
changed = signal emitted when a theme has changed
'''
__gsignals__ = {
'theme_changed': (GObject.SIGNAL_RUN_LAST, None, ())
}
# below public variables and methods that can be called for Theme
def __init__(self, plugin):
'''
Initializes the singleton interface, assigning all the constants
used to access the plugin's settings.
'''
super(Theme._impl, self).__init__()
self.plugin = plugin
popups = rb.find_plugin_file(plugin, 'img/popups.xml')
root = ET.parse(open(popups)).getroot()
base = 'theme/theme'
self.themes = []
for elem in root.xpath(base):
self.themes.append(elem.attrib['folder_name'])
self.gs = GSetting()
self.setting = self.gs.get_setting(self.gs.Path.PLUGIN)
# connect properties and signals
self._connect_properties()
self._connect_signals()
@property
def current(self):
return self.setting[self.gs.PluginKey.THEME]
def _connect_properties(self):
self.setting.bind(self.gs.PluginKey.THEME, self,
'theme', Gio.SettingsBindFlags.GET)
def _connect_signals(self):
self.connect('notify::theme', self._on_theme_changed,
None)
def _on_theme_changed(self, *args):
self.emit('theme_changed')
def __init__(self, plugin):
""" Create singleton instance """
# Check whether we already have an instance
if Theme.__instance is None:
# Create and remember instance
Theme.__instance = Theme._impl(plugin)
# Store instance reference as the only member in the handle
self.__dict__['_Theme__instance'] = Theme.__instance
def __getattr__(self, attr):
""" Delegate access to implementation """
return getattr(self.__instance, attr)
def __setattr__(self, attr, value):
""" Delegate access to implementation """
return setattr(self.__instance, attr, value)
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):
# load the image
base_image = GdkPixbuf.Pixbuf.new_from_file(image)
if alpha_color:
base_image = base_image.add_alpha(True, *alpha_color)
delta_y = icon_height + y_spacing
delta_x = icon_width + x_spacing
self._sprites = []
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)
base_image.copy_area(x_start + (x * delta_x),
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)
self._sprites.append(sprite)
def __len__(self):
return len(self._sprites)
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)
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'])
y_spacing = int(root.xpath(base + 'spacing')[0].attrib['y'])
x_start = int(root.xpath(base + 'start-position')[0].attrib['x'])
y_start = int(root.xpath(base + 'start-position')[0].attrib['y'])
across_dimension = int(root.xpath(base + 'dimension')[0].attrib['across'])
down_dimension = int(root.xpath(base + 'dimension')[0].attrib['down'])
try:
alpha_color = list(map(int,
root.xpath(base + 'alpha')[0].text.split(' ')))
except:
alpha_color = None
self.names = []
self.locale_names = {}
cl = CoverLocale()
lang = cl.get_locale()
base = sprite_name + '/' + sprite_name + \
'[@spritesheet="' + sprite_name + '"]'
for elem in root.xpath(base + '[not(@xml:lang)]'):
self.names.append(elem.text)
for elem in root.xpath(base + '[@xml:lang="' + lang + '"]'):
self.locale_names[elem.text] = elem.attrib['name']
if (not self.locale_names) and len(lang) > 2:
for elem in root.xpath(base + '[@xml:lang="' + \
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)
self._genre_db = RB.ExtDB(name='cb_genre')
def __len__(self):
return len(self._sheet)
def __getitem__(self, name):
try:
return self._sheet[self.names.index(name)]
except:
return None
def __contains__(self, name):
return name in self.names
def keys(self):
return self.names
class GenreConfiguredSpriteSheet(ConfiguredSpriteSheet):
'''
A sprite-sheet of genres. Creates a pixbuf representation of a picture
that has several icons in a regular pattern. This uses the file
'popups.xml' for its definition
:plugin: rhythmbox plugin
:sprite_name: `str` containing name of the spritesheet pattern in
popups.xml
:size: `int` array dimension of the final sprite which is to be used.
output:
:names: `str` array of sprite names
'''
# types of genre
GENRE_USER = 1
GENRE_SYSTEM = 2
GENRE_LOCALE = 3
def __init__(self, plugin, sprite_name, size=None):
super(GenreConfiguredSpriteSheet, self).__init__(plugin, sprite_name,
size)
self.genre_alternate = {} # contains GenreType tuples
self._alt_icons = {}
self._sprite_name = sprite_name
self._size = size
popups = rb.find_plugin_file(plugin, 'img/popups.xml')
root = ET.parse(open(popups)).getroot()
self._parse_popups(plugin, root, self.GENRE_SYSTEM)
try:
#self._user_popups = RB.find_user_data_file('plugins/coverart_browser/img/usericons/popups.xml')
self._user_popups = RB.user_cache_dir() + "/coverart_browser/usericons/popups.xml"
root = ET.parse(open(self._user_popups)).getroot()
self._parse_popups(plugin, root, self.GENRE_USER)
elem = root.xpath(self._sprite_name + '/index')
curr_index = int(elem[0].text)
for index in range(0, curr_index + 1):
key = RB.ExtDBKey.create_lookup('icon', str(index))
icon_location = self._genre_db.lookup(key)
sprite = GdkPixbuf.Pixbuf.new_from_file(icon_location)
if self._size:
sprite = sprite.scale_simple(self._size[0], self._size[1],
GdkPixbuf.InterpType.BILINEAR)
self._alt_icons[str(index)] = sprite
self.names.append(str(index))
except:
pass
def __getitem__(self, name):
try:
return self._alt_icons[name]
except:
return self._sheet[self.names.index(name)]
def _parse_popups(self, plugin, root, genre_type):
icon_names = {}
cl = CoverLocale()
lang = cl.get_locale()
base = self._sprite_name + '/alt'
for elem in root.xpath(base + '[not(@xml:lang)]/alt'):
self.genre_alternate[GenreType(name=elem.text, genre_type=genre_type)] = elem.attrib['genre']
for elem in root.xpath(base + '[@xml:lang="' + lang + '"]/alt'):
self.genre_alternate[GenreType(name=elem.text, genre_type=self.GENRE_LOCALE)] = elem.attrib['genre']
# 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'):
self.genre_alternate[GenreType(name=elem.text, genre_type=self.GENRE_LOCALE)] = elem.attrib['genre']
def add_genre_icon(self, filename):
root = ET.parse(open(self._user_popups)).getroot()
elem = root.xpath(self._sprite_name + '/index')
next_index = int(elem[0].text)
elem[0].text = str(next_index + 1)
tree = ET.ElementTree(root)
tree.write(self._user_popups, pretty_print=True, xml_declaration=True)
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)
new_genre = GenreType(name=str(next_index), genre_type=self.GENRE_USER)
if self._size:
pixbuf = pixbuf.scale_simple(self._size[0], self._size[1],
GdkPixbuf.InterpType.BILINEAR)
self._alt_icons[new_genre.name] = pixbuf
self.names.append(new_genre.name)
return new_genre
def delete_genre(self, current_genre):
root = ET.parse(open(self._user_popups)).getroot()
base = self._sprite_name + '/alt/alt'
found = False
for elem in root.xpath(base):
if RB.search_fold(elem.text) == RB.search_fold(current_genre):
found = True
break
if found:
elem.getparent().remove(elem)
tree = ET.ElementTree(root)
tree.write(self._user_popups, pretty_print=True, xml_declaration=True)
else:
print("not found to delete")
def amend_genre_info(self, current_genre, new_genre, icon_name):
root = ET.parse(open(self._user_popups)).getroot()
base = self._sprite_name + '/alt/alt'
found = False
if current_genre != "":
for elem in root.xpath(base):
if RB.search_fold(elem.text) == RB.search_fold(current_genre):
found = True
del self.genre_alternate[GenreType(name=elem.text, genre_type=self.GENRE_USER)]
break
else:
elem = ET.SubElement(root.xpath(self._sprite_name + '/alt')[0], "alt")
if elem != None:
found = True
if found:
elem.text = rb3compat.unicodestr(new_genre, 'utf-8')
elem.attrib['genre'] = icon_name
tree = ET.ElementTree(root)
tree.write(self._user_popups, pretty_print=True, xml_declaration=True)
self.genre_alternate[GenreType(name=elem.text, genre_type=self.GENRE_USER)] = icon_name
return GenreType(name=elem.text, genre_type=self.GENRE_USER)
else:
print("nothing found to amend")
return None
def get_stock_size():
what, width, height = Gtk.icon_size_lookup(Gtk.IconSize.BUTTON)
return width, height
def create_pixbuf_from_file_at_size(filename, width, height):
pixbuf = GdkPixbuf.Pixbuf.new_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)
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
returns True if connected with an account
Also returns True if lastFM is not in the list of search providers
'''
providers = get_search_providers()
print(providers)
print(force_check)
if force_check or 'lastfm-search' in providers:
connected = lastfm_connected()
print(connected)
return connected
elif not 'lastfm-search' in providers:
print("not lastm-search")
return True
else:
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())
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+81
Ver Arquivo
@@ -0,0 +1,81 @@
/* ContentFlowAddOn_black, version 2.0
* (c) 2008 - 2010 Sebastian Kutsch
* <http://www.jacksasylum.eu/ContentFlow/>
*
* This file is distributed under the terms of the MIT license.
* (see http://www.jacksasylum.eu/ContentFlow/LICENSE)
*
*--------------------------------------------------------------------------*/
/* ========== ContentFlow ========== */
/*
* Within this file you can ajust the styling of ContentFlow
* to your personal needs. The default styling is the same as found on the
* projectpage.
*
*/
.ContentFlowAddOn_black {
background: black;
}
/* ----- styling of items ----- */
.ContentFlowAddOn_black .flow .item .caption {
background: url(img/1x1_0.5_black.png);
}
* html .ContentFlowAddOn_black .flow .item .caption {
background-image: none;
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=scale, src='img/1x1_0.5_black.png');
}
.ContentFlowAddOn_black .flow .item .caption a,
.ContentFlowAddOn_black .flow .item .caption a:link,
.ContentFlowAddOn_black .flow .item .caption a:visited,
.ContentFlowAddOn_black .flow .item .caption a:active,
.ContentFlowAddOn_black .flow .item .caption a:hover {
color: black;
}
/* ----- scrollbar ----- */
.ContentFlowAddOn_black .scrollbar {
background: url(img/scrollbar_white.png) left center repeat-x;
}
.ContentFlowAddOn_black .scrollbar .slider {
background: url(img/slider_white.png) center center no-repeat;
}
/* only for IE <= 6 and a alphatransparent slider image */
* html .ContentFlowAddOn_black .scrollbar .slider { background-image: none; }
* html .ContentFlowAddOn_black .scrollbar .slider .virtualSlider {
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='img/slider_white.png');
}
.ContentFlowAddOn_black .scrollbar .slider .position {
color:silver;
}
/* ----- global caption ----- */
.ContentFlowAddOn_black .globalCaption {
color: white;
}
.ContentFlowAddOn_black .globalCaption .caption a,
.ContentFlowAddOn_black .globalCaption .caption a:link,
.ContentFlowAddOn_black .globalCaption .caption a:visited,
.ContentFlowAddOn_black .globalCaption .caption a:active,
.ContentFlowAddOn_black .globalCaption .caption a:hover {
color: white;
}
/* ----- load indicator ----- */
.ContentFlowAddOn_black .loadIndicator {
background: url(img/1x1_0.5_black.png);
}
* html .ContentFlowAddOn_black .loadIndicator {
background-image: none;
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=scale, src='img/1x1_0.5_black.png');
}
.ContentFlowAddOn_black .loadIndicator .indicator {
background: url(img/loader_black.gif) center center no-repeat;
}
/* ================================= */
+19
Ver Arquivo
@@ -0,0 +1,19 @@
/* ContentFlowAddOn_black, version 2.0
* (c) 2008 - 2010 Sebastian Kutsch
* <http://www.jacksasylum.eu/ContentFlow/>
*
* This file is distributed under the terms of the MIT license.
* (see http://www.jacksasylum.eu/ContentFlow/LICENSE)
*/
new ContentFlowAddOn ('black', {
init: function () {
this.addStylesheet();
},
ContentFlowConf: {
reflectionColor: "#000000" // none, transparent, overlay or hex RGB CSS style #RRGGBB
}
});
+65
Ver Arquivo
@@ -0,0 +1,65 @@
/* ContentFlowAddOn_carousel, version 1.1
* (c) 2008 - 2010 Sebastian Kutsch
* <http://www.jacksasylum.eu/ContentFlow/>
*
* This file is distributed under the terms of the MIT license.
* (see http://www.jacksasylum.eu/ContentFlow/LICENSE)
*
*--------------------------------------------------------------------------*/
.ContentFlowAddOn_carousel {
border: 5px solid #767676;
margin: 0 25px;
padding: 25px 0px;
overflow: visible;
/*overflow: hidden;*/
}
.ContentFlowAddOn_carousel #preButton,
.ContentFlowAddOn_carousel #nextButton {
position: absolute;
top: 50%;
margin-top: -25px;
width: 50px;
height: 50px;
}
.ContentFlowAddOn_carousel #preButton {
background: url(img/pre_h.png) center no-repeat;
left: -25px;
left: -28px;
}
* html .ContentFlowAddOn_carousel #preButton {
background-image: none;
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=scale, src='img/pre_h.png');
}
.ContentFlowAddOn_carousel #preButton:hover {
background: url(img/pre_h.png) center no-repeat;
}
.ContentFlowAddOn_carousel #nextButton {
background: url(img/next_h.png) center no-repeat;
right: -25px;
right: -28px;
}
* html .ContentFlowAddOn_carousel #nextButton {
background-image: none;
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=scale, src='img/next_h.png');
}
.ContentFlowAddOn_carousel #nextButton:hover {
background: url(img/next_h.png) center no-repeat;
}
/* ----- styling of items ----- */
.ContentFlowAddOn_carousel .showCaption .item .caption {
display: block !important;
}
/* ----- global caption ----- */
.ContentFlowAddOn_carousel .globalCaption {
margin-top: -3em;
}
/* ================================= */
+131
Ver Arquivo
@@ -0,0 +1,131 @@
/* ContentFlowAddOn_carousel, version 1.1
* (c) 2008 - 2010 Sebastian Kutsch
* <http://www.jacksasylum.eu/ContentFlow/>
*
* This file is distributed under the terms of the MIT license.
* (see http://www.jacksasylum.eu/ContentFlow/LICENSE)
*/
new ContentFlowAddOn ('carousel', {
conf: {
shownItems: 3,
showCaption: true,
width: 75,
height: 75,
space:0.0
},
init: function() {
this.addStylesheet();
},
onloadInit: function (flow) {
},
afterContentFlowInit: function (flow) {
var SI = flow.getAddOnConf('carousel').shownItems;
var c = flow.Container;
var ac = flow.getAddOnConf('carousel')
if (ac.showCaption) {
$CF(flow.Flow).addClassName('showCaption');
}
var p = document.createElement('div');
p.id = "preButton";
var pre = function () {
var item = flow._activeItem;
for (var i=0; i< SI; i++) { item = item.pre; }
flow.moveToItem(item);
}
p.onclick = pre;
c.appendChild(p);
var n = document.createElement('div');
n.id = "nextButton";
var next = function () {
var item = flow._activeItem;
for (var i=0; i< SI; i++) { item = item.next; }
flow.moveToItem(item);
}
n.onclick = next;
c.appendChild(n);
flow.Flow.style.fontSize = 12*(flow.maxHeight / 150) +"px";
if (flow.Browser.IE) {
window.setTimeout(function () {flow.Flow.style.overflow = "hidden"}, 1000);
}
else {
flow.Flow.style.overflow = "hidden";
}
flow.setConfig({visibleItems: Math.ceil((flow.getAddOnConf('carousel').shownItems - 1)/2) + 1});
},
ContentFlowConf: {
scaleFactorLandscape: "max", // scale factor of landscape images ('max' := height= maxItemHeight)
scaleFactorPortrait: "max",
fixItemSize: true,
relativeItemPosition: "center", // align top/above, bottom/below, left, right, center of position coordinate
visibleItems: 2, // how man item are visible on each side (-1 := auto)
reflectionHeight: 0, // float (relative to original image height)
/* ==================== actions ==================== */
onclickInactiveItem : function (item) {
this.conf.onclickActiveItem(item);
return false;
},
/* ==================== calculations ==================== */
calcStepWidth: function(diff) {
var vI = this.conf.visibleItems;
var items = this.items.length;
items = items == 0 ? 1 : items;
var absDiff = Math.abs(diff);
if (absDiff > vI) {
if (diff > 0) {
var stepwidth = diff - vI;
} else {
var stepwidth = diff + vI;
}
} else if (vI >= items) {
var stepwidth = diff / items;
} else {
var c = this.getAddOnConf('carousel');
var f = 0.1 * 2/3 * c.shownItems * diff/absDiff;
var d = diff * ( vI / items);
var stepwidth = absDiff > 0.1 ? f : d*8;
}
return stepwidth;
},
calcSize: function (item) {
var c = this.getAddOnConf('carousel');
var a = c.width / c.height;
//if (this.conf.verticalFlow) a = 1/a;
var h = 3/c.shownItems / a;
//if (this.conf.verticalFlow) h *= 2/3;
var w = h * a;
return {width: w, height: h};
},
calcCoordinates: function (item) {
var rP = item.relativePosition;
var c = this.getAddOnConf('carousel');
var w = item.size.width;
//if (this.conf.verticalFlow) w = item.size.height;
var x = rP*w/2*(1 + c.space) *this.conf.scaleFactor - w* (c.shownItems % 2 ? 0 : 0.5) / 1.4;
if (this.conf.verticalFlow) x *= 2*2/3;
var y = 0;
return {x: x, y: y};
}
}
});
+52
Ver Arquivo
@@ -0,0 +1,52 @@
/* ContentFlowAddOn_roundabout, version 3.0
* (c) 2008 - 2010 Sebastian Kutsch
* <http://www.jacksasylum.eu/ContentFlow/>
*
* This file is distributed under the terms of the MIT license.
* (see http://www.jacksasylum.eu/ContentFlow/LICENSE)
*/
new ContentFlowAddOn ('roundabout', {
ContentFlowConf: {
circularFlow: true,
visibleItems: -1,
relativeItemPosition: "top center",
endOpacity: 0.5,
/*
* calculates the size of the item at its relative position x
* returns a size object
*/
calcSize: function (item) {
var rP = item.relativePosition;
//var rPN = relativePositionNormed;
//var vI = rPN != 0 ? rP/rPN : 0 ; // visible Items
var h = 1/(Math.abs(rP)+1);
var w = h;
return {width: w, height: h};
},
/*
* calculates the position of an item within the flow element
* returns a vector object
*/
calcCoordinates: function (item) {
var rP = item.relativePosition;
var rPN = item.relativePositionNormed;
var vI = rPN != 0 ? rP/rPN : 0 ; // visible Items
var f = 1 - 1/Math.exp( Math.abs(rP)*0.75);
var x = item.side * vI/(vI+1)* f;
var y = 1;
var f = Math.sin(Math.PI * (rP*(1+1/(rP*rP+1))) / (vI+1));
var x = vI/(vI+1)* f;
var y = 1 - Math.abs(rP)*1.5/(vI+1);
return {x: x, y: y};
}
}
});
+31
Ver Arquivo
@@ -0,0 +1,31 @@
/* ContentFlowAddOn_vertical, version 2.0
* (c) 2008 - 2010 Sebastian Kutsch
* <http://www.jacksasylum.eu/ContentFlow/>
*
* This file is distributed under the terms of the MIT license.
* (see http://www.jacksasylum.eu/ContentFlow/LICENSE)
*/
new ContentFlowAddOn ('vertical', {
ContentFlowConf: {
relativeItemPosition: "center", // top, bottom, left, right, center
verticalFlow: true, // turn ContentFlow 90 degree counterclockwise
reflectionHeight: 0,
calcCoordinates: function (item) {
var rP = item.relativePosition;
var rPN = item.relativePositionNormed;
var vI = rPN != 0 ? rP/rPN : 0 ; // visible Items
var f = 1 - 1/Math.exp( Math.abs(rP)*0.75);
var x = item.side * vI/(vI+1)* f;
var y = 0;
return {x: x, y: y};
}
}
});
+80
Ver Arquivo
@@ -0,0 +1,80 @@
/* ContentFlowAddOn_white, version 2.0
* (c) 2008 - 2010 Sebastian Kutsch
* <http://www.jacksasylum.eu/ContentFlow/>
*
* This file is distributed under the terms of the MIT license.
* (see http://www.jacksasylum.eu/ContentFlow/LICENSE)
*
*--------------------------------------------------------------------------*/
/* ========== ContentFlow ========== */
/*
* Within this file you can ajust the styling of ContentFlow
* to your personal needs. The default styling is the same as found on the
* projectpage.
*
*/
.ContentFlowAddOn_white {
background: white;
}
/* ----- styling of items ----- */
.ContentFlowAddOn_white .flow .item .caption {
background: url(img/1x1_0.5_white.png);
}
* html .ContentFlowAddOn_white .flow .item .caption {
background-image: none;
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=scale, src='img/1x1_0.5_white.png');
}
.ContentFlowAddOn_white .flow .item .caption a,
.ContentFlowAddOn_white .flow .item .caption a:link,
.ContentFlowAddOn_white .flow .item .caption a:visited,
.ContentFlowAddOn_white .flow .item .caption a:active,
.ContentFlowAddOn_white .flow .item .caption a:hover {
color: black;
}
/* ----- scrollbar ----- */
.ContentFlowAddOn_white .scrollbar {
background: url(img/scrollbar_black.png) left center repeat-x;
}
.ContentFlowAddOn_white .scrollbar .slider {
background: url(img/slider_black.png) center center no-repeat;
}
/* only for IE <= 6 and a alphatransparent slider image */
* html .ContentFlowAddOn_white .scrollbar .slider { background-image: none; }
* html .ContentFlowAddOn_white .scrollbar .slider .virtualSlider {
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='img/slider_black.png');
}
.ContentFlowAddOn_white .scrollbar .slider .position {
color:gray;
}
/* ----- global caption ----- */
.ContentFlowAddOn_white .globalCaption {
color: black;
}
.ContentFlowAddOn_white .globalCaption .caption a,
.ContentFlowAddOn_white .globalCaption .caption a:link,
.ContentFlowAddOn_white .globalCaption .caption a:visited,
.ContentFlowAddOn_white .globalCaption .caption a:active,
.ContentFlowAddOn_white .globalCaption .caption a:hover {
color: black;
}
/* ----- load indicator ----- */
.ContentFlowAddOn_white .loadIndicator {
background: url(img/1x1_0.5_white.png);
}
* html .ContentFlowAddOn_white .loadIndicator {
background-image: none;
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=scale, src='img/1x1_0.5_white.png');
}
.ContentFlowAddOn_white .loadIndicator .indicator {
background: url(img/loader_white.gif) center center no-repeat;
}
/* ================================= */
+19
Ver Arquivo
@@ -0,0 +1,19 @@
/* ContentFlowAddOn_white, version 2.0
* (c) 2008 - 2010 Sebastian Kutsch
* <http://www.jacksasylum.eu/ContentFlow/>
*
* This file is distributed under the terms of the MIT license.
* (see http://www.jacksasylum.eu/ContentFlow/LICENSE)
*/
new ContentFlowAddOn ('white', {
init: function () {
this.addStylesheet();
},
ContentFlowConf: {
reflectionColor: "#ffffff" // none, transparent, overlay or hex RGB CSS style #RRGGBB
}
});
+19
Ver Arquivo
@@ -0,0 +1,19 @@
Copyright (c) 2007 - 2010 Sebastian Kutsch
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+250
Ver Arquivo
@@ -0,0 +1,250 @@
/* ========== ContentFlow ========== */
/*
* default style to look nice
*/
.ContentFlow {
}
.ContentFlow .flow {
/*border: 1px solid green;*/
}
.ContentFlow .flow * {
}
.ContentFlow .flow .item {
/*border: 1px solid red;*/
}
.ContentFlow .flow .item canvas.content {
height: 100%;
width: 100%;
/*border: 1px solid yellow;*/
}
.ContentFlow .flow .item img.content {
/*border: 1px solid yellow;*/
width: 100%;
}
.ContentFlow .flow .item img.reflection,
.ContentFlow .flow .item canvas.reflection {
width: 100%;
}
/* ----- styling of items ----- */
.ContentFlow .flow .item.active {
cursor: pointer;
}
.ContentFlow .flow .item .caption {
font-size: 100%;
font-weight: bold;
text-align: center;
color: white;
max-height: 30%;
bottom: 10%;
background: url(img/1x1_0.5_black.png);
width: 100%;
}
* html .ContentFlow .flow .item .caption {
background-image: none;
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=scale, src='img/1x1_0.5_black.png');
}
.ContentFlow .flow .item .caption a,
.ContentFlow .flow .item .caption a:link,
.ContentFlow .flow .item .caption a:visited,
.ContentFlow .flow .item .caption a:active,
.ContentFlow .flow .item .caption a:hover {
text-decoration: none;
color: white;
font-style: italic;
font-size: 0.8em;
}
.ContentFlow .flow .item .caption a:hover {
text-decoration: underline;
}
.ContentFlow .flow .item.active .caption {
/*display: block;*/ /* uncomment to show caption inside item */
}
/* ----- scrollbar ----- */
.ContentFlow .scrollbar {
width: 50%;
margin: 0px auto;
margin-top: 10px;
height: 16px;
background: url(img/scrollbar_white.png) left center repeat-x;
position: relative;
overflow: visible;
}
.ContentFlow .scrollbar .slider {
width: 16px;
height: 16px;
background: url(img/slider_white.png) center center no-repeat;
cursor: move;
}
/* only for IE <= 6 and a alphatransparent slider image */
* html .ContentFlow .scrollbar .slider { background-image: none; }
* html .ContentFlow .scrollbar .slider .virtualSlider {
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='img/slider_white.png');
}
.ContentFlow .scrollbar .slider .position {
top: 120%;
font-size: 16px;
font-weight: bold;
color: silver
}
/* ----- global caption ----- */
.ContentFlow .globalCaption {
text-align: center;
font-weight: bold;
color: white;
font-size: 14px;
height: 20px;
margin: 2em auto;
}
.ContentFlow .globalCaption .caption {
}
.ContentFlow .globalCaption .caption a,
.ContentFlow .globalCaption .caption a:link,
.ContentFlow .globalCaption .caption a:visited,
.ContentFlow .globalCaption .caption a:active,
.ContentFlow .globalCaption .caption a:hover {
text-decoration: none;
color: white;
font-style: italic;
font-size: 0.8em;
}
.ContentFlow .globalCaption .caption a:hover {
text-decoration: underline;
}
/* ----- load indicator ----- */
.ContentFlow .loadIndicator {
width: 100%;
height: 100%;
top: 0px;
left: 0px;
background: black;
}
.ContentFlow .loadIndicator .indicator {
background: url(img/loader.gif) center center no-repeat;
width: 100%;
height: 100%;
}
* html .ContentFlow .loadIndicator .indicator {
height: 100px;
}
/* ================================= */
/* ========== ContentFlow ========== */
/*
* This is the basic CSS file needed for the correct functioning of ContentFlow.
* DON'T CHANGE IT.
*
*/
.ContentFlow {
position: relative; /* needed so overlay dimensions are constrained to the ContentFlow */
overflow: hidden;
}
.ContentFlow * {
margin: 0px;
padding: 0px;
/*border: none;*/
}
.ContentFlow img {
-ms-interpolation-mode: bicubic;
}
.ContentFlow .mouseoverCheckElement {
position: absolute;
width: 0px;
height: 0px;
left: 0px;
/*display: none;*/
visibility: hidden;
}
.ContentFlow:hover .mouseoverCheckElement {
left: 1px;
/*width: 1px;*/
/*left: -1px;*/
/*background-color: red;*/
}
.ContentFlow .flow {
position: relative; /* needed so that items can be positioned relative to flow*/
z-index: 0; /* need so every item has a z-index relative to the flow-box */
visibility: hidden; /* needed so that content is hidden while loading */
width: 100%; /* needed for IE6 */
margin: 0 auto;
}
.ContentFlow .flow.hidden {
visibility: hidden;
}
.ContentFlow .flow .item {
position: absolute; /* needed */
visibility: hidden;
top: 0px;
left: 0px;
}
.ContentFlow .flow .item.active {
}
.ContentFlow .flow .item .content {
display: block;
}
.ContentFlow .flow .item div.content {
width: 100%;
height: 100%;
}
.ContentFlow .flow .item .label {
display: none;
}
.ContentFlow .flow .item .reflection {
display: block;
}
.ContentFlow .flow .item canvas.reflection {
margin-top: -1px; /* for FF */
}
.ContentFlow .flow .item .caption {
position: absolute; /* needed */
display: none; /* needed to hide it on inactive items */
}
.ContentFlow .flow .item.active .caption {
/*display: block;*/ /* uncomment to show caption inside item */
}
/* ----- scrollbar ----- */
.ContentFlow .scrollbar {
position: relative; /* needed for z-index */
z-index: 1; /* set above flow */
visibility: hidden;
}
.ContentFlow .scrollbar .slider {
position: absolute; /* needed */
}
* html .ContentFlow .scrollbar .slider .virtualSlider {
height: 100%;
}
.ContentFlow .scrollbar .slider .position {
position: absolute; /* needed */
text-align: center;
}
/* ----- global caption ----- */
.ContentFlow .globalCaption {
position: relative; /* needed for z-index */
z-index: 1; /* set above flow */
}
/* ----- load indicator ----- */
.ContentFlow .loadIndicator {
position: absolute; /* needed */
z-index: 65000; /* set above everything */
}
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+44
Ver Arquivo
@@ -0,0 +1,44 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<link rel="stylesheet" title="Standard" href="styles.css" type="text/css" media="screen" />
<!--<style>
</style> -->
<style type="text/css">
.className{
width:270px;
height:150px;
position:absolute;
left:50%;
top:50%;
margin:-75px 0 0 -135px;
}
.className p{
font-size:22px;
margin:45px 10px 10px;
color: #BACKGROUND_COLOUR;
text-align:center;
position:absolute;
}
body{
background: #BACKGROUND_COLOUR;
font-size:0.825em;
font-family:Arial, Helvetica, sans-serif;
}
</style>
</head>
<body>
<div class="className">
<p>Use the Search and Filter options to display covers
</p>
</div>
</body>
</html>
Arquivo binário não exibido.
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 82 B

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 82 B

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 43 B

+241
Ver Arquivo
@@ -0,0 +1,241 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.47 r22583"
version="1.1"
sodipodi:docname="New document 1.2009_11_04_18_16_23.0.2009_11_29_20_35_24.0.svg">
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient3612">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop3614" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop3616" />
</linearGradient>
<linearGradient
id="linearGradient3157">
<stop
style="stop-color:#ffffff;stop-opacity:0.57391304;"
offset="0"
id="stop3159" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop3161" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3157"
id="radialGradient3163"
cx="307.14285"
cy="582.36218"
fx="307.14285"
fy="582.36218"
r="158.57143"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3157"
id="radialGradient3187"
gradientUnits="userSpaceOnUse"
cx="307.14285"
cy="582.36218"
fx="307.14285"
fy="582.36218"
r="158.57143" />
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath3195">
<path
sodipodi:type="arc"
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path3197"
sodipodi:cx="268.57144"
sodipodi:cy="569.50507"
sodipodi:rx="171.42857"
sodipodi:ry="171.42857"
d="M 440.00002,569.50507 A 171.42857,171.42857 0 1 1 97.142868,569.50507 A 171.42857,171.42857 0 1 1 440.00002,569.50507 z"
transform="matrix(0.5151267,0,0,0.5151267,47.101979,154.22678)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath3205">
<path
sodipodi:type="arc"
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path3207"
sodipodi:cx="268.57144"
sodipodi:cy="569.50507"
sodipodi:rx="171.42857"
sodipodi:ry="171.42857"
d="M 440.00002,569.50507 A 171.42857,171.42857 0 1 1 97.142868,569.50507 A 171.42857,171.42857 0 1 1 440.00002,569.50507 z"
transform="matrix(0.5151267,0,0,0.5151267,385.50521,158.17142)" />
</clipPath>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3612"
id="radialGradient3618"
cx="307.14285"
cy="582.36218"
fx="307.14285"
fy="582.36218"
r="158.57143"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3612"
id="radialGradient3639"
gradientUnits="userSpaceOnUse"
cx="307.14285"
cy="582.36218"
fx="307.14285"
fy="582.36218"
r="158.57143" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
gridtolerance="10000"
guidetolerance="10"
objecttolerance="10"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="-159.28571"
inkscape:cy="405.71429"
inkscape:document-units="px"
inkscape:current-layer="g3620"
showgrid="false"
inkscape:window-width="1400"
inkscape:window-height="1002"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g3620">
<g
id="g2835"
transform="matrix(-1,0,0,1,370.9006,0)">
<path
d="m 440.00002,569.50507 c 0,94.67738 -76.75119,171.42857 -171.42858,171.42857 -94.67739,0 -171.428572,-76.75119 -171.428572,-171.42857 0,-94.67739 76.751182,-171.42858 171.428572,-171.42858 94.67739,0 171.42858,76.75119 171.42858,171.42858 z"
sodipodi:ry="171.42857"
sodipodi:rx="171.42857"
sodipodi:cy="569.50507"
sodipodi:cx="268.57144"
id="path3173"
style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:none"
sodipodi:type="arc"
transform="matrix(0.5151267,0,0,0.5151267,47.101979,154.22678)" />
<path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 193.02128,416.31621 0,17.11698 -69.31988,0 0,28.32175 69.31988,0 0,17.11698 27.09543,-15.63248 27.08251,-15.64537 -27.08251,-15.63248 -27.09543,-15.64538 z"
id="rect3165" />
<path
d="m 440.00002,569.50507 c 0,94.67738 -76.75119,171.42857 -171.42858,171.42857 -94.67739,0 -171.428572,-76.75119 -171.428572,-171.42857 0,-94.67739 76.751182,-171.42858 171.428572,-171.42858 94.67739,0 171.42858,76.75119 171.42858,171.42858 z"
sodipodi:ry="171.42857"
sodipodi:rx="171.42857"
sodipodi:cy="569.50507"
sodipodi:cx="268.57144"
id="path3175"
style="fill:none;stroke:#808080;stroke-width:10.80000019;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc"
transform="matrix(0.4679068,0,0,0.4679068,59.783914,181.11879)" />
</g>
<path
transform="matrix(0.5847384,0,0,0.5847384,30.872504,70.269726)"
d="m 465.71428,582.36218 a 158.57143,158.57143 0 1 1 -317.14285,0 158.57143,158.57143 0 1 1 317.14285,0 z"
sodipodi:ry="158.57143"
sodipodi:rx="158.57143"
sodipodi:cy="582.36218"
sodipodi:cx="307.14285"
id="path2385"
style="fill:url(#radialGradient3618);fill-opacity:1;fill-rule:evenodd;stroke:none"
sodipodi:type="arc" />
</g>
<g
id="g3627"
transform="translate(314.28571,5.7142857)">
<g
id="g3629">
<path
transform="matrix(0.5151267,0,0,0.5151267,47.101979,154.22678)"
sodipodi:type="arc"
style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path3631"
sodipodi:cx="268.57144"
sodipodi:cy="569.50507"
sodipodi:rx="171.42857"
sodipodi:ry="171.42857"
d="m 440.00002,569.50507 c 0,94.67738 -76.75119,171.42857 -171.42858,171.42857 -94.67739,0 -171.428572,-76.75119 -171.428572,-171.42857 0,-94.67739 76.751182,-171.42858 171.428572,-171.42858 94.67739,0 171.42858,76.75119 171.42858,171.42858 z" />
<path
id="path3633"
d="m 193.02128,416.31621 0,17.11698 -69.31988,0 0,28.32175 69.31988,0 0,17.11698 27.09543,-15.63248 27.08251,-15.64537 -27.08251,-15.63248 -27.09543,-15.64538 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none" />
<path
transform="matrix(0.4679068,0,0,0.4679068,59.783914,181.11879)"
sodipodi:type="arc"
style="fill:none;stroke:#808080;stroke-width:10.80000019;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3635"
sodipodi:cx="268.57144"
sodipodi:cy="569.50507"
sodipodi:rx="171.42857"
sodipodi:ry="171.42857"
d="m 440.00002,569.50507 c 0,94.67738 -76.75119,171.42857 -171.42858,171.42857 -94.67739,0 -171.428572,-76.75119 -171.428572,-171.42857 0,-94.67739 76.751182,-171.42858 171.428572,-171.42858 94.67739,0 171.42858,76.75119 171.42858,171.42858 z" />
</g>
<path
sodipodi:type="arc"
style="fill:url(#radialGradient3639);fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path3637"
sodipodi:cx="307.14285"
sodipodi:cy="582.36218"
sodipodi:rx="158.57143"
sodipodi:ry="158.57143"
d="m 465.71428,582.36218 c 0,87.57658 -70.99485,158.57143 -158.57143,158.57143 -87.57658,0 -158.57142,-70.99485 -158.57142,-158.57143 0,-87.57658 70.99484,-158.57142 158.57142,-158.57142 87.57658,0 158.57143,70.99484 158.57143,158.57142 z"
transform="matrix(0.5847384,0,0,0.5847384,30.872504,70.269726)" />
</g>
</g>
</svg>

Depois

Largura:  |  Altura:  |  Tamanho: 9.2 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 8.0 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 8.0 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 8.0 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 2.0 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 3.0 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 1.9 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 3.0 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 82 B

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 82 B

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 175 B

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 178 B

+55
Ver Arquivo
@@ -0,0 +1,55 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<link rel="stylesheet" title="Standard" href="styles.css" type="text/css" media="screen" />
<!-- styles.css contains white for background colour - we'll need an equivalent css statement for a black background -->
<script language="JavaScript" type="text/javascript" src="contentflow.js" load="#BACKGROUND_COLOUR #ADDON"></script>
<script style="text/javascript">
var cf = new ContentFlow('contentFlow',
{reflectionColor: "#000000",
endOpacity : 0.3,
circularFlow: true,
startItem: #START,
scaleFactor: #FACTOR,
visibleItems: 7
});
</script>
<style>
body{
background: #BACKGROUND_COLOUR;
color: #FOREGROUND_COLOUR;
}
</style>
</head>
<body>
#MAXCOVERS
<div class="maincontent">
<div style="width: #WIDTHpx; margin: 0px auto;">
<!-- ===== FLOW ===== -->
<div id="contentFlow" class="ContentFlow">
<!-- should be place before flow so that contained images will be loaded first -->
<div class="loadIndicator"><div class="indicator"></div></div>
<div class="flow">
#ITEMS
</div>
#GLOBAL_CAPTION
<!--
<div class="scrollbar">
<div class="slider"></div>
</div>
-->
</div>
</div>
</div>
</body>
</html>
+231
Ver Arquivo
@@ -0,0 +1,231 @@
body {
background: white;
color: white;
font-size: 8pt;
font-family: sans-serif;
margin: 0;
padding: 0 7%;
}
.className{
background-color:#338BC7;
width:270px;
height:150px;
position:relative;
-moz-border-radius:6px;
-webkit-border-radius:6px;
border-radius:6px;
}
a,
a:link,
a:visited,
a:active,
a:hover {
text-decoration: none;
color: white;
font-weight: bold;
}
a:hover {
text-decoration: underline;
}
.clear { clear: both; }
img { border: none; }
h1 {
text-align: center;
text-decoration: underline;
font-family: serif;
color: white;
font-size: 1.8em;
}
h2 {
margin-top: 2.5em;
font-size: 1.7em;
text-decoration: underline;
}
h3 {
margin-top: 2em;
font-size: 1.3em;
text-decoration: underline;
}
p {
line-height: 150%;
text-align: justify;
}
pre {
font-family: sans-serif;
background: #222;
}
.simpleBlack {
background: black;
width: 80ex;
margin: 0 auto;
font-family: sans-serif;
}
code {
font-family: monospace;
font-size: 0.9em;
line-height: 150%;
}
dl {
margin-left: 4ex;
}
dl dt {
font-size: 0.9em;
font-weight: bold;
color: #eee;
font-family: monospace;
}
dl dd {
margin-bottom: 1.5em;
color: #ccc;
font-size: 0.85em;
line-height: 1.5em;
}
ol {
}
ol li {
margin-bottom: 1em;
line-height: 150%;
}
ul {
}
ul li {
margin-bottom: 0;
}
/* ------------------------------------------------------------------------- */
#title {
margin: 0px auto;
}
#sponsor {
position: absolute;
right: 7%;
font-size: 10px;
height: 66px;
line-height: 66px;
padding: 0 20px;
}
div.maincontent {
margin: 0px auto 20pt auto;
}
#menu {
display: block;
text-align: center;
background: #222;
padding: 1em;
margin: 2em auto 4em auto;
}
#menu li {
display: inline;
padding: 0px 20px;
}
.totop {
text-align: center;
margin: 4em 10%;
padding: 0.25em;
background: #161616;
}
.totop a {
margin-right: 7%;
color: silver;
}
.block {
display: none;
}
#browserComp {
border-collapse: collapse;
margin: 40px auto;
}
#browserComp caption {
text-align: center;
margin: 20px auto;
font-size: 1.1em;
font-weight: bold;
}
#browserComp th {
font-size: 10px;
padding: 5px 10px;
border-bottom: 1px solid silver;
}
#browserComp th img {
width: 22px;
}
#browserComp td {
text-align: center;
}
#browserComp th.feature,
#browserComp td.feature {
text-align: right;
padding: 5px;
padding-right: 10px;
border-right: 1px solid silver;
}
.addon {
border: 1px solid #222;
padding: 20px;
margin-bottom: 20px;
background: #222;
}
.addon .flowBox {
width: 500px;
margin-right: 40px;
float: left;
}
.addon .discription {
margin-left: 540px;
}
.addon .title {
line-height: 1.3em;
}
.addon .title h3 {
margin: 0;
display: inline;
}
.addon .title .by {
margin-left: 2em;
font-size: 0.8em;
text-decoration: none;
}
.addon p {
font-size: 0.9em;
}
.addon .comment {
margin-top: 2em;
font-style: italic;
}
.addon .download {
font-size: 0.8em;
float: right;
margin: 0 0 2em 4em;
line-height: 1.5em;
}
.addon .download a {
font-weight: normal;
}
#info { /* for example.php */
color: white;
/*display: none;*/
}
+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
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 1.2 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 1.2 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 1.0 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 1.0 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 14 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 9.6 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 7.2 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 8.5 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 7.6 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 9.0 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 541 B

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 3.7 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 2.1 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 2.0 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 33 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 1.8 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 40 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 15 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 18 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 3.4 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 13 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 16 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 4.8 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 1.3 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 1.2 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 54 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 2.7 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 68 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 19 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 3.8 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 17 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 22 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 2.7 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 3.9 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 368 B

BIN
Ver Arquivo
Arquivo binário não exibido.
+202
Ver Arquivo
@@ -0,0 +1,202 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="Franco.svg">
<defs
id="defs4">
<linearGradient
y2="244.95164"
x2="99.124153"
y1="400.5535"
x1="101.42937"
gradientTransform="matrix(1.340328,0,0,1.340328,-31.467381,555.95415)"
gradientUnits="userSpaceOnUse"
id="linearGradient6460-1"
xlink:href="#linearGradient6364-8"
inkscape:collect="always" />
<linearGradient
inkscape:collect="always"
id="linearGradient6364-8">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop6366-5" />
<stop
style="stop-color:#4d4d4d;stop-opacity:0;"
offset="1"
id="stop6368-9" />
</linearGradient>
<linearGradient
y2="244.95164"
x2="99.124153"
y1="400.5535"
x1="101.42937"
gradientTransform="matrix(1.340328,0,0,1.340328,133.59345,299.76445)"
gradientUnits="userSpaceOnUse"
id="linearGradient3022"
xlink:href="#linearGradient6364-8"
inkscape:collect="always" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient6364-8"
id="linearGradient3966"
x1="440.96436"
y1="94.980904"
x2="447.48547"
y2="491.33676"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient6364-8-2"
id="linearGradient3966-8"
x1="440.96436"
y1="94.980904"
x2="447.48547"
y2="491.33676"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient6364-8-2">
<stop
style="stop-color:#4d4d4d;stop-opacity:1;"
offset="0"
id="stop6366-5-8" />
<stop
style="stop-color:#4d4d4d;stop-opacity:0;"
offset="1"
id="stop6368-9-1" />
</linearGradient>
<filter
id="filter4002"
inkscape:label="Inner Shadow"
inkscape:menu="Shadows and Glows"
inkscape:menu-tooltip="Adds a colorizable drop shadow inside"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur4004"
stdDeviation="7"
result="result8" />
<feOffset
id="feOffset4006"
dx="4"
dy="4"
result="result11" />
<feComposite
id="feComposite4008"
in2="result11"
result="result6"
in="SourceGraphic"
operator="in" />
<feFlood
id="feFlood4010"
result="result10"
in="result6"
flood-opacity="1"
flood-color="rgb(0,0,0)" />
<feBlend
id="feBlend4012"
in2="result10"
mode="normal"
in="result6"
result="result12" />
<feComposite
id="feComposite4014"
in2="SourceGraphic"
result="result2"
operator="in" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.52225797"
inkscape:cx="353.55944"
inkscape:cy="380.57923"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1280"
inkscape:window-height="1000"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
id="path3048"
style="fill:#333333"
d="m 196.23032,388.02753 c -3.1625,-0.17639 -5.74997,-0.41075 -5.74994,-0.5208 4e-5,-0.11006 1.35004,-7.10107 3,-15.53558 1.64997,-8.43452 2.99994,-15.52202 2.99994,-15.75 0,-0.22799 3.08413,-0.41452 6.85362,-0.41452 l 6.85363,0 3.13824,15.6748 c 1.72603,8.62114 3.0294,15.7589 2.89638,15.86169 -0.62424,0.48234 -14.87054,0.97005 -19.99187,0.68441 z m -37.58129,91.8046 c 5.68507,-26.43563 21.62839,-41.0255 44.83129,-41.0255 18.15518,0 31.82505,8.81969 39.52313,25.5 2.87004,6.21885 3.86007,9.44962 6.43518,21 l 0.66884,3 21.92273,0 c 20.18917,0 21.94973,-0.14015 22.26407,-1.77237 0.40759,-2.11648 -10.69635,-24.34334 -26.54806,-53.14148 -11.39707,-20.70531 -11.50643,-20.96649 -10.96706,-26.19245 0.39075,-3.78605 0.15862,-5.5991 -0.82097,-6.41209 -1.04547,-0.86766 -1.11356,-1.68703 -0.29035,-3.49378 1.48268,-3.25413 0.45149,-5.28386 -2.87844,-5.66571 -2.4629,-0.28243 -3.42556,-1.55441 -7.8119,-10.32212 -6.23802,-12.46895 -13.04989,-32.67223 -12.70848,-37.69199 0.14323,-2.10603 -0.32646,-4.23152 -1.0934,-4.94795 -1.12497,-1.05087 -1.10874,-1.72854 0.0994,-4.15034 0.79415,-1.59191 1.50272,-5.1916 1.5746,-7.9993 0.11147,-4.35393 -0.19922,-5.27281 -2.112,-6.24624 -3.68961,-1.87767 -6.00171,-5.26503 -6.6488,-9.74089 -1.36194,-9.42061 -8.59426,-100.10119 -10.02716,-125.72329 -1.81034,-32.3711 -1.86263,-43.55534 -0.21854,-46.73467 2.00375,-3.87483 2.13663,-10.60596 0.24044,-12.17965 -0.76822,-0.63757 -1.16783,-2.03465 -0.88802,-3.10463 0.37015,-1.41548 -0.18621,-2.20962 -2.04244,-2.91536 -3.73433,-1.41979 -4.67277,-4.0055 -4.67277,-12.87495 0,-10.503593 -0.78166,-14.190743 -3.00837,-14.190743 -2.34316,0 -2.96163,2.76543 -2.97798,13.315743 -0.0151,9.73751 -0.89079,12.30685 -4.68642,13.74995 -1.90884,0.72574 -2.41663,1.48443 -2.01682,3.0133 0.32514,1.24334 -0.0225,2.25705 -0.88803,2.58917 -2.02638,0.77759 -1.87332,9.71399 0.22113,12.91052 1.43529,2.19053 1.57348,5.00498 1.09076,22.21482 -0.30401,10.83857 -1.0474,24.6565 -1.65198,30.7065 -0.60458,6.05 -2.02755,24.275 -3.16216,40.5 -1.80776,25.85116 -3.20459,43.53305 -5.49034,69.5 -1.10665,12.57186 -2.71725,16.47258 -7.85201,19.01682 -2.69729,1.33648 -3.098,5.22174 -1.20469,11.68045 1.09273,3.72763 1.22363,5.68776 0.47345,7.08948 -0.57027,1.06555 -0.81718,3.08648 -0.5487,4.49096 0.65367,3.41945 -7.51963,27.25432 -13.27839,38.72229 -4.17675,8.31754 -4.76589,9.02376 -7.76945,9.31336 -3.47466,0.33502 -4.25346,2.44314 -2.27101,6.14738 0.70326,1.31405 0.58446,2.15668 -0.42181,2.99181 -1.02028,0.84675 -1.25209,2.60341 -0.85335,6.46677 0.53305,5.16476 0.33701,5.68256 -7.45546,19.69245 -11.62472,20.89979 -25.68313,47.58052 -28.25219,53.61838 -3.64643,8.56989 -4.21545,8.34839 20.65307,8.03942 l 21.697,-0.26957 1.82246,-8.4745 z"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/jrbastien/Ubuntu One/CoverArt/0.8 dark icons/Tour_Effel_Dark.png"
inkscape:export-xdpi="29.227327"
inkscape:export-ydpi="29.227327" />
<path
style="fill:#000000"
d=""
id="path3019"
inkscape:connector-curvature="0" />
<path
style="fill:#000000"
d=""
id="path3017"
inkscape:connector-curvature="0" />
<path
style="fill:#000000"
d=""
id="path3015"
inkscape:connector-curvature="0" />
<path
style="fill:#000000"
d=""
id="path3013"
inkscape:connector-curvature="0" />
<path
id="path3048-1"
style="fill:url(#linearGradient3966);stroke:#666666;fill-opacity:1;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none"
d="m 435.07498,387.56901 c -3.1625,-0.17639 -5.74997,-0.41075 -5.74994,-0.5208 4e-5,-0.11006 1.35004,-7.10107 3,-15.53558 1.64997,-8.43452 2.99994,-15.52202 2.99994,-15.75 0,-0.22799 3.08413,-0.41452 6.85362,-0.41452 l 6.85363,0 3.13824,15.6748 c 1.72603,8.62114 3.0294,15.7589 2.89638,15.86169 -0.62424,0.48234 -14.87054,0.97005 -19.99187,0.68441 z m -37.58129,91.8046 c 5.68507,-26.43563 21.62839,-41.0255 44.83129,-41.0255 18.15518,0 31.82505,8.81969 39.52313,25.5 2.87004,6.21885 3.86007,9.44962 6.43518,21 l 0.66884,3 21.92273,0 c 20.18917,0 21.94973,-0.14015 22.26407,-1.77237 0.40759,-2.11648 -10.69635,-24.34334 -26.54806,-53.14148 -11.39707,-20.70531 -11.50643,-20.96649 -10.96706,-26.19245 0.39075,-3.78605 0.15862,-5.5991 -0.82097,-6.41209 -1.04547,-0.86766 -1.11356,-1.68703 -0.29035,-3.49378 1.48268,-3.25413 0.45149,-5.28386 -2.87844,-5.66571 -2.4629,-0.28243 -3.42556,-1.55441 -7.8119,-10.32212 -6.23802,-12.46895 -13.04989,-32.67223 -12.70848,-37.69199 0.14323,-2.10603 -0.32646,-4.23152 -1.0934,-4.94795 -1.12497,-1.05087 -1.10874,-1.72854 0.0994,-4.15034 0.79415,-1.59191 1.50272,-5.1916 1.5746,-7.9993 0.11147,-4.35393 -0.19922,-5.27281 -2.112,-6.24624 -3.68961,-1.87767 -6.00171,-5.26503 -6.6488,-9.74089 -1.36194,-9.42061 -8.59426,-100.10119 -10.02716,-125.72329 -1.81034,-32.3711 -1.86263,-43.55534 -0.21854,-46.73467 2.00375,-3.87483 2.13663,-10.60596 0.24044,-12.17965 -0.76822,-0.63757 -1.16783,-2.03465 -0.88802,-3.10463 0.37015,-1.41548 -0.18621,-2.20962 -2.04244,-2.91536 -3.73433,-1.41979 -4.67277,-4.0055 -4.67277,-12.87495 0,-10.503592 -0.78166,-14.190742 -3.00837,-14.190742 -2.34316,0 -2.96163,2.76543 -2.97798,13.315742 -0.0151,9.73751 -0.89079,12.30685 -4.68642,13.74995 -1.90884,0.72574 -2.41663,1.48443 -2.01682,3.0133 0.32514,1.24334 -0.0225,2.25705 -0.88803,2.58917 -2.02638,0.77759 -1.87332,9.71399 0.22113,12.91052 1.43529,2.19053 1.57348,5.00498 1.09076,22.21482 -0.30401,10.83857 -1.0474,24.6565 -1.65198,30.7065 -0.60458,6.05 -2.02755,24.275 -3.16216,40.5 -1.80776,25.85116 -3.20459,43.53305 -5.49034,69.5 -1.10665,12.57186 -2.71725,16.47258 -7.85201,19.01682 -2.69729,1.33648 -3.098,5.22174 -1.20469,11.68045 1.09273,3.72763 1.22363,5.68776 0.47345,7.08948 -0.57027,1.06555 -0.81718,3.08648 -0.5487,4.49096 0.65367,3.41945 -7.51963,27.25432 -13.27839,38.72229 -4.17675,8.31754 -4.76589,9.02376 -7.76945,9.31336 -3.47466,0.33502 -4.25346,2.44314 -2.27101,6.14738 0.70326,1.31405 0.58446,2.15668 -0.42181,2.99181 -1.02028,0.84675 -1.25209,2.60341 -0.85335,6.46677 0.53305,5.16476 0.33701,5.68256 -7.45546,19.69245 -11.62472,20.89979 -25.68313,47.58052 -28.25219,53.61838 -3.64643,8.56989 -4.21545,8.34839 20.65307,8.03942 l 21.697,-0.26957 1.82246,-8.4745 z"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/jrbastien/Ubuntu One/CoverArt/0.8 icons/Tour_Effel.png"
inkscape:export-xdpi="28.748562"
inkscape:export-ydpi="28.748562" />
<path
id="path3048-1-8"
style="fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;filter:url(#filter4002)"
d="m 201.47395,870.08916 c -3.1625,-0.17639 -5.74997,-0.41075 -5.74994,-0.5208 4e-5,-0.11006 1.35004,-7.10107 3,-15.53558 1.64997,-8.43452 2.99994,-15.52202 2.99994,-15.75 0,-0.22799 3.08413,-0.41452 6.85362,-0.41452 l 6.85363,0 3.13824,15.6748 c 1.72603,8.62114 3.0294,15.7589 2.89638,15.86169 -0.62424,0.48234 -14.87054,0.97005 -19.99187,0.68441 z m -37.58129,91.8046 c 5.68507,-26.43563 21.62839,-41.0255 44.83129,-41.0255 18.15518,0 31.82505,8.81969 39.52313,25.5 2.87004,6.21885 3.86007,9.44962 6.43518,21 l 0.66884,3 21.92273,0 c 20.18917,0 21.94973,-0.14015 22.26407,-1.77237 0.40759,-2.11648 -10.69635,-24.34334 -26.54806,-53.14148 -11.39707,-20.70531 -11.50643,-20.96649 -10.96706,-26.19245 0.39075,-3.78605 0.15862,-5.5991 -0.82097,-6.41209 -1.04547,-0.86766 -1.11356,-1.68703 -0.29035,-3.49378 1.48268,-3.25413 0.45149,-5.28386 -2.87844,-5.66571 -2.4629,-0.28243 -3.42556,-1.55441 -7.8119,-10.32212 -6.23802,-12.46895 -13.04989,-32.67223 -12.70848,-37.69199 0.14323,-2.10603 -0.32646,-4.23152 -1.0934,-4.94795 -1.12497,-1.05087 -1.10874,-1.72854 0.0994,-4.15034 0.79415,-1.59191 1.50272,-5.1916 1.5746,-7.9993 0.11147,-4.35393 -0.19922,-5.27281 -2.112,-6.24624 -3.68961,-1.87767 -6.00171,-5.26503 -6.6488,-9.74089 -1.36194,-9.42061 -8.59426,-100.10119 -10.02716,-125.72328 -1.81034,-32.3711 -1.86263,-43.55534 -0.21854,-46.73467 2.00375,-3.87483 2.13663,-10.60596 0.24044,-12.17965 -0.76822,-0.63757 -1.16783,-2.03465 -0.88802,-3.10463 0.37015,-1.41548 -0.18621,-2.20962 -2.04244,-2.91536 -3.73433,-1.41979 -4.67277,-4.0055 -4.67277,-12.87495 0,-10.5036 -0.78166,-14.19075 -3.00837,-14.19075 -2.34316,0 -2.96163,2.76543 -2.97798,13.31575 -0.0151,9.73751 -0.89079,12.30685 -4.68642,13.74995 -1.90884,0.72574 -2.41663,1.48443 -2.01682,3.0133 0.32514,1.24334 -0.0225,2.25705 -0.88803,2.58917 -2.02638,0.77759 -1.87332,9.71399 0.22113,12.91052 1.43529,2.19053 1.57348,5.00498 1.09076,22.21482 -0.30401,10.83857 -1.0474,24.6565 -1.65198,30.70649 -0.60458,6.05 -2.02755,24.275 -3.16216,40.5 -1.80776,25.85116 -3.20459,43.53305 -5.49034,69.5 -1.10665,12.57186 -2.71725,16.47258 -7.85201,19.01682 -2.69729,1.33648 -3.098,5.22174 -1.20469,11.68045 1.09273,3.72763 1.22363,5.68776 0.47345,7.08948 -0.57027,1.06555 -0.81718,3.08648 -0.5487,4.49096 0.65367,3.41945 -7.51963,27.25432 -13.27839,38.72229 -4.17675,8.31754 -4.76589,9.02376 -7.76945,9.31336 -3.47466,0.33502 -4.25346,2.44314 -2.27101,6.14738 0.70326,1.31405 0.58446,2.15668 -0.42181,2.99181 -1.02028,0.84675 -1.25209,2.60341 -0.85335,6.46677 0.53305,5.16476 0.33701,5.68256 -7.45546,19.69245 -11.62472,20.89979 -25.68313,47.58052 -28.25219,53.61838 -3.64643,8.56989 -4.21545,8.34839 20.65307,8.03942 l 21.697,-0.26957 1.82246,-8.4745 z"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/jrbastien/Ubuntu One/CoverArt/0.8 light/Tour_Effel_Light.png"
inkscape:export-xdpi="100"
inkscape:export-ydpi="100" />
</g>
</svg>

Depois

Largura:  |  Altura:  |  Tamanho: 14 KiB

@@ -0,0 +1,373 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="Genre_Quebecois.svg">
<defs
id="defs4">
<filter
inkscape:collect="always"
id="filter3839">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="1.885992"
id="feGaussianBlur3841" />
</filter>
<filter
color-interpolation-filters="sRGB"
inkscape:collect="always"
id="filter3839-4">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="1.885992"
id="feGaussianBlur3841-9" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.53212713"
inkscape:cx="231.89759"
inkscape:cy="589.80239"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1440"
inkscape:window-height="838"
inkscape:window-x="1272"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:#333333;stroke:none;filter:url(#filter3839)"
d="m 158.66886,202.42048 c -1.24282,-1.91859 -3.99614,-4.74624 -6.11849,-6.28367 l -3.85881,-2.79531 2.42335,-2.95395 c 4.57156,-5.57251 10.43395,-19.45395 8.21576,-19.45395 -0.49617,0 -0.90213,0.47833 -0.90213,1.06295 0,0.58462 -1.20409,3.52202 -2.67576,6.52756 -3.34771,6.83692 -10.71552,13.84722 -16.31733,15.52557 -3.7809,1.13278 -4.6427,1.05924 -7.87617,-0.67206 -3.17425,-1.6996 -3.66904,-2.47097 -3.93536,-6.13518 -0.26372,-3.62839 0.0617,-4.43119 2.42372,-5.97883 2.02052,-1.3239 2.83615,-1.46425 3.14391,-0.54099 0.7042,2.1126 3.28859,4.21098 5.18629,4.21098 5.25901,0 12.87594,-5.72708 14.96233,-11.25 l 1.03887,-2.75 -4.77525,0 c -5.23603,0 -7.17525,-1.24133 -7.17525,-4.59301 0,-2.69216 1.70981,-3.40699 8.14917,-3.40699 4.70924,0 4.94713,-0.12472 4.29153,-2.25 -2.16346,-7.01337 -10.02555,-15.96707 -17.26216,-19.65891 -4.35269,-2.22058 -10.13687,-2.71894 -13.10713,-1.12931 -3.83357,2.05166 -4.76949,7.97721 -2.4937,15.78822 0.61968,2.12688 0.37382,2.25 -4.4932,2.25 -2.83181,0 -6.60382,-0.60797 -8.38225,-1.35104 -7.92003,-3.3092 -12.963546,-14.24304 -10.609576,-23.00046 2.312466,-8.60297 14.134186,-15.55932 24.463236,-14.39509 8.93345,1.00693 20.22327,8.99665 26.58443,18.81358 1.75731,2.71199 3.35834,4.76765 3.55785,4.56814 0.69158,-0.69158 -5.11825,-13.16126 -8.62402,-18.5098 -2.9593,-4.51482 -3.58357,-6.44847 -3.90216,-12.08672 -0.5025,-8.89315 1.68676,-13.162576 10.23904,-19.967833 3.56979,-2.84057 7.41267,-6.38104 8.53973,-7.86772 l 2.04921,-2.70305 2.04921,2.70305 c 1.12706,1.48668 4.96994,5.02715 8.53973,7.86772 7.83453,6.234117 10.41106,10.793073 10.41106,18.421503 0,6.62555 -1.41372,10.31711 -6.48774,16.94111 -4.65595,6.0782 -8.05961,12.86293 -8.93796,17.81661 -0.36571,2.0625 -0.47319,3.75 -0.23884,3.75 0.23435,0 1.71724,-2.58058 3.29531,-5.73461 3.50271,-7.00073 14.20441,-17.99154 20.47842,-21.03164 10.08278,-4.88567 21.38081,-3.49588 28.76857,3.53885 7.18183,6.83865 7.52916,18.27754 0.78809,25.95519 -3.92717,4.4728 -8.15884,6.27221 -14.75036,6.27221 -4.86702,0 -5.11288,-0.12312 -4.4932,-2.25 1.5791,-5.41982 1.74838,-8.92375 0.56872,-11.7717 -1.53377,-3.70286 -3.70253,-4.9783 -8.46511,-4.9783 -8.75282,0 -21.75002,11.32269 -24.9666,21.75 -0.6556,2.12528 -0.41771,2.25 4.29152,2.25 6.43937,0 8.14918,0.71483 8.14918,3.40699 0,3.35168 -1.93922,4.59301 -7.17525,4.59301 l -4.77525,0 1.03887,2.75 c 2.08639,5.52292 9.70332,11.25 14.96232,11.25 1.89771,0 4.4821,-2.09838 5.1863,-4.21098 0.30776,-0.92326 1.12338,-0.78291 3.14391,0.54099 2.362,1.54764 2.68744,2.35044 2.42372,5.97883 -0.26632,3.66421 -0.76111,4.43558 -3.93536,6.13518 -3.23347,1.7313 -4.09527,1.80484 -7.87617,0.67206 -5.60181,-1.67835 -12.96962,-8.68865 -16.31733,-15.52557 -1.47167,-3.00554 -2.67576,-5.94294 -2.67576,-6.52756 0,-0.58462 -0.40596,-1.06295 -0.90213,-1.06295 -2.21819,0 3.6442,13.88144 8.21576,19.45395 l 2.42335,2.95395 -3.85881,2.79531 c -2.12235,1.53743 -4.87567,4.36508 -6.11849,6.28367 -1.24283,1.9186 -2.48468,3.48836 -2.75968,3.48836 -0.275,0 -1.51685,-1.56976 -2.75968,-3.48836 z m 3.75968,-42.48688 c 0,-1.65 -0.45,-3 -1,-3 -0.55,0 -1,1.35 -1,3 0,1.65 0.45,3 1,3 0.55,0 1,-1.35 1,-3 z"
id="path3047-4"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/jrbastien/Ubuntu One/CoverArt/1.0 New light/path3047.png"
inkscape:export-xdpi="82.564102"
inkscape:export-ydpi="82.564102" />
<path
style="fill:#ffffff;stroke:none;filter:url(#filter3839-4)"
d="m 158.66886,208.42047 c -1.24282,-1.91859 -3.99614,-4.74624 -6.11849,-6.28367 l -3.85881,-2.79531 2.42335,-2.95395 c 4.57156,-5.57251 10.43395,-19.45394 8.21576,-19.45394 -0.49617,0 -0.90213,0.47833 -0.90213,1.06295 0,0.58462 -1.20409,3.52202 -2.67576,6.52755 -3.34771,6.83692 -10.71552,13.84722 -16.31733,15.52557 -3.7809,1.13278 -4.6427,1.05924 -7.87617,-0.67206 -3.17425,-1.6996 -3.66904,-2.47097 -3.93536,-6.13518 -0.26372,-3.62839 0.0617,-4.43119 2.42372,-5.97883 2.02052,-1.3239 2.83615,-1.46425 3.14391,-0.54099 0.7042,2.1126 3.28859,4.21098 5.18629,4.21098 5.25901,0 12.87594,-5.72708 14.96233,-11.24999 l 1.03887,-2.75 -4.77525,0 c -5.23603,0 -7.17525,-1.24133 -7.17525,-4.59301 0,-2.69216 1.70981,-3.40699 8.14917,-3.40699 4.70924,0 4.94713,-0.12472 4.29153,-2.25 -2.16346,-7.01337 -10.02555,-15.96707 -17.26216,-19.65891 -4.35269,-2.22058 -10.13687,-2.71894 -13.10713,-1.12931 -3.83357,2.05166 -4.76949,7.97721 -2.4937,15.78822 0.61968,2.12688 0.37382,2.25 -4.4932,2.25 -2.83181,0 -6.60382,-0.60797 -8.38225,-1.35104 -7.92003,-3.3092 -12.963546,-14.24304 -10.609576,-23.00046 2.312466,-8.60297 14.134186,-15.55932 24.463236,-14.39509 8.93345,1.00693 20.22327,8.99665 26.58443,18.81358 1.75731,2.71199 3.35834,4.76765 3.55785,4.56814 0.69158,-0.69158 -5.11825,-13.16126 -8.62402,-18.5098 -2.9593,-4.51482 -3.58357,-6.44847 -3.90216,-12.08672 -0.5025,-8.89315 1.68676,-13.16258 10.23904,-19.967836 3.56979,-2.84057 7.41267,-6.38104 8.53973,-7.86772 l 2.04921,-2.70305 2.04921,2.70305 c 1.12706,1.48668 4.96994,5.02715 8.53973,7.86772 7.83453,6.234116 10.41106,10.793076 10.41106,18.421506 0,6.62555 -1.41372,10.31711 -6.48774,16.94111 -4.65595,6.0782 -8.05961,12.86293 -8.93796,17.81661 -0.36571,2.0625 -0.47319,3.75 -0.23884,3.75 0.23435,0 1.71724,-2.58058 3.29531,-5.73461 3.50271,-7.00073 14.20441,-17.99154 20.47842,-21.03164 10.08278,-4.88567 21.38081,-3.49588 28.76857,3.53885 7.18183,6.83865 7.52916,18.27754 0.78809,25.95519 -3.92717,4.4728 -8.15884,6.27221 -14.75036,6.27221 -4.86702,0 -5.11288,-0.12312 -4.4932,-2.25 1.5791,-5.41982 1.74838,-8.92375 0.56872,-11.7717 -1.53377,-3.70286 -3.70253,-4.9783 -8.46511,-4.9783 -8.75282,0 -21.75002,11.32269 -24.9666,21.75 -0.6556,2.12528 -0.41771,2.25 4.29152,2.25 6.43937,0 8.14918,0.71483 8.14918,3.40699 0,3.35168 -1.93922,4.59301 -7.17525,4.59301 l -4.77525,0 1.03887,2.75 c 2.08639,5.52291 9.70332,11.24999 14.96232,11.24999 1.89771,0 4.4821,-2.09838 5.1863,-4.21098 0.30776,-0.92326 1.12338,-0.78291 3.14391,0.54099 2.362,1.54764 2.68744,2.35044 2.42372,5.97883 -0.26632,3.66421 -0.76111,4.43558 -3.93536,6.13518 -3.23347,1.7313 -4.09527,1.80484 -7.87617,0.67206 -5.60181,-1.67835 -12.96962,-8.68865 -16.31733,-15.52557 -1.47167,-3.00553 -2.67576,-5.94293 -2.67576,-6.52755 0,-0.58462 -0.40596,-1.06295 -0.90213,-1.06295 -2.21819,0 3.6442,13.88143 8.21576,19.45394 l 2.42335,2.95395 -3.85881,2.79531 c -2.12235,1.53743 -4.87567,4.36508 -6.11849,6.28367 -1.24283,1.9186 -2.48468,3.48836 -2.75968,3.48836 -0.275,0 -1.51685,-1.56976 -2.75968,-3.48836 z m 3.75968,-42.48687 c 0,-1.65 -0.45,-3 -1,-3 -0.55,0 -1,1.35 -1,3 0,1.65 0.45,3 1,3 0.55,0 1,-1.35 1,-3 z"
id="path3047-4-5"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/jrbastien/Ubuntu One/CoverArt/1.0 New light/path3047.png"
inkscape:export-xdpi="82.564102"
inkscape:export-ydpi="82.564102" />
<path
style="fill:#b3b3b3;stroke:#333333"
d="m 158.66886,205.13477 c -1.24282,-1.91859 -3.99614,-4.74624 -6.11849,-6.28367 l -3.85881,-2.79531 2.42335,-2.95395 c 4.57156,-5.57251 10.43395,-19.45395 8.21576,-19.45395 -0.49617,0 -0.90213,0.47833 -0.90213,1.06295 0,0.58462 -1.20409,3.52202 -2.67576,6.52756 -3.34771,6.83692 -10.71552,13.84722 -16.31733,15.52557 -3.7809,1.13278 -4.6427,1.05924 -7.87617,-0.67206 -3.17425,-1.6996 -3.66904,-2.47097 -3.93536,-6.13518 -0.26372,-3.62839 0.0617,-4.43119 2.42372,-5.97883 2.02052,-1.3239 2.83615,-1.46425 3.14391,-0.54099 0.7042,2.1126 3.28859,4.21098 5.18629,4.21098 5.25901,0 12.87594,-5.72708 14.96233,-11.25 l 1.03887,-2.75 -4.77525,0 c -5.23603,0 -7.17525,-1.24133 -7.17525,-4.59301 0,-2.69216 1.70981,-3.40699 8.14917,-3.40699 4.70924,0 4.94713,-0.12472 4.29153,-2.25 -2.16346,-7.01337 -10.02555,-15.96707 -17.26216,-19.65891 -4.35269,-2.22058 -10.13687,-2.71894 -13.10713,-1.12931 -3.83357,2.05166 -4.76949,7.97721 -2.4937,15.78822 0.61968,2.12688 0.37382,2.25 -4.4932,2.25 -2.83181,0 -6.60382,-0.60797 -8.38225,-1.35104 -7.92003,-3.3092 -12.963546,-14.24304 -10.609576,-23.00046 2.312466,-8.60297 14.134186,-15.55932 24.463236,-14.39509 8.93345,1.00693 20.22327,8.99665 26.58443,18.81358 1.75731,2.71199 3.35834,4.76765 3.55785,4.56814 0.69158,-0.69158 -5.11825,-13.16126 -8.62402,-18.5098 -2.9593,-4.51482 -3.58357,-6.44847 -3.90216,-12.08672 -0.5025,-8.89315 1.68676,-13.16258 10.23904,-19.967836 3.56979,-2.84057 7.41267,-6.38104 8.53973,-7.86772 l 2.04921,-2.70305 2.04921,2.70305 c 1.12706,1.48668 4.96994,5.02715 8.53973,7.86772 7.83453,6.234116 10.41106,10.793076 10.41106,18.421506 0,6.62555 -1.41372,10.31711 -6.48774,16.94111 -4.65595,6.0782 -8.05961,12.86293 -8.93796,17.81661 -0.36571,2.0625 -0.47319,3.75 -0.23884,3.75 0.23435,0 1.71724,-2.58058 3.29531,-5.73461 3.50271,-7.00073 14.20441,-17.99154 20.47842,-21.03164 10.08278,-4.88567 21.38081,-3.49588 28.76857,3.53885 7.18183,6.83865 7.52916,18.27754 0.78809,25.95519 -3.92717,4.4728 -8.15884,6.27221 -14.75036,6.27221 -4.86702,0 -5.11288,-0.12312 -4.4932,-2.25 1.5791,-5.41982 1.74838,-8.92375 0.56872,-11.7717 -1.53377,-3.70286 -3.70253,-4.9783 -8.46511,-4.9783 -8.75282,0 -21.75002,11.32269 -24.9666,21.75 -0.6556,2.12528 -0.41771,2.25 4.29152,2.25 6.43937,0 8.14918,0.71483 8.14918,3.40699 0,3.35168 -1.93922,4.59301 -7.17525,4.59301 l -4.77525,0 1.03887,2.75 c 2.08639,5.52292 9.70332,11.25 14.96232,11.25 1.89771,0 4.4821,-2.09838 5.1863,-4.21098 0.30776,-0.92326 1.12338,-0.78291 3.14391,0.54099 2.362,1.54764 2.68744,2.35044 2.42372,5.97883 -0.26632,3.66421 -0.76111,4.43558 -3.93536,6.13518 -3.23347,1.7313 -4.09527,1.80484 -7.87617,0.67206 -5.60181,-1.67835 -12.96962,-8.68865 -16.31733,-15.52557 -1.47167,-3.00554 -2.67576,-5.94294 -2.67576,-6.52756 0,-0.58462 -0.40596,-1.06295 -0.90213,-1.06295 -2.21819,0 3.6442,13.88144 8.21576,19.45395 l 2.42335,2.95395 -3.85881,2.79531 c -2.12235,1.53743 -4.87567,4.36508 -6.11849,6.28367 -1.24283,1.9186 -2.48468,3.48836 -2.75968,3.48836 -0.275,0 -1.51685,-1.56976 -2.75968,-3.48836 z m 3.75968,-42.48688 c 0,-1.65 -0.45,-3 -1,-3 -0.55,0 -1,1.35 -1,3 0,1.65 0.45,3 1,3 0.55,0 1,-1.35 1,-3 z"
id="path3047"
inkscape:connector-curvature="0"
inkscape:export-xdpi="82.564102"
inkscape:export-ydpi="82.564102" />
<path
style="fill:#000000"
d="m 448.04252,335.21056 c -2.58411,-3.90486 -2.64377,-4.28088 -1.38842,-8.75 2.36519,-8.4202 2.42052,-9.16981 0.67677,-9.16981 -1.00491,0 -1.6228,0.85134 -1.63303,2.25 -0.009,1.2375 -1.03276,4.05 -2.27492,6.25 -1.89789,3.36138 -2.83489,4.04846 -5.86886,4.30353 -2.9275,0.24611 -4.01039,-0.20497 -5.72508,-2.38486 -2.79351,-3.55136 -2.74637,-7.86282 0.10782,-9.86197 3.1443,-2.20235 3.59164,-1.98118 3.42494,1.6933 -0.11897,2.62223 0.26851,3.30928 2.00599,3.5569 1.34948,0.19232 2.7495,-0.54409 3.75,-1.97251 2.11855,-3.02465 2.01917,-3.63527 -0.65344,-4.01484 -1.47682,-0.20974 -2.25,-1.00682 -2.25,-2.31955 0,-1.31273 0.77318,-2.10981 2.25,-2.31955 2.7935,-0.39674 2.91486,-3.12231 0.38652,-8.68045 -2.32594,-5.11319 -4.31878,-6.72462 -7.81866,-6.32226 -3.67436,0.42243 -6.04813,4.92407 -4.44818,8.43558 0.59809,1.31268 1.7759,2.38668 2.61736,2.38668 1.24341,0 1.3115,-0.34972 0.36361,-1.86753 -1.50331,-2.4072 -0.3652,-5.37672 1.89633,-4.94783 2.11197,0.40053 2.84387,4.84283 1.27949,7.76591 -1.4247,2.66207 -6.35601,2.71991 -8.97069,0.10523 -4.18792,-4.18792 -3.47358,-15.24888 1.19603,-18.5196 1.20626,-0.8449 3.69042,-1.53618 5.52036,-1.53618 7.61625,0 11.51476,4.7897 13.54483,16.64112 0.59907,3.49738 1.50025,6.35888 2.00261,6.35888 1.61541,0 -0.39,-13.16311 -3.40998,-22.38242 -1.6003,-4.88534 -2.90523,-9.38534 -2.89986,-10 0.0166,-1.90269 4.68879,-10.96512 7.22385,-14.01186 l 2.4082,-2.89428 4.17909,6.25823 c 5.01866,7.5155 5.27378,10.72719 1.69822,21.37862 -2.70903,8.07006 -4.46866,21.65171 -2.80519,21.65171 0.55063,0 1.28393,-2.70554 1.62956,-6.01231 0.70613,-6.75588 3.52118,-13.07907 6.86562,-15.42161 1.22974,-0.86134 4.12076,-1.56608 6.42449,-1.56608 3.40366,0 4.65039,0.54881 6.6528,2.92853 3.50935,4.17063 4.2565,9.30215 2.01935,13.86925 -3.72668,7.60797 -10.80576,7.34195 -10.80576,-0.40606 0,-2.77121 0.45944,-3.95281 1.62417,-4.17712 2.15621,-0.41524 3.14674,2.41068 1.75591,5.00947 -0.95142,1.77776 -0.86656,1.98472 0.58537,1.42756 0.93397,-0.3584 1.88632,-0.65163 2.11634,-0.65163 0.85544,0 0.38112,-6.20078 -0.61697,-8.06574 -1.43017,-2.6723 -6.08256,-2.50391 -8.72389,0.31574 -4.3537,4.64763 -5.77777,12.75 -2.24093,12.75 1.36314,0 2,0.66667 2,2.09362 0,1.25262 -0.8034,2.30371 -2,2.61663 -2.52507,0.66032 -2.55372,2.47867 -0.0744,4.72242 2.98769,2.70382 5.60373,1.63421 5.42695,-2.21889 -0.17171,-3.7426 0.8241,-4.03713 3.75162,-1.10961 1.60208,1.60208 1.95717,2.83914 1.48812,5.18438 -0.94053,4.70267 -3.40555,6.85902 -7.44184,6.50996 -3.96575,-0.34296 -6.96288,-3.64633 -8.02644,-8.84655 -0.73703,-3.60366 -3.12401,-5.29489 -3.12401,-2.21343 0,1.04785 0.71747,4.01085 1.59438,6.58443 1.56902,4.60484 1.55245,4.75078 -1.04216,9.17814 -1.45009,2.4744 -2.83617,4.4989 -3.08019,4.4989 -0.24401,0 -1.65872,-1.83609 -3.1438,-4.08019 z"
id="path3086" />
<path
style="fill:#000000"
d="m 297.75775,335.13026 c -1.11979,-1.83671 -2.03767,-4.99265 -2.03972,-7.01319 -0.005,-4.61059 2.0777,-13.82632 3.1242,-13.82632 0.43144,0 0.93974,3.2625 1.12955,7.25 l 0.34512,7.25 0.19869,-7.25 c 0.10929,-3.9875 0.60353,-7.25 1.09832,-7.25 0.49479,0 1.64743,3.36125 2.56141,7.46945 1.54629,6.95026 1.54686,7.71166 0.008,10.954 -2.73186,5.75698 -4.07995,6.26384 -6.42584,2.41603 z"
id="path3084" />
<path
style="fill:#000000"
d="m 369.14283,335.63774 c -1.56934,-2.31669 -1.89237,-4.38358 -1.73433,-11.09696 0.20904,-8.88036 -1.04265,-11.08058 -2.14015,-3.76195 -0.37016,2.46843 -1.3534,5.16843 -2.18497,6 -2.27749,2.2775 -5.97236,1.83177 -8.19043,-0.98805 -2.4382,-3.09967 -3.92463,-3.18379 -4.41772,-0.25 -0.36648,2.18047 -0.41995,2.16502 -1.73007,-0.5 -1.06645,-2.16935 -1.10263,-3.29709 -0.17133,-5.34108 1.10298,-2.42077 1.4961,-2.54768 5.98072,-1.93089 3.75544,0.5165 5.08886,0.31231 6.12671,-0.93824 1.12775,-1.35885 0.98017,-1.96716 -0.98467,-4.05863 -2.2283,-2.37192 -2.24716,-2.53101 -0.52549,-4.43344 2.03943,-2.25354 1.55361,-4.43659 -1.33088,-5.98032 -2.75582,-1.4749 -5.12593,1.07003 -5.12593,5.504 0,6.45265 -7.09337,9.98875 -10.4433,5.20606 -1.55766,-2.22387 -2.12165,-5.77749 -0.91694,-5.77749 0.35186,0 1.21973,0.7875 1.92858,1.75 1.20809,1.64038 1.33027,1.64311 1.95051,0.0436 0.36394,-0.93855 0.23948,-3.6861 -0.27657,-6.10568 -1.30712,-6.12868 -0.47791,-10.22412 2.77517,-13.70656 3.18422,-3.40872 7.21342,-3.90877 10.86826,-1.34882 2.75274,1.9281 6.06123,8.74808 6.09233,12.55848 0.0121,1.48004 0.47196,2.9691 1.02196,3.30902 1.75578,1.08513 1.09889,-6.90558 -1.07636,-13.09345 -2.71508,-7.72349 -2.14883,-14.26052 2.22157,-25.64687 1.84514,-4.80718 3.60368,-8.74468 3.90789,-8.75 0.3042,-0.005 2.2167,4.48884 4.25,9.98703 4.23884,11.46215 4.5981,16.75574 1.6969,25.00329 -2.07382,5.89546 -2.68552,13.54171 -1,12.5 0.55,-0.33992 1.00251,-1.37898 1.00558,-2.30902 0.0129,-3.9017 2.37309,-9.72349 5.01177,-12.36218 7.83481,-7.83481 17.79989,1.14831 14.90117,13.43282 -0.61797,2.61889 -0.84306,5.49265 -0.5002,6.38612 0.5655,1.47366 0.80715,1.45819 2.60253,-0.16661 1.90733,-1.72611 1.97915,-1.72098 1.97915,0.14138 0,4.57848 -5.13247,7.9277 -8.6003,5.61218 -1.73792,-1.16045 -3.3997,-4.87412 -3.3997,-7.59752 0,-3.60688 -2.59778,-5.9228 -5.12593,-4.56977 -2.88449,1.54373 -3.37031,3.72678 -1.33088,5.98032 1.71913,1.89962 1.70748,2.05678 -0.3124,4.21276 -1.63664,1.7469 -1.8698,2.64599 -1.06001,4.0874 0.86694,1.54312 1.77337,1.73762 5.49611,1.17936 5.04192,-0.75608 7.33311,0.46373 7.33311,3.90408 0,1.20253 -0.71174,3.05993 -1.58164,4.12755 -1.31039,1.60824 -1.48937,1.64651 -1.04367,0.22316 0.91426,-2.91967 -1.39978,-3.13217 -4.26067,-0.39127 -4.97808,4.7693 -9.31352,2.66374 -10.59377,-5.14501 -1.10118,-6.71656 -2.59247,-3.87259 -1.89643,3.61659 0.51603,5.55228 0.283,7.41241 -1.32004,10.53707 l -1.9467,3.79451 -1.92854,-2.84696 z"
id="path3082" />
<path
style="fill:#000000"
d="m 588.10408,336.11152 c -3.1521,-3.49715 -3.43416,-5.23021 -1.8445,-11.3333 1.67975,-6.44899 1.79296,-7.81453 0.58606,-7.06862 -0.47775,0.29527 -1.17525,2.45427 -1.55,4.79779 -1.53241,9.58316 -9.91102,11.84791 -14.071,3.80341 -1.88939,-3.65369 -2.0597,-5.96937 -0.31035,-4.22002 2.67439,2.67439 10.8,0.13028 10.8,-3.38145 0,-0.80743 -1.2375,-1.94053 -2.75,-2.51801 -2.6256,-1.00245 -2.66442,-1.11279 -0.85819,-2.43918 1.0405,-0.76408 2.36767,-1.54785 2.94927,-1.74172 1.41667,-0.47222 -2.50671,-7.13595 -4.39522,-7.46512 -2.52381,-0.4399 -4.20673,1.41879 -4.88147,5.39132 l -0.65464,3.85416 -3.61515,-3.56028 c -5.66759,-5.58155 -6.16316,-14.1497 -1.25195,-21.64515 2.31782,-3.53744 7.28989,-3.11136 11.14923,0.95543 4.08964,4.30947 5.87984,8.05607 7.42584,15.54108 0.70534,3.41491 1.67272,6.20892 2.14974,6.20892 0.47703,0 0.64559,-0.7875 0.37458,-1.75 -0.271,-0.9625 -0.80625,-3.325 -1.18945,-5.25 -0.38321,-1.925 -2.23412,-7.22887 -4.11315,-11.78637 -4.07464,-9.88286 -3.87149,-12.54118 1.60387,-20.9878 5.81479,-8.97023 6.23932,-9.11456 10.31852,-3.508 7.92107,10.88694 8.30957,13.99595 3.28858,26.31711 -3.18571,7.81752 -5.7629,18.32517 -4.12461,16.81677 0.38727,-0.35657 1.36949,-3.50048 2.18272,-6.98647 3.46919,-14.8711 12.11116,-22.05033 18.43723,-15.31654 1.93435,2.05902 2.45839,3.8362 2.72755,9.25 0.35745,7.18941 -1.52706,11.92878 -5.61265,14.11532 -1.71339,0.91698 -2.02223,0.63738 -2.57998,-2.33569 -1.14749,-6.11668 -6.02791,-7.28168 -8.42891,-2.01206 -1.58501,3.47872 -1.46086,4.20085 0.84824,4.93373 2.72574,0.86512 2.51297,2.35447 -0.5,3.5 -3.097,1.17748 -3.13089,2.05868 -0.18555,4.82569 2.49639,2.34523 5.6969,2.77379 8.70242,1.16528 1.74176,-0.93216 1.80532,-0.77935 0.83433,2.00602 -1.18553,3.40083 -5.23639,7.00301 -7.87527,7.00301 -3.09098,0 -5.85027,-3.34985 -7.41335,-9 -1.29014,-4.66352 -1.45018,-4.89165 -1.05229,-1.5 0.25809,2.2 0.7481,5.83016 1.08891,8.06702 0.51401,3.37365 0.21583,4.53649 -1.749,6.82074 l -2.36865,2.75373 -2.09176,-2.32075 z"
id="path3078" />
<path
style="fill:#000000"
d="m 659.28148,333.57168 -2.71511,-4.10281 1.96073,-6.53561 c 1.0784,-3.59458 1.71168,-6.78465 1.40729,-7.08904 -1.23368,-1.23369 -2.0963,-0.38763 -2.74936,2.69656 -0.97296,4.59495 -4.50274,10.62538 -6.61099,11.29451 -3.33266,1.05775 -6.30842,-1.75793 -8.23769,-7.79451 -0.70437,-2.20394 -2.21381,-2.25628 -3.03918,-0.10538 -0.35989,0.93786 0.1313,2.49906 1.14301,3.6329 1.71984,1.92746 1.70529,1.96686 -0.47589,1.28799 -1.64839,-0.51304 -2.25,-0.29698 -2.25,0.80809 0,0.8296 -0.52023,1.82989 -1.15607,2.22286 -1.98058,1.22407 -0.13307,-6.75177 2.07558,-8.96042 1.94397,-1.94397 2.06456,-1.94697 6.04814,-0.15063 4.05876,1.83025 4.06822,1.82952 6.54849,-0.50057 3.18476,-2.99196 3.17831,-4.98487 -0.0161,-4.98487 -2.02406,0 -2.5,-0.49376 -2.5,-2.59362 0,-1.75262 0.64852,-2.76321 2,-3.11663 1.1,-0.28766 2,-0.92053 2,-1.40638 0,-1.17035 -3.74739,-4.88337 -4.92857,-4.88337 -1.76735,0 -4.07143,3.15295 -4.07143,5.57143 0,3.16381 -3.32412,6.42857 -6.54545,6.42857 -3.53675,0 -7.45455,-4.18058 -7.45455,-7.95455 0,-1.675 0.45,-3.04545 1,-3.04545 0.55,0 1,0.9 1,2 0,1.1 0.64511,2 1.43358,2 0.80829,0 1.57159,1.16584 1.75,2.67288 0.17403,1.47009 0.93143,2.87509 1.68311,3.12223 1.05708,0.34755 1.24111,-0.42787 0.81234,-3.42288 -1.96645,-13.73581 -1.96606,-13.75782 0.2952,-16.62223 5.26969,-6.67531 14.82875,-0.82483 17.97283,11 0.67691,2.54582 1.71605,4.25 2.59148,4.25 2.2984,0 1.76875,-3.79437 -2.0988,-15.03562 -2.30849,-6.70977 -3.34019,-11.2249 -2.93434,-12.84192 0.34425,-1.37161 2.39637,-5.47395 4.56026,-9.11631 2.16389,-3.64237 3.95227,-7.27131 3.97419,-8.06432 0.0219,-0.79301 0.85697,0.13317 1.85568,2.05817 0.99871,1.925 3.23078,6.1332 4.96016,9.35155 1.72937,3.21835 3.14431,6.59797 3.14431,7.51026 0,0.9123 -1.57046,6.1541 -3.48992,11.64846 -3.95712,11.3271 -4.39582,14.80696 -1.80374,14.30777 1.08148,-0.20828 2.0093,-1.7868 2.53366,-4.31058 2.18326,-10.50809 12.30669,-16.96077 17.87435,-11.39311 1.84944,1.84944 2.02925,2.82043 1.43515,7.75 -1.09936,9.12197 -1.11119,12.0777 -0.0495,12.36502 0.55,0.14885 1.14322,-1.22148 1.31827,-3.04516 0.26383,-2.74874 0.56311,-3.11258 1.75,-2.12755 1.18041,0.97965 1.43173,0.72789 1.43173,-1.43422 0,-1.44234 0.45,-2.62244 1,-2.62244 0.55,0 1,1.37045 1,3.04545 0,3.77397 -3.9178,7.95455 -7.45455,7.95455 -3.07081,0 -6.54545,-3.21998 -6.54545,-6.06574 0,-2.90888 -1.8023,-5.93426 -3.53518,-5.93426 -1.5948,0 -5.46482,3.45825 -5.46482,4.88337 0,0.48585 0.9,1.11872 2,1.40638 1.35148,0.35342 2,1.36401 2,3.11663 0,2.09986 -0.47594,2.59362 -2.5,2.59362 -3.13748,0 -3.21365,1.98093 -0.18555,4.82569 2.77561,2.60754 2.94485,2.61963 7.35785,0.52552 3.30705,-1.56929 3.569,-1.55436 5.43314,0.30978 2.19116,2.19117 4.01798,10.15135 2.05063,8.93547 -0.63584,-0.39297 -1.15607,-1.39326 -1.15607,-2.22286 0,-1.10507 -0.60161,-1.32113 -2.25,-0.80809 -2.21842,0.69046 -2.22373,0.67087 -0.37872,-1.39536 1.02921,-1.15261 1.60583,-2.78742 1.28139,-3.6329 -0.94095,-2.45207 -2.36788,-1.79295 -3.56734,1.64782 -1.41385,4.05576 -3.90438,6.81493 -6.15142,6.81493 -2.60334,0 -6.49075,-5.18298 -7.80647,-10.40816 -0.63593,-2.52551 -1.59976,-4.59184 -2.14184,-4.59184 -1.4811,0 -1.18648,4.27497 0.58716,8.5199 1.82547,4.36896 1.39354,6.83848 -1.91028,10.92196 l -2.38017,2.94185 -2.71512,-4.10281 z"
id="path3076" />
<path
style="fill:#000000"
d="m 283.93108,325.73005 c -2.68937,-1.88372 -5.21679,-6.51724 -5.21679,-9.56395 l 0,-2.22398 2.31445,2.17432 c 2.88524,2.71054 5.29579,2.77128 6.91762,0.17431 1.66599,-2.66767 3.7371,-2.5981 3.03105,0.10182 -0.3023,1.156 -1.94767,2.95432 -3.65638,3.99626 -2.72662,1.66265 -2.84032,1.8949 -0.92932,1.89818 2.26372,0.004 6.32258,-4.25304 6.32258,-6.63112 0,-0.75083 0.48032,-1.36514 1.06739,-1.36514 0.58706,0 0.84777,0.7875 0.57934,1.75 -0.26842,0.9625 -0.80046,2.875 -1.18232,4.25 -0.79724,2.87073 -4.28719,7 -5.91622,7 -0.60675,0 -2.10588,-0.70232 -3.3314,-1.5607 z"
id="path3074" />
<path
style="fill:#000000"
d="m 310.49778,325.54075 c -2.51655,-2.88502 -3.78824,-5.95385 -3.70227,-8.93426 l 0.0812,-2.81574 1.16878,2.60441 c 1.81875,4.05276 3.72371,5.89916 6.04123,5.85555 1.9296,-0.0363 1.87808,-0.17549 -0.55378,-1.49595 -2.63744,-1.43209 -4.59103,-5.02498 -3.38697,-6.22904 0.33787,-0.33787 1.30447,0.37105 2.14801,1.57536 1.95648,2.79327 4.15239,2.77499 7.24774,-0.0603 l 2.45635,-2.25 -0.60717,4 c -0.40957,2.69822 -1.59994,4.89497 -3.65768,6.75 -3.54913,3.19951 -5.12669,3.41754 -7.23546,1 z"
id="path3072" />
<path
style="fill:#000000"
d="m 286.46038,311.04075 c -0.23132,-0.6875 -0.21633,-2.0375 0.0333,-3 0.40366,-1.5564 1.92794,-1.75 13.77823,-1.75 7.88299,0 13.579,0.41203 13.94791,1.00895 0.34296,0.55492 0.3392,1.90492 -0.008,3 -0.58938,1.85707 -1.53016,1.99105 -13.98118,1.99105 -9.78845,0 -13.46147,-0.33343 -13.76986,-1.25 z"
id="path3070" />
<path
style="fill:#000000"
d="m 308.71429,309.29075 c 1.02799,-0.66434 -1.42932,-0.97419 -7.80902,-0.98469 -5.95501,-0.01 -9.08634,0.34499 -8.69098,0.98469 0.79182,1.28119 14.5175,1.28119 16.5,0 z"
id="path3068" />
<path
style="fill:#000000"
d="m 277.42155,306.53307 c -5.29714,-5.89259 -1.52406,-19.24232 5.43852,-19.24232 2.34399,0 7.80642,4.7708 9.05826,7.91133 1.68762,4.23382 2.64296,9.08867 1.78847,9.08867 -0.43827,0 -1.25495,-1.31406 -1.81483,-2.92014 -1.15149,-3.30318 -4.56561,-7.07986 -6.40019,-7.07986 -0.67238,0 -0.28439,0.65708 0.86219,1.46017 2.26971,1.58977 5.01109,7.22239 4.00485,8.22863 -0.34357,0.34357 -1.73265,-0.57086 -3.08685,-2.03206 -1.3542,-1.46121 -2.84766,-2.65674 -3.3188,-2.65674 -2.06342,0 -4.15087,2.76337 -4.43291,5.8683 -0.30142,3.31817 -0.33265,3.33862 -2.09871,1.37402 z"
id="path3066" />
<path
style="fill:#000000"
d="m 321.24089,306.49177 c 0.69387,-2.49537 -1.75333,-7.20102 -3.74493,-7.20102 -0.64473,0 -2.42966,1.20469 -3.96652,2.6771 -1.53686,1.4724 -3.03657,2.43481 -3.3327,2.13869 -0.85366,-0.85367 1.24258,-5.54082 3.47762,-7.77586 1.12196,-1.12196 1.66837,-2.03993 1.21424,-2.03993 -1.19103,0 -4.67227,4.009 -6.51268,7.5 -1.57739,2.99209 -1.58166,2.99331 -1.62159,0.46189 -0.0557,-3.52907 3.92732,-10.95701 7.12291,-13.2836 8.0805,-5.88313 16.34253,9.26507 9.28073,17.01595 -2.3968,2.63067 -2.51644,2.6623 -1.91708,0.5068 z"
id="path3064" />
<path
style="fill:#000000"
d="m 297.23844,299.54075 c -0.38661,-2.6125 -2.08064,-8.24849 -3.76451,-12.52443 -2.72891,-6.92968 -2.93882,-8.18416 -1.93182,-11.54522 1.10355,-3.68333 7.49871,-12.18035 9.16736,-12.18035 0.46164,0 2.71365,2.80681 5.00448,6.23735 4.79188,7.17589 4.8449,8.7817 0.66909,20.26265 -1.40031,3.85 -2.81903,8.6875 -3.1527,10.75 -1.23207,7.6157 -2.46669,3.37941 -2.76865,-9.5 l -0.31066,-13.25 -0.21837,13.25 c -0.22118,13.42066 -1.41067,17.17339 -2.69422,8.5 z"
id="path3062" />
<path
style="fill:#000000"
d="m 660.89088,245.49153 c -0.64712,-2.91457 -1.17659,-8.65207 -1.17659,-12.75 0,-6.44661 -0.23585,-7.44466 -1.75,-7.40539 -1.10111,0.0286 -2.67699,1.97524 -4.25,5.25 -1.40668,2.9285 -3.57867,5.72947 -4.96651,6.40477 -1.35658,0.66008 -2.57866,1.08801 -2.71572,0.95094 -0.13707,-0.13707 0.86036,-2.4409 2.21651,-5.11963 2.98861,-5.90326 3.08929,-7.53147 0.46572,-7.53147 -1.46667,0 -2,-0.66667 -2,-2.5 0,-2 0.5,-2.5 2.5,-2.5 2.7383,0 2.96809,-0.63862 1.56126,-4.33887 -2.25025,-5.91861 -9.01269,-8.61724 -12.30669,-4.91113 -0.85547,0.9625 -2.25225,3.4375 -3.10396,5.5 -1.90425,4.61135 -3.11594,4.79729 -3.25821,0.5 -0.0592,-1.7875 -0.28418,-6.05053 -0.5,-9.47339 -0.36092,-5.72411 -0.16995,-6.44615 2.38033,-9 3.77938,-3.78467 10.33443,-3.91165 14.27905,-0.27661 3.51752,3.24146 7.25737,11.23904 8.06649,17.25 0.44326,3.29295 1.10369,4.75 2.15302,4.75 2.2209,0 0.96171,-10.03742 -2.41041,-19.21418 -1.4725,-4.0072 -2.87405,-9.41628 -3.11454,-12.02019 -0.38221,-4.1383 0.0784,-5.6287 3.6582,-11.83784 2.2525,-3.90691 4.09546,-7.48739 4.09546,-7.95662 0,-0.46922 0.41191,-1.10771 0.91536,-1.41886 0.50345,-0.31115 2.97845,3.21906 5.5,7.84491 5.52568,10.137 5.72681,13.09034 1.65189,24.25591 -3.58104,9.81228 -5.06849,20.34687 -2.87292,20.34687 0.9871,0 1.81538,-1.95042 2.58444,-6.08582 2.02031,-10.86368 8.58924,-18.90105 15.45545,-18.91044 1.60179,-0.002 4.06476,1.18509 5.94675,2.86665 2.58885,2.31313 3.20651,3.62358 3.18153,6.75 -0.0573,7.17065 -0.85708,15.37865 -1.49856,15.37913 -0.34983,2.7e-4 -1.41975,-1.68702 -2.3776,-3.74952 -2.49009,-5.36181 -4.42818,-7.25451 -7.39133,-7.2182 -3.48162,0.0427 -5.76615,1.93688 -7.5844,6.28857 -1.95185,4.67143 -1.95011,4.67963 0.98939,4.67963 2,0 2.5,0.5 2.5,2.5 0,1.83333 -0.53333,2.5 -2,2.5 -2.62357,0 -2.52289,1.62821 0.46572,7.53147 1.35615,2.67873 2.35358,4.98256 2.21651,5.11963 -0.13706,0.13707 -1.35914,-0.29086 -2.71572,-0.95094 -1.38784,-0.6753 -3.55983,-3.47627 -4.96651,-6.40477 -1.57301,-3.27476 -3.14889,-5.22143 -4.25,-5.25 -1.53112,-0.0397 -1.75961,1.04901 -1.82685,8.70461 -0.0423,4.8125 -0.6378,10.55 -1.32341,12.75 l -1.24655,4 -1.1766,-5.29922 z"
id="path3060" />
<path
style="fill:#000000"
d="m 587.37421,244.97342 c -3.50524,-5.98123 -4.0391,-8.83882 -2.3896,-12.79064 2.38252,-5.70798 3.04885,-10.89203 1.39999,-10.89203 -0.78481,0 -1.69458,1.37136 -2.03329,3.0649 -1.30748,6.53741 -8.50028,10.6842 -13.70276,7.89992 -2.88262,-1.54274 -2.3399,-2.96482 1.13148,-2.96482 4.13496,0 6.61075,-1.47372 8.44959,-5.02964 1.50751,-2.91521 1.48914,-2.97036 -0.98965,-2.97036 -1.85901,0 -2.52568,-0.52791 -2.52568,-2 0,-1.46667 0.66667,-2 2.5,-2 2.92346,0 2.93123,-0.0322 1.07878,-4.4657 -1.59213,-3.81049 -6.6311,-6.96986 -9.609,-6.02471 -1.06164,0.33695 -2.78816,2.55207 -3.8367,4.92248 -3.23633,7.31624 -4.13649,6.63078 -4.10128,-3.12305 0.0353,-9.7628 0.86687,-11.68053 5.81296,-13.40475 7.15984,-2.49593 14.8435,5.79599 15.9394,17.2012 0.42972,4.47226 1.40636,6.0654 2.54071,4.14453 0.95429,-1.61597 -1.4263,-14.10251 -3.92017,-20.56184 -3.14378,-8.14267 -2.93829,-9.69141 2.66389,-20.0775 l 4.19688,-7.78073 4.58035,8.33504 c 5.16474,9.39846 5.54434,12.63221 2.34436,19.97114 -2.31077,5.29961 -4.94333,19.44408 -3.82673,20.56067 1.08528,1.08528 2.60349,-1.76336 2.62124,-4.91827 0.0195,-3.47547 4.14874,-12.77869 6.62093,-14.91722 4.56188,-3.94618 13.36232,-2.68838 14.89108,2.1283 0.86509,2.72566 0.19812,17.92285 -0.76099,17.33954 -0.42036,-0.25566 -1.65933,-2.21925 -2.75327,-4.36354 -2.647,-5.18856 -5.11212,-6.46701 -8.79624,-4.56188 -3.55667,1.83923 -4.84335,3.38208 -5.71421,6.85188 -0.61922,2.46717 -0.4253,2.74441 1.9196,2.74441 1.94174,0 2.60841,0.51117 2.60841,2 0,1.44592 -0.66667,2 -2.40638,2 -1.92361,0 -2.30019,0.40608 -1.87706,2.02412 0.8168,3.12347 5.14767,5.97588 9.07329,5.97588 3.45678,0 3.48756,0.036 1.71015,2 -3.41395,3.77237 -10.38623,1.86686 -12.95181,-3.53969 -0.8515,-1.79441 -1.54819,-3.98206 -1.54819,-4.86144 0,-0.87938 -0.675,-1.59887 -1.5,-1.59887 -1.97452,0 -1.89374,1.55803 0.47499,9.16091 l 1.975,6.33909 -2.92246,6.25 c -1.60735,3.4375 -3.12833,6.25 -3.37997,6.25 -0.25163,0 -1.59607,-1.9428 -2.98764,-4.31733 z"
id="path3058" />
<path
style="fill:#000000"
d="m 447.11158,243.09214 c -1.27055,-2.45698 -1.52185,-4.77559 -1.08386,-10 l 0.57021,-6.80139 -5.31938,0 c -6.03869,0 -8.75405,-1.86146 -6.56426,-4.5 0.68469,-0.825 2.08979,-1.5 3.12244,-1.5 4.36462,0 1.09533,-8.22656 -3.75285,-9.44338 -4.45886,-1.1191 -8.2061,1.12249 -9.42143,5.63589 -0.88414,3.28345 -1.30994,3.75213 -2.50661,2.75898 -1.99084,-1.65225 -1.86936,-9.89569 0.18914,-12.83462 1.80955,-2.58349 7.06228,-5.11687 10.60935,-5.11687 3.39163,0 9.47464,5.44546 10.34295,9.25893 0.72395,3.17945 1.16808,6.00707 1.33368,8.49107 0.0458,0.6875 0.5986,1.25 1.22837,1.25 1.89732,0 0.27754,-12.72829 -2.57989,-20.27283 -4.09139,-10.80259 -3.27977,-14.98931 4.87898,-25.1683 1.54014,-1.92151 1.5716,-1.92151 3.11174,0 8.18624,10.21329 8.89626,13.96048 4.83495,25.5168 -2.47518,7.04303 -3.98984,19.92433 -2.34282,19.92433 0.40606,0 1.22578,-2.8125 1.82158,-6.25 1.88526,-10.87705 9.81082,-15.68124 17.8166,-10.79977 1.7524,1.06851 3.71701,2.93462 4.3658,4.1469 1.65049,3.08397 1.37397,9.90549 -0.47654,11.756 -1.39424,1.39424 -1.60369,1.25671 -2.13783,-1.40373 -0.88883,-4.42714 -2.83664,-6.47762 -6.75773,-7.11392 -2.90377,-0.47122 -3.99125,-0.11501 -6.10641,2.00014 -3.09468,3.09468 -3.5096,7.66438 -0.69591,7.66438 1.03265,0 2.43775,0.675 3.12244,1.5 2.18979,2.63854 -0.52557,4.5 -6.56426,4.5 l -5.31938,0 0.57021,6.80139 c 0.43799,5.22441 0.18669,7.54302 -1.08386,10 -0.90974,1.75923 -2.08096,3.19861 -2.60271,3.19861 -0.52175,0 -1.69297,-1.43938 -2.60271,-3.19861 z"
id="path3056" />
<path
style="fill:#000000"
d="m 296.66953,243.04074 c -2.1471,-2.27591 -2.23296,-5.26148 -0.33663,-11.70685 0.67642,-2.29907 0.53527,-2.2532 -2.19789,0.71431 -4.01372,4.35787 -7.43636,5.016 -10.74234,2.06563 -1.43169,-1.2777 -2.87956,-3.50381 -3.21747,-4.94692 -0.58538,-2.49993 -0.43007,-2.61899 3.2891,-2.52147 2.30427,0.0604 5.09275,-0.67688 6.80632,-1.79965 l 2.90282,-1.902 -2.64356,-0.88119 c -3.72316,-1.24105 -4.16313,-3.35333 -1.18621,-5.69497 l 2.53837,-1.99668 -2.49073,-0.62514 c -4.28789,-1.07619 -10.67702,3.28211 -10.67702,7.28325 0,3.19394 -1.88602,2.51367 -4.75,-1.7133 -7.30142,-10.7762 5.50982,-27.48497 15.63571,-20.39252 1.28189,0.89788 3.21412,3.48538 4.29384,5.75 l 1.96312,4.1175 -0.59529,-3.5 c -0.32741,-1.925 -1.93451,-6.36302 -3.57134,-9.86227 -1.63682,-3.49925 -2.97604,-7.0091 -2.97604,-7.79966 0,-3.58276 8.7474,-15.33807 11.41341,-15.33807 1.29289,0 7.31919,6.04766 9.93729,9.97251 2.34439,3.51453 2.05831,6.65959 -1.23996,13.63144 -1.58822,3.35718 -3.18501,7.63218 -3.54841,9.5 -0.65321,3.35736 -0.63862,3.34914 1.28025,-0.72145 2.37933,-5.04736 5.53467,-7.3825 9.97556,-7.3825 6.84846,0 12.18186,6.73593 12.18186,15.38535 0,3.63348 -0.59471,5.24869 -2.87023,7.79544 l -2.87022,3.21234 -0.74471,-2.44657 c -1.62858,-5.35029 -7.18441,-8.82099 -11.78372,-7.36122 -2.01381,0.63916 -1.99012,0.72306 0.58505,2.07233 3.36324,1.76219 3.43908,4.04109 0.18383,5.52428 -1.375,0.6265 -2.5,1.53981 -2.5,2.02959 0,1.49765 7.13596,3.97115 9.64202,3.34217 2.81555,-0.70666 3.11305,1.08547 0.89289,5.37878 -2.65972,5.14334 -9.82622,4.54511 -14.09725,-1.17678 -1.45471,-1.94887 -1.58397,-1.98366 -0.95023,-0.25571 0.40344,1.1 0.9614,3.8599 1.23992,6.13312 0.40501,3.30554 0.0825,4.55699 -1.61047,6.25 -2.7073,2.7073 -4.51364,2.67372 -7.16164,-0.13312 z"
id="path3054" />
<path
style="fill:#000000"
d="m 301.644,236.48172 c -0.0387,-2.58004 -0.41412,-5.56992 -0.83436,-6.64417 -0.64702,-1.65396 -0.86605,-1.44331 -1.42971,1.375 -0.84431,4.22156 -0.85774,9.55275 -0.0262,10.38434 1.41853,1.41853 2.35673,-0.67692 2.29023,-5.11517 z"
id="path3052" />
<path
style="fill:#000000"
d="m 291.88555,230.63575 c 2.71439,-2.45649 2.21497,-3.42788 -0.92126,-1.79187 -1.5125,0.789 -3.56406,1.43731 -4.55902,1.4407 -0.99496,0.003 -1.5309,0.45616 -1.19098,1.00616 0.95494,1.54513 4.63993,1.18334 6.67126,-0.65499 z"
id="path3050" />
<path
style="fill:#000000"
d="m 315.21429,231.29074 c 0.33992,-0.55 -0.19602,-1.00277 -1.19098,-1.00616 -0.99496,-0.003 -3.04652,-0.6517 -4.55902,-1.4407 -3.28919,-1.7158 -3.67942,-0.31754 -0.52749,1.89016 2.53483,1.77546 5.36892,2.0268 6.27749,0.5567 z"
id="path3048" />
<path
style="fill:#000000"
d="m 309.14953,219.39552 c 0.87191,-1.41078 -2.07427,-2.10478 -8.93524,-2.10478 -7.30433,0 -10.42835,0.79614 -8.33385,2.12385 1.78571,1.13196 16.56781,1.11564 17.26909,-0.0191 z"
id="path3046" />
<path
style="fill:#000000"
d="m 323.68063,208.68874 c -1.47123,-4.45785 -5.8444,-6.68507 -10.16728,-5.17811 -1.5627,0.54476 -3.26769,1.78724 -3.78887,2.76107 -0.83794,1.5657 -0.34438,1.82856 4.26494,2.27145 4.33019,0.41608 5.63675,1.03044 7.7187,3.62939 2.95028,3.68293 3.82682,2.13481 1.97251,-3.4838 z"
id="path3044" />
<path
style="fill:#000000"
d="m 278.36686,212.75375 c 0.86593,-2.2566 5.72713,-4.46301 9.83295,-4.46301 3.20864,0 3.47843,-0.19885 2.57077,-1.89482 -1.27636,-2.3849 -5.93799,-4.06511 -8.56147,-3.08585 -2.90819,1.08555 -5.17534,4.02477 -5.96298,7.73067 -0.71566,3.36721 0.96501,4.72477 2.12073,1.71301 z"
id="path3042" />
<path
style="fill:#000000"
d="m 304.55138,194.37878 c 2.88294,-5.53103 2.72847,-7.9938 -0.76054,-12.12581 -3.43147,-4.06387 -5.33375,-3.56176 -8.51843,2.24845 l -1.94187,3.54281 2.8371,6.32191 c 1.56041,3.47706 3.07924,8.37002 3.37518,10.87326 l 0.53808,4.55134 1.15379,-5.63117 c 0.63458,-3.09714 2.1271,-7.4985 3.31669,-9.78079 z"
id="path3040" />
<path
style="fill:#000000"
d="m 516.21037,241.28641 c -1.63435,-1.80594 -1.72217,-2.95171 -0.88718,-11.57564 1.08111,-11.16596 -0.24334,-19.79171 -4.25429,-27.70694 -3.59948,-7.10323 -3.37591,-12.42949 0.81206,-19.34583 1.83333,-3.02771 4.11153,-6.21029 5.06267,-7.07239 1.58356,-1.43532 1.90212,-1.36629 3.77876,0.81884 4.00269,4.66066 7.97327,12.43246 7.98252,15.62458 0.005,1.78106 -1.52402,6.95606 -3.39817,11.5 -4.18973,10.15812 -5.17435,17.38316 -3.62264,26.5827 1.01541,6.02003 0.95696,7.41994 -0.42104,10.08471 -1.87714,3.62998 -2.61079,3.78824 -5.05269,1.08997 z"
id="path3036" />
<path
style="fill:#000000"
d="m 502.80828,234.54074 c -1.61566,-1.5125 -3.22671,-3.75819 -3.58012,-4.99042 -0.6387,-2.22694 -0.62429,-2.22995 2.39604,-0.5 2.99539,1.71567 3.07621,1.70285 5.68372,-0.90168 3.14168,-3.1381 3.53209,-9.21019 0.85489,-13.29612 -2.86894,-4.37856 -7.44852,-2.7565 -7.44852,2.63822 0,3.42443 -0.93715,3.51436 -4.28548,0.41123 -6.85923,-6.35692 -4.52596,-21.61123 3.30561,-21.61123 5.6523,0 11.32516,7.63857 13.95118,18.78541 2.00468,8.50937 -0.37241,18.75334 -4.91592,21.18495 -2.61735,1.40076 -2.63189,1.39656 -5.9614,-1.72036 z"
id="path3030" />
<path
style="fill:#000000"
d="m 527.54534,236.24783 c -3.20749,-1.85604 -5.16686,-9.33389 -4.55735,-17.39294 0.63544,-8.40201 3.02837,-14.58643 7.47319,-19.31415 5.70867,-6.072 12.48368,-3.56373 13.78333,5.10293 0.84762,5.65235 -0.82705,10.9488 -4.33412,13.70746 l -2.65913,2.09167 -0.76449,-2.95027 c -1.61718,-6.24093 -5.63987,-6.64475 -8.40613,-0.84385 -2.18715,4.5865 -1.16258,10.07424 2.30507,12.34634 2.36484,1.5495 2.80537,1.55863 5.32881,0.11037 2.54876,-1.46279 2.71812,-1.45364 2.30543,0.12451 -0.67517,2.58183 -4.55351,6.81944 -6.80566,7.43608 -1.1,0.30118 -2.75103,0.11301 -3.66895,-0.41815 z"
id="path3028" />
<g
id="g3217">
<path
id="path3038"
d="m 373.27099,241.06823 c -1.84628,-2.63593 -1.95033,-5.32861 -0.43799,-11.33467 1.61864,-6.4282 4.3072,-6.11508 6.34255,0.73868 1.96801,6.62701 1.94689,7.0299 -0.53384,10.18364 -2.52614,3.21146 -3.36503,3.27587 -5.37072,0.41235 z"
style="fill:#000000" />
<path
id="path3034"
d="m 361.46427,236.21063 c -1.52802,-0.88999 -4.74998,-5.87251 -4.74998,-7.34548 0,-0.2887 1.18444,-0.0746 2.63208,0.47581 3.15303,1.19878 5.48207,0.58612 7.89102,-2.07575 0.98278,-1.08596 2.5693,-1.97447 3.52559,-1.97447 1.52353,0 1.62459,0.52599 0.81659,4.25 -1.39084,6.41021 -5.69191,9.24628 -10.1153,6.66989 z"
style="fill:#000000" />
<path
id="path3032"
d="m 383.50366,236.23263 c -1.5524,-0.905 -3.76993,-6.58507 -3.78379,-9.69193 -0.009,-2.05468 2.73324,-1.42027 5.40351,1.25 2.70807,2.70807 4.64221,3.0781 7.65665,1.46482 2.8803,-1.54149 2.52031,1.18525 -0.65463,4.95845 -2.64398,3.14219 -5.53385,3.81881 -8.62174,2.01866 z"
style="fill:#000000" />
<path
id="path3026"
d="m 368.78843,223.41349 c -3.24581,-0.90704 -3.69702,-2.72315 -0.86503,-3.48176 3.75717,-1.00645 13.45518,-0.72293 16.47579,0.48166 l 2.8151,1.12264 -3,1.33225 c -3.15862,1.4027 -11.32454,1.69131 -15.42586,0.54521 z"
style="fill:#000000" />
<path
id="path3024"
d="m 355.01357,218.04075 c -3.23464,-3.55134 -4.29928,-6.2843 -4.29928,-11.03638 0,-5.50199 3.43432,-11.08028 7.08696,-11.51119 5.3124,-0.62673 11.97142,8.33556 13.43248,18.07861 0.62854,4.19146 0.49934,4.71825 -1.1559,4.71264 -1.02495,-0.003 -2.63011,-1.35348 -3.56703,-3 -2.8379,-4.98728 -7.79651,-3.11706 -7.79651,2.94058 0,2.673 -1.15044,2.61572 -3.70072,-0.18426 z"
style="fill:#000000" />
<path
id="path3022"
d="m 392.71429,217.49075 c 0,-5.59592 -4.97984,-7.12598 -7.16029,-2.2 -0.90934,2.05433 -2.03904,3 -3.58382,3 -2.77389,0 -2.79911,-1.0306 -0.22884,-9.35214 4.66972,-15.11881 15.54237,-18.38805 18.95875,-5.70061 1.40323,5.2112 -0.15236,11.37584 -3.70032,14.66398 -3.34833,3.10313 -4.28548,3.0132 -4.28548,-0.41123 z"
style="fill:#000000" />
<path
id="path3020"
d="m 373.29635,217.11089 c -0.22494,-0.72608 -0.66545,-2.8015 -0.97891,-4.61205 -0.31345,-1.81056 -1.92739,-6.68206 -3.58653,-10.82556 -1.65914,-4.14351 -3.0124,-8.73715 -3.00724,-10.20809 0.009,-2.61552 3.96102,-10.21858 7.87383,-15.1487 1.84172,-2.32055 2.07717,-2.37142 3.79025,-0.81883 1.0046,0.91048 3.31662,4.11628 5.13784,7.12399 4.15721,6.86561 4.33517,11.32146 0.74626,18.6863 -1.46947,3.01554 -3.24206,7.9578 -3.93908,10.9828 -1.01385,4.40006 -1.70329,5.56402 -3.44736,5.82014 -1.19903,0.17607 -2.36411,-0.27393 -2.58906,-1 z"
style="fill:#000000" />
</g>
<path
style="fill:#000000"
d="m 472.42807,161.65144 c -4.29218,-6.5176 -4.61264,-9.69845 -1.82774,-18.14224 1.03732,-3.14515 2.19581,-7.8954 2.57442,-10.55611 0.61402,-4.31513 0.49415,-4.80025 -1.10984,-4.49135 -1.24066,0.23893 -2.02181,1.65606 -2.51934,4.57045 -1.67587,9.81689 -6.0246,14.09861 -11.39833,11.22267 -2.53132,-1.35472 -6.43295,-8.29002 -6.43295,-11.43482 0,-1.66653 0.16435,-1.69591 1.75,-0.31278 4.02038,3.50689 6.07538,3.95811 8.22708,1.80641 2.91731,-2.91731 2.67131,-6.02292 -0.47708,-6.02292 -2.11111,0 -2.5,-0.46667 -2.5,-3 0,-2.53333 0.38889,-3 2.5,-3 1.58824,0 2.5,-0.56364 2.5,-1.54546 0,-1.9654 -4.43946,-6.45454 -6.38312,-6.45454 -2.70269,0 -4.81986,3.13564 -4.42144,6.5484 0.43568,3.73192 -1.15437,3.68942 -3.63545,-0.0972 -3.68975,-5.63128 -0.79027,-17.02227 5.00499,-19.66277 5.77829,-2.632771 13.70308,6.33567 15.15346,17.14906 0.42715,3.18459 0.96892,4.0625 2.50707,4.0625 1.78657,0 1.90143,-0.46982 1.28348,-5.25 -0.37327,-2.8875 -2.07006,-9.075 -3.77065,-13.75 -1.70058,-4.675001 -3.1234,-10.565641 -3.16182,-13.090311 -0.071,-4.66288 1.47523,-7.64415 7.992,-15.40969 l 2.51758,-3 3.50149,4 c 7.79243,8.90183 9.01238,14.60356 5.05497,23.62557 -4.248,9.684491 -6.245,22.130631 -3.63204,22.636441 1.09797,0.21254 1.99112,-1.54002 3.27024,-6.41696 1.95437,-7.45149 5.57568,-12.95977 9.50138,-14.45232 9.53391,-3.624791 16.32607,14.26944 8.20571,21.61827 -1.88379,1.70481 -1.93307,1.65194 -1.79843,-1.92945 0.26597,-7.07507 -6.429,-8.88651 -9.67123,-2.61672 -1.98213,3.83302 -1.97732,3.8495 1.23181,4.21875 2.18229,0.2511 2.75,0.83252 2.75,2.81642 0,1.9839 -0.56771,2.56532 -2.75,2.81642 -3.06486,0.35265 -3.26944,0.94943 -1.58948,4.63653 1.6558,3.63408 4.86591,3.741 8.58948,0.28608 l 2.25,-2.08766 0,2.72398 c 0,3.49599 -2.37156,8.07106 -5.21679,10.06395 -5.45161,3.81845 -9.94702,0.22953 -11.9679,-9.55459 -0.73082,-3.53828 -1.73379,-5.88471 -2.51539,-5.88471 -2.12209,0 -1.54527,7.11585 1.28914,15.90337 2.2004,6.82187 2.42096,8.53625 1.46923,11.42 -1.07823,3.26708 -6.882,11.67663 -8.05851,11.67663 -0.31472,0 -2.24342,-2.53769 -4.286,-5.63931 z"
id="path3018" />
<path
style="fill:#000000"
d="m 297.65022,157.87605 c -2.2006,-4.14817 -2.25612,-4.72375 -1.07435,-11.13745 1.01644,-5.51645 1.0058,-6.91568 -0.0577,-7.59022 -2.09414,-1.32824 -2.26904,-1.11667 -3.55604,4.30126 -2.44208,10.28061 -7.5788,11.78157 -14.12212,4.12653 -2.21645,-2.59301 -3.83105,-4.91343 -3.588,-5.15647 0.24304,-0.24304 2.2683,0.3817 4.50057,1.38832 3.96071,1.78603 4.10326,1.78377 5.90569,-0.0935 3.20534,-3.3385 3.41052,-5.94119 0.57039,-7.23524 -3.00177,-1.3677 -3.32056,-4.66808 -0.61415,-6.35826 2.14593,-1.34016 1.79515,-3.22663 -1.22354,-6.58024 -2.31284,-2.56944 -5.86253,-2.93592 -7.64413,-0.78922 -0.66679,0.80343 -1.45799,3.39093 -1.75823,5.75 -0.68255,5.36303 -1.70964,7.40829 -4.02425,8.01357 -1.53525,0.40148 -1.75335,-0.0391 -1.42245,-2.87349 0.21495,-1.84121 -0.33333,-5.15749 -1.2184,-7.3695 -0.88507,-2.21201 -1.60922,-5.85773 -1.60922,-8.1016 0,-3.30639 0.64451,-4.72427 3.4,-7.47976 7.46217,-7.46217 17.46811,-2.56624 21.28275,10.41371 1.37379,4.67458 2.26574,6.18386 3.65515,6.1849 1.60567,0.001 1.7493,-0.53562 1.13671,-4.24861 -0.38566,-2.3375 -1.82368,-7.82895 -3.19561,-12.20322 -3.22539,-10.28389 -2.79188,-14.437901 2.80234,-26.852911 l 4.41866,-9.80612 4.41578,9.30612 c 5.69234,11.99644 6.23101,17.404191 2.82479,28.358611 -3.54723,11.40788 -4.05189,15.44973 -1.9277,15.43897 1.18837,-0.006 2.25078,-1.81601 3.59376,-6.12254 1.23521,-3.96097 3.20554,-7.47943 5.59516,-9.99145 3.20598,-3.37018 4.26481,-3.87455 8.09337,-3.85522 9.43959,0.0477 13.20279,7.87737 9.29638,19.34206 -0.88466,2.59632 -1.39706,5.82649 -1.13868,7.17814 0.30478,1.59432 0.0115,2.45756 -0.83483,2.45756 -2.00513,0 -4.41803,-4.09611 -4.41803,-7.5 0,-6.75824 -5.58111,-9.7037 -9.82569,-5.18555 -2.75235,2.92975 -2.73687,4.45582 0.0757,7.45971 l 2.25,2.40307 -2.88476,2.3064 c -2.31157,1.84813 -2.75629,2.81827 -2.23819,4.88252 0.99254,3.95458 3.93874,5.05236 8.17033,3.04434 1.93827,-0.91977 3.71148,-1.48496 3.94047,-1.25598 0.22898,0.22899 -1.39712,2.5379 -3.61357,5.13091 -3.10664,3.63447 -4.70274,4.71458 -6.96678,4.71458 -3.45447,0 -6.39793,-4.02441 -7.43558,-10.1662 -0.66026,-3.90802 -1.12144,-4.36766 -3.07883,-3.06849 -0.79603,0.52835 -0.82076,2.62419 -0.088,7.46232 0.88025,5.81233 0.76913,7.28145 -0.81675,10.79831 -1.00959,2.23887 -2.14977,4.26484 -2.53373,4.50214 -0.38397,0.2373 -1.74235,-1.53695 -3.01864,-3.94278 z"
id="path3016" />
<path
style="fill:#000000"
d="m 388.25173,159.32983 c -3.62395,-3.78259 -4.48377,-10.81352 -2.15736,-17.64115 2.0869,-6.12471 2.05789,-6.39793 -0.67919,-6.39793 -1.71354,0 -2.6169,0.87883 -3.54684,3.45051 -3.6298,10.03804 -16.37653,11.47636 -21.23606,2.39624 -0.96238,-1.79822 -0.86828,-1.89051 0.94057,-0.92244 2.83535,1.51743 7.739,1.33516 11.12023,-0.41334 3.53714,-1.82913 4.94561,-5.41415 2.6351,-6.70717 -2.18858,-1.22479 -2.02984,-2.53701 0.4002,-3.30827 1.9317,-0.6131 1.94539,-0.75554 0.33473,-3.48218 -3.31849,-5.61774 -12.03695,-6.88526 -16.82667,-2.4463 l -2.76989,2.56705 0.67169,-3.81705 c 1.89727,-10.7818 9.05502,-17.6342 16.01919,-15.33581 5.90372,1.9484 9.35388,7.35593 11.01797,17.26876 0.49333,2.93874 1.08243,3.75 2.72309,3.75 1.9431,0 2.05572,-0.41328 1.56687,-5.75 -0.28969,-3.1625 -2.15219,-9.90281 -4.13889,-14.97847 -1.9867,-5.07566 -3.61218,-10.741281 -3.61218,-12.590281 0,-3.60626 4.47114,-13.34338 8.30001,-18.07553 l 2.34181,-2.89428 4.16592,6.23851 c 7.22727,10.82292 7.54674,15.31323 2.01367,28.303221 -1.86711,4.38342 -3.28601,9.71637 -3.59101,13.49683 -0.46452,5.7579 -0.34621,6.25 1.50258,6.25 1.64486,0 2.21144,-1.03708 3.14138,-5.75 2.80814,-14.23161 12.59932,-20.02942 20.64909,-12.22727 3.27212,3.17146 6.52993,10.70781 6.44475,14.90877 -0.0287,1.41518 -0.15088,1.41341 -1.25,-0.0182 -4.42019,-5.75714 -15.59203,-4.87518 -18.47796,1.45873 -0.99615,2.18631 -0.88894,2.60465 0.80531,3.14239 2.37519,0.75385 2.51189,2.0742 0.34056,3.28933 -2.31051,1.29303 -0.90204,4.87805 2.6351,6.70717 3.39603,1.75616 7.31613,1.91405 10.13018,0.40802 3.46945,-1.8568 3.26681,-0.13718 -0.41806,3.54768 -6.26578,6.26579 -14.24686,4.32754 -18.78155,-4.56119 -1.3032,-2.55448 -2.67724,-3.90547 -3.9721,-3.90547 -2.37288,0 -2.39595,0.30996 -0.4905,6.59107 2.58616,8.52504 1.59138,14.31109 -3.20706,18.65362 -1.84221,1.66717 -2.0407,1.61673 -4.74468,-1.20561 z"
id="path3014" />
<path
style="fill:#000000"
d="m 648.71928,156.18512 c -1.91535,-3.03634 -2.07749,-4.31729 -1.53716,-12.14437 0.51527,-7.46427 0.37834,-8.75 -0.9319,-8.75 -0.91246,0 -1.53593,0.88665 -1.53593,2.18428 0,3.47554 -3.79112,10.36597 -6.50145,11.8165 -3.68767,1.97358 -6.84916,0.0955 -9.11557,-5.415 -1.03564,-2.51806 -2.2346,-4.57997 -2.66436,-4.58203 -1.52702,-0.007 -2.01484,3.29227 -0.77926,5.27076 1.12682,1.80432 1.0408,1.95356 -0.84218,1.46115 -1.57067,-0.41074 -2.09718,-0.0846 -2.09718,1.29894 0,1.01605 -0.45,2.12548 -1,2.4654 -0.56789,0.35098 -1,-0.49597 -1,-1.96004 0,-2.24937 -0.31818,-2.49821 -2.49556,-1.95172 -2.16848,0.54425 -1.90025,0.0225 2.04656,-3.98095 2.49817,-2.53401 5.01871,-4.60729 5.6012,-4.60729 0.5825,0 2.39607,1.125 4.03017,2.5 3.6798,3.09634 7.21239,3.20219 9.26093,0.27749 2.28645,-3.26437 1.90822,-5.49988 -1.18107,-6.98054 -3.69591,-1.7714 -4.07412,-4.46991 -0.83343,-5.94646 1.41416,-0.64434 2.5712,-1.67416 2.5712,-2.2885 0,-2.10103 -3.11126,-5.56199 -5,-5.56199 -2.39402,0 -5,3.66606 -5,7.03395 0,3.60784 -3.41892,6.96605 -7.09201,6.96605 -1.65775,0 -4.66968,-1.125 -6.69318,-2.5 -2.02349,-1.375 -4.50118,-2.5 -5.50596,-2.5 -1.00479,0 -1.54877,-0.45 -1.20885,-1 0.33992,-0.55 1.49148,-1 2.55902,-1 1.3912,0 1.94098,-0.69141 1.94098,-2.44098 0,-3.18614 1.76205,-4.25667 2.17899,-1.32385 0.20366,1.43258 0.96147,2.21436 2.06227,2.12748 1.35677,-0.10708 1.60753,0.39164 1.13785,2.26298 -0.63105,2.5143 1.20878,5.7471 2.7342,4.80434 1.2188,-0.75326 1.11126,-4.94838 -0.2427,-9.46749 -2.40513,-8.02762 2.42516,-16.96248 9.17011,-16.96248 4.96054,0 10.36163,5.97036 11.9921,13.25604 1.03201,4.61153 1.67357,5.74396 3.25412,5.74396 1.74425,0 1.89405,-0.46669 1.31404,-4.09387 -0.36005,-2.25164 -1.76756,-6.73808 -3.12781,-9.96987 -1.39598,-3.31672 -2.47317,-7.83914 -2.47317,-10.383301 0,-4.57306 2.92597,-13.5672 7.03202,-21.61572 l 2.25947,-4.42893 3.22805,7.09844 c 6.60969,14.53458 7.1973,20.115231 3.14822,29.899181 -1.36706,3.30326 -2.80163,7.69076 -3.18795,9.75 -0.62963,3.35623 -0.48566,3.74407 1.38985,3.74407 1.47313,0 2.26635,-0.81374 2.68064,-2.75 2.19968,-10.28051 6.9235,-16.27686 12.77643,-16.2182 7.88732,0.079 11.47247,8.21249 8.50711,19.29965 -1.43828,5.37755 -0.59135,8.54212 1.76315,6.58806 0.96728,-0.80278 1.41816,-2.36291 1.17052,-4.05022 -0.35092,-2.39093 -0.16865,-2.61916 1.41484,-1.7717 1.5294,0.81851 1.81765,0.58009 1.81765,-1.50339 0,-2.86574 1.94122,-4.6097 2.11848,-1.90322 0.0652,0.99496 0.0454,2.37152 -0.0438,3.05902 -0.0893,0.6875 0.73085,1.25 1.8225,1.25 1.09165,0 2.26293,0.45 2.60285,1 0.33992,0.55 -0.20406,1 -1.20885,1 -1.00478,0 -3.48247,1.125 -5.50596,2.5 -4.30569,2.92579 -8.81868,3.22546 -11.07231,0.73523 -0.87841,-0.97063 -2.04003,-3.7361 -2.58138,-6.14549 -1.59527,-7.1 -5.90347,-8.25381 -8.9818,-2.40548 -1.51302,2.8745 -1.48479,2.99485 0.84525,3.60417 3.10581,0.81218 3.69124,3.73751 1.15843,5.78845 -1.19908,0.97096 -2.68383,1.93327 -3.29946,2.13848 -1.37522,0.45841 -0.31944,4.17626 1.78302,6.27872 2.17571,2.17572 4.79133,1.85788 8.18069,-0.99408 1.6341,-1.375 3.48541,-2.5 4.11403,-2.5 0.62862,0 3.16574,2.08129 5.63803,4.6251 3.19105,3.28335 3.90548,4.47092 2.4627,4.09362 -1.50158,-0.39266 -2.03242,-0.049 -2.03242,1.31588 0,2.87356 -1.87067,3.32888 -2.55036,0.62075 -0.47631,-1.89774 -1.08189,-2.30304 -2.77932,-1.86008 -2.09015,0.54543 -2.10383,0.49978 -0.37037,-1.23584 1.95023,-1.95266 1.92268,-5.05943 -0.0449,-5.05943 -0.66964,0 -2.23848,2.02452 -3.48631,4.49893 -1.51335,3.00093 -3.21811,4.82512 -5.11992,5.47861 -2.54265,0.87368 -3.16604,0.62725 -5.7615,-2.27758 -1.6007,-1.79149 -3.44852,-5.26936 -4.10627,-7.72861 -0.70881,-2.65032 -1.77765,-4.47135 -2.62438,-4.47135 -1.15261,0 -1.27015,0.91743 -0.60859,4.75 1.35636,7.85771 0.98677,12.50647 -1.28355,16.14437 l -2.11834,3.39437 -2.1412,-3.39437 z"
id="path3012" />
<path
style="fill:#000000"
d="m 660.82626,157.54075 c -0.79087,-1.40166 -1.23867,-1.50119 -2.24969,-0.5 -2.13582,2.11504 -2.80892,1.39537 -1.69188,-1.80896 0.5865,-1.68243 0.76716,-4.93003 0.40148,-7.21689 l -0.66488,-4.15792 2.32652,3.45232 c 1.27958,1.89877 3.438,4.02134 4.7965,4.71682 l 2.46998,1.2645 -2.24041,0.61419 c -1.77372,0.48625 -2.11567,1.11117 -1.64159,3.00006 0.70205,2.79718 -0.0963,3.13428 -1.50603,0.63588 z"
id="path3010" />
<path
style="fill:#000000"
d="m 639.42454,156.29075 c 0.36373,-1.39092 0.0307,-2 -1.09362,-2 -2.3745,0 -1.96884,-1.51849 0.73476,-2.75033 1.29326,-0.58926 3.34056,-3.02399 4.54954,-5.41052 l 2.19815,-4.33915 -0.73626,4 c -0.93085,5.05715 -0.9285,6.30774 0.0188,9.97285 0.74904,2.89805 0.71927,2.92841 -1.18306,1.20682 -1.71483,-1.5519 -2.02323,-1.57891 -2.5436,-0.22284 -0.87281,2.27444 -2.55558,1.87914 -1.94471,-0.45683 z"
id="path3008" />
<path
style="fill:#000000"
d="m 559.33389,152.29805 c -1.36096,-3.40136 -1.60986,-5.78322 -1.08074,-10.34215 0.49271,-4.24518 0.37211,-5.72816 -0.42571,-5.23508 -0.61223,0.37838 -1.11315,1.28882 -1.11315,2.02319 0,2.11942 -5.60257,7.54674 -7.79043,7.54674 -1.0934,0 -3.05035,-0.88503 -4.34879,-1.96673 l -2.36078,-1.96673 2.31574,-0.0333 c 3.11832,-0.0448 5.90334,-1.63985 7.24702,-4.15054 0.90882,-1.69815 0.71442,-2.49101 -1.07878,-4.39979 l -2.18738,-2.32836 2.1017,-1.79953 c 1.15593,-0.98974 2.10002,-2.71202 2.09796,-3.82729 -0.01,-5.30251 -7.74635,-12.37663 -12.04391,-11.01264 -2.53059,0.80318 -3.49325,4.81583 -2.18107,9.0913 1.20581,3.9289 0.5328,4.20721 -3.1564,1.30528 -3.74589,-2.94651 -5.61488,-7.07208 -5.61488,-12.39416 0,-9.10194 4.25301,-14.790271 11.05829,-14.790271 7.84838,0 13.00716,6.931041 15.3955,20.684461 0.73687,4.24334 1.61121,7.9866 1.94299,8.31838 1.39708,1.39708 0.42618,-7.62798 -1.94067,-18.03965 -4.71604,-20.745631 -4.15173,-29.870761 2.32573,-37.607851 l 2.86102,-3.4174 2.79571,3.66536 c 6.38236,8.36771 6.84993,14.88375 2.55331,35.582971 -3.12581,15.05877 -3.69319,20.63793 -1.99188,19.58646 0.55,-0.33992 1.00534,-2.05398 1.01187,-3.80902 0.016,-4.30623 2.22262,-12.29036 4.77801,-17.28823 5.05289,-9.882491 15.99745,-10.516041 20.8125,-1.20477 1.67788,3.24466 1.83876,12.07486 0.27543,15.11776 -1.41068,2.74581 -5.97323,7.68426 -7.09934,7.68426 -0.46605,0 -0.55944,-2.17044 -0.20754,-4.8232 0.65011,-4.90076 -0.91279,-8.18251 -3.89236,-8.17306 -2.55594,0.008 -7.42529,3.94274 -9.00861,7.27933 -2.1768,4.58727 -2.10667,5.89853 0.43174,8.07198 2.1014,1.79928 2.10142,1.79985 0.10725,3.97224 -3.30503,3.60042 -1.05459,7.33372 5.20376,8.63262 l 3.08729,0.64075 -2.74271,1.80991 c -3.6265,2.3931 -7.40378,1.39839 -9.5744,-2.52134 -3.79073,-6.84534 -4.21981,-6.83231 -3.64856,0.11077 0.41513,5.04556 0.12293,7.38681 -1.30588,10.4632 l -1.84068,3.96321 -1.76817,-4.41911 z"
id="path3006" />
<path
style="fill:#000000"
d="m 639.30715,107.25171 c -2.80055,-4.23192 -7.86815,-7.950871 -10.84286,-7.957221 -2.66872,-0.006 -2.08483,-1.70289 0.83825,-2.43653 2.09086,-0.52478 2.36325,-0.92073 1.41742,-2.06037 -0.64395,-0.77592 -1.01774,-2.20565 -0.83064,-3.17717 0.30604,-1.58913 0.54862,-1.54267 2.41708,0.46289 l 2.0769,2.22929 1.37964,-2.76092 c 1.34281,-2.68724 1.39463,-2.58743 1.94144,3.73907 0.30899,3.575 1.53732,8.637501 2.72963,11.250001 1.1923,2.6125 2.02792,4.75 1.85694,4.75 -0.17099,0 -1.5137,-1.81757 -2.9838,-4.03904 z"
id="path3004" />
<path
style="fill:#000000"
d="m 659.73653,110.00091 c -0.0122,-0.43441 0.96219,-2.68441 2.16539,-5 1.4775,-2.84349 2.20788,-6.092081 2.25,-10.007601 0.0614,-5.70604 0.0903,-5.76302 1.83303,-3.61371 1.71029,2.1093 1.83833,2.12249 3.75613,0.38691 2.49397,-2.25702 3.52367,-1.37283 1.91083,1.64079 -0.77266,1.44374 -0.85488,2.41103 -0.22283,2.62171 0.55088,0.18363 1.8529,0.97833 2.8934,1.766 1.80807,1.36874 1.78352,1.43354 -0.55444,1.46394 -2.29392,0.0298 -10.02724,4.571731 -10.07599,5.917801 -0.0348,0.96003 -3.93055,5.71133 -3.95552,4.82416 z"
id="path3000" />
<g
id="g3200"
transform="matrix(1.585999,0,0,1.585999,169.37726,-81.031037)"
style="fill:#ff0000">
<path
id="path3036-1"
d="m 142.07892,371.19992 c -1.63435,-1.80594 -1.72217,-2.95171 -0.88718,-11.57564 1.08111,-11.16596 -0.24334,-19.79171 -4.25429,-27.70694 -3.59948,-7.10323 -3.37591,-12.42949 0.81206,-19.34583 1.83333,-3.02771 4.11153,-6.21029 5.06267,-7.07239 1.58356,-1.43532 1.90212,-1.36629 3.77876,0.81884 4.00269,4.66066 7.97327,12.43246 7.98252,15.62458 0.005,1.78106 -1.52402,6.95606 -3.39817,11.5 -4.18973,10.15812 -5.17435,17.38316 -3.62264,26.5827 1.01541,6.02003 0.95696,7.41994 -0.42104,10.08471 -1.87714,3.62998 -2.61079,3.78824 -5.05269,1.08997 z"
style="fill:#ff0000"
inkscape:connector-curvature="0" />
<path
id="path3030-6"
d="m 128.67683,364.45425 c -1.61566,-1.5125 -3.22671,-3.75819 -3.58012,-4.99042 -0.6387,-2.22694 -0.62429,-2.22995 2.39604,-0.5 2.99539,1.71567 3.07621,1.70285 5.68372,-0.90168 3.14168,-3.1381 3.53209,-9.21019 0.85489,-13.29612 -2.86894,-4.37856 -7.44852,-2.7565 -7.44852,2.63822 0,3.42443 -0.93715,3.51436 -4.28548,0.41123 -6.85923,-6.35692 -4.52596,-21.61123 3.30561,-21.61123 5.6523,0 11.32516,7.63857 13.95118,18.78541 2.00468,8.50937 -0.37241,18.75334 -4.91592,21.18495 -2.61735,1.40076 -2.63189,1.39656 -5.9614,-1.72036 z"
style="fill:#ff0000"
inkscape:connector-curvature="0" />
<path
id="path3028-2"
d="m 153.41389,366.16134 c -3.20749,-1.85604 -5.16686,-9.33389 -4.55735,-17.39294 0.63544,-8.40201 3.02837,-14.58643 7.47319,-19.31415 5.70867,-6.072 12.48368,-3.56373 13.78333,5.10293 0.84762,5.65235 -0.82705,10.9488 -4.33412,13.70746 l -2.65913,2.09167 -0.76449,-2.95027 c -1.61718,-6.24093 -5.63987,-6.64475 -8.40613,-0.84385 -2.18715,4.5865 -1.16258,10.07424 2.30507,12.34634 2.36484,1.5495 2.80537,1.55863 5.32881,0.11037 2.54876,-1.46279 2.71812,-1.45364 2.30543,0.12451 -0.67517,2.58183 -4.55351,6.81944 -6.80566,7.43608 -1.1,0.30118 -2.75103,0.11301 -3.66895,-0.41815 z"
style="fill:#ff0000"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(1.7069309,0,0,1.7069309,-128.89437,105.97217)"
id="g3217-6">
<path
inkscape:connector-curvature="0"
id="path3038-5"
d="m 373.27099,241.06823 c -1.84628,-2.63593 -1.95033,-5.32861 -0.43799,-11.33467 1.61864,-6.4282 4.3072,-6.11508 6.34255,0.73868 1.96801,6.62701 1.94689,7.0299 -0.53384,10.18364 -2.52614,3.21146 -3.36503,3.27587 -5.37072,0.41235 z"
style="fill:#000000" />
<path
inkscape:connector-curvature="0"
id="path3034-7"
d="m 361.46427,236.21063 c -1.52802,-0.88999 -4.74998,-5.87251 -4.74998,-7.34548 0,-0.2887 1.18444,-0.0746 2.63208,0.47581 3.15303,1.19878 5.48207,0.58612 7.89102,-2.07575 0.98278,-1.08596 2.5693,-1.97447 3.52559,-1.97447 1.52353,0 1.62459,0.52599 0.81659,4.25 -1.39084,6.41021 -5.69191,9.24628 -10.1153,6.66989 z"
style="fill:#000000" />
<path
inkscape:connector-curvature="0"
id="path3032-7"
d="m 383.50366,236.23263 c -1.5524,-0.905 -3.76993,-6.58507 -3.78379,-9.69193 -0.009,-2.05468 2.73324,-1.42027 5.40351,1.25 2.70807,2.70807 4.64221,3.0781 7.65665,1.46482 2.8803,-1.54149 2.52031,1.18525 -0.65463,4.95845 -2.64398,3.14219 -5.53385,3.81881 -8.62174,2.01866 z"
style="fill:#000000" />
<path
inkscape:connector-curvature="0"
id="path3026-1"
d="m 368.78843,223.41349 c -3.24581,-0.90704 -3.69702,-2.72315 -0.86503,-3.48176 3.75717,-1.00645 13.45518,-0.72293 16.47579,0.48166 l 2.8151,1.12264 -3,1.33225 c -3.15862,1.4027 -11.32454,1.69131 -15.42586,0.54521 z"
style="fill:#000000" />
<path
inkscape:connector-curvature="0"
id="path3024-7"
d="m 355.01357,218.04075 c -3.23464,-3.55134 -4.29928,-6.2843 -4.29928,-11.03638 0,-5.50199 3.43432,-11.08028 7.08696,-11.51119 5.3124,-0.62673 11.97142,8.33556 13.43248,18.07861 0.62854,4.19146 0.49934,4.71825 -1.1559,4.71264 -1.02495,-0.003 -2.63011,-1.35348 -3.56703,-3 -2.8379,-4.98728 -7.79651,-3.11706 -7.79651,2.94058 0,2.673 -1.15044,2.61572 -3.70072,-0.18426 z"
style="fill:#000000" />
<path
inkscape:connector-curvature="0"
id="path3022-6"
d="m 392.71429,217.49075 c 0,-5.59592 -4.97984,-7.12598 -7.16029,-2.2 -0.90934,2.05433 -2.03904,3 -3.58382,3 -2.77389,0 -2.79911,-1.0306 -0.22884,-9.35214 4.66972,-15.11881 15.54237,-18.38805 18.95875,-5.70061 1.40323,5.2112 -0.15236,11.37584 -3.70032,14.66398 -3.34833,3.10313 -4.28548,3.0132 -4.28548,-0.41123 z"
style="fill:#000000" />
<path
inkscape:connector-curvature="0"
id="path3020-5"
d="m 373.29635,217.11089 c -0.22494,-0.72608 -0.66545,-2.8015 -0.97891,-4.61205 -0.31345,-1.81056 -1.92739,-6.68206 -3.58653,-10.82556 -1.65914,-4.14351 -3.0124,-8.73715 -3.00724,-10.20809 0.009,-2.61552 3.96102,-10.21858 7.87383,-15.1487 1.84172,-2.32055 2.07717,-2.37142 3.79025,-0.81883 1.0046,0.91048 3.31662,4.11628 5.13784,7.12399 4.15721,6.86561 4.33517,11.32146 0.74626,18.6863 -1.46947,3.01554 -3.24206,7.9578 -3.93908,10.9828 -1.01385,4.40006 -1.70329,5.56402 -3.44736,5.82014 -1.19903,0.17607 -2.36411,-0.27393 -2.58906,-1 z"
style="fill:#000000" />
</g>
<path
style="color:#000000;fill:#2c2c2c;fill-opacity:1;fill-rule:nonzero;stroke:#2c2c2c;stroke-width:1.43718708;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 164.28043,528.44679 c -3.91612,-2.91534 -4.55691,-4.61021 -3.66768,-9.70101 0.59261,-3.39268 1.74495,-7.30762 2.56075,-8.69988 0.81582,-1.39226 -1.76374,-0.26048 -5.73237,2.51508 -4.44433,3.10819 -9.2105,5.0464 -12.40946,5.0464 -6.05029,0 -11.12011,-5.77601 -11.12011,-10.50782 0,-2.65046 0.50293,-3.05427 5.46109,-2.54533 3.24121,0.33269 6.80418,-0.72174 10.55758,-2.07919 l 6.82434,-2.46809 -5.52502,-2.02458 c -6.59267,-2.4158 -7.44963,-7.59864 -1.43824,-8.69808 5.71044,-1.04437 3.02541,-3.29502 -3.93096,-3.29502 -7.03817,0 -16.5956,4.97729 -16.5956,8.64261 0,4.32095 -3.46167,4.60324 -7.91323,0.64531 -8.48024,-7.53984 -7.27811,-19.35286 2.8238,-27.74914 10.85074,-9.01867 25.20095,-6.98906 33.21905,4.6983 4.50002,6.55933 4.86658,6.78795 3.63235,2.26542 -0.75504,-2.76658 -4.14961,-9.50256 -7.54349,-14.96883 -5.42212,-8.73301 -5.90194,-10.56366 -3.95534,-15.09047 1.21843,-2.83348 6.22367,-8.33268 11.12278,-12.22046 l 8.90741,-7.06866 8.10915,6.11516 c 14.93895,11.26555 16.39726,16.32278 8.37454,29.04186 -2.67964,4.24824 -5.49934,9.98763 -6.26604,12.75422 l -1.39394,5.03015 3.98865,-5.91762 c 6.92288,-10.27081 16.43155,-12.93174 28.23004,-7.89996 8.26862,3.52637 13.19281,11.03619 13.19281,20.12015 0,6.08962 -1.01591,8.08645 -6.10466,11.99889 -4.75053,2.71939 -5.1898,4.42271 -6.16486,0.67264 -0.076,-5.07712 -8.9853,-11.06957 -16.4576,-11.06957 -7.75112,0 -8.82255,1.35629 -3.55123,4.49539 l 4.53631,2.70138 -5.7648,3.34561 c -5.30719,3.08004 -5.49049,3.50484 -2.30913,5.35138 1.90062,1.10318 7.57865,1.6872 12.59062,2.03842 6.62376,0.46417 6.74133,0.74306 5.56002,4.89787 -3.32368,11.68985 -17.9597,11.91636 -27.41762,0.88354 -2.27674,-2.65585 -2.36707,-2.60123 -1.18808,0.71859 3.94061,11.09626 3.74213,14.43963 -1.0699,18.02192 -5.86621,4.3671 -6.31005,4.36723 -12.17191,0.003 z"
id="path3080"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssssssscssssssssssscssccsssccsscssssssscc"
inkscape:export-filename="/home/jrbastien/Ubuntu One/CoverArt/1.1 dark/genre_québécois.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
inkscape:connector-curvature="0"
style="fill:#2c2c2c;fill-opacity:1;stroke:#2c2c2c;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 633.92274,511.61622 c -1.70784,-4.26831 -2.02018,-7.25725 -1.3562,-12.97816 0.61829,-5.32719 0.46695,-7.18816 -0.53422,-6.5694 -0.76827,0.47482 -1.39686,1.61731 -1.39686,2.53887 0,2.65961 -7.03056,9.47025 -9.77606,9.47025 -1.37209,0 -3.82783,-1.11061 -5.45721,-2.46801 l -2.96249,-2.46801 2.90597,-0.0418 c 3.91311,-0.0562 7.40798,-2.05782 9.09414,-5.20844 1.14046,-2.13097 0.89651,-3.12591 -1.35374,-5.5212 l -2.7449,-2.92181 2.63739,-2.2582 c 1.45055,-1.242 2.63527,-3.40326 2.63268,-4.80279 -0.0125,-6.65402 -9.72074,-15.53118 -15.11366,-13.81954 -3.17559,1.00789 -4.38361,6.04329 -2.73699,11.40848 1.51315,4.93031 0.6686,5.27955 -3.9609,1.63798 -4.70064,-3.69752 -7.046,-8.87462 -7.046,-15.55319 0,-11.42184 5.33702,-18.56003 13.87683,-18.56003 9.84878,0 16.32242,8.69764 19.3195,25.95654 0.92469,5.32488 2.02189,10.02222 2.43822,10.43856 1.75318,1.75318 0.53481,-9.5722 -2.4353,-22.6376 -5.91807,-26.03329 -5.20993,-37.48423 2.91851,-47.19335 l 3.59024,-4.28843 3.50828,4.59959 c 8.0091,10.50047 8.59584,18.67732 3.2041,44.65238 -3.92252,18.89695 -4.63452,25.89812 -2.49957,24.57866 0.69018,-0.42657 1.26158,-2.5775 1.26977,-4.77987 0.02,-5.40381 2.78912,-15.42293 5.99584,-21.69466 6.34076,-12.40135 20.07488,-13.19638 26.11719,-1.51184 2.10554,4.07166 2.30742,15.15251 0.34563,18.97098 -1.77023,3.44566 -7.49569,9.64283 -8.90882,9.64283 -0.58484,0 -0.70203,-2.72365 -0.26044,-6.05254 0.81581,-6.14987 -1.14544,-10.26808 -4.88444,-10.25622 -3.2074,0.01 -9.31785,4.94767 -11.30473,9.13469 -2.73163,5.75647 -2.64362,7.40195 0.54178,10.12937 2.637,2.25788 2.63703,2.2586 0.13459,4.98469 -4.14743,4.51809 -1.32339,9.20293 6.53009,10.8329 l 3.87418,0.80407 -3.44177,2.27122 c -4.55082,3.00304 -9.29086,1.75481 -12.01473,-3.16399 -4.75691,-8.59007 -5.29535,-8.57373 -4.5785,0.13901 0.52093,6.33157 0.15426,9.26956 -1.63873,13.13006 l -2.30983,4.97336 -2.21884,-5.54546 z"
id="path3006-2" />
<rect
style="color:#000000;fill:none;stroke:none;stroke-width:2.36100698;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect3198"
width="125.63898"
height="125.63899"
x="97.537636"
y="288.19638"
inkscape:export-filename="/home/jrbastien/Ubuntu One/CoverArt/1.1 dark/genre_québécois.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<g
transform="matrix(0.36596452,0,0,0.36596452,59.415002,137.45052)"
id="layer1-9"
style="color:#000000;fill:#2c2c2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.25158429;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
inkscape:export-filename="/home/jrbastien/Ubuntu One/CoverArt/1.1 dark/genre_québécois.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
d="m 144.15625,0.03125 c -0.13825,0.31354858 -36.40104,50.955423 -59,117.46875 41.30895,13.57365 48.17533,45.24368 51.28125,71.375 C 108.69377,86.75113 -18.822472,131.23717 2.375,208.75 7.218782,226.46225 40.75964,260.03271 78.65625,250.3125 119.07933,239.94428 105.98568,182.7267 72.25,190 50.415851,194.70736 49.77397,224.97331 71,230.40625 c -17.53122,4.74162 -34.12577,-34.17765 -3.15625,-49.375 21.344524,-10.47419 54.09634,2.35206 59.84375,35.75 l 16.46875,0 0.0312,0 16.46875,0 c 5.74741,-33.39794 38.49924,-46.22419 59.84375,-35.75 30.96952,15.19735 14.37499,54.11662 -3.15625,49.375 21.22603,-5.43294 20.58417,-35.69889 -1.25,-40.40625 -33.73568,-7.2733 -46.82932,49.94428 -6.40625,60.3125 37.89661,9.72021 71.43748,-23.85025 76.28125,-41.5625 21.19748,-77.51283 -106.31876,-121.99887 -134.0625,-19.875 3.10592,-26.13132 9.97229,-57.80135 51.28125,-71.375 -22.59896,-66.513324 -58.86175,-117.15520141 -59,-117.46875 -1.1e-4,-2.4516e-4 -0.0312,1.8357e-4 -0.0312,0 z m -16.625,243.28125 c -2.76917,13.76764 -10.5875,25.83587 -26.90625,34.46875 32.90955,1.68807 43.38373,38.1235 43.53125,38.8125 l 0,0.0937 c 0,-8.6e-4 0.0307,-0.0598 0.0312,-0.0625 2.2e-4,-0.001 -3.2e-4,-0.0298 0,-0.0312 0.14752,-0.689 10.62171,-37.12443 43.53125,-38.8125 C 171.4,269.14837 163.58168,257.08014 160.8125,243.3125 l -16.625,0 -0.0312,0 -16.625,0 z"
transform="translate(147.05822,286.5625)"
id="path3178"
style="color:#000000;fill:#2c2c2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.25158429;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
width="62.578949"
height="20.152544"
rx="10.076272"
ry="10.076272"
x="260.21054"
y="506.52594"
id="rect2392"
style="color:#000000;fill:#2c2c2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.25158429;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
</g>
</svg>

Depois

Largura:  |  Altura:  |  Tamanho: 67 KiB

@@ -0,0 +1,213 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
width="500"
height="500"
xml:space="preserve"
sodipodi:docname="Thumb_down.svg"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs6"><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2515"
id="linearGradient6232"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.98766169,0,0,1.0896755,-34.224531,-15.222843)"
x1="257.69382"
y1="97.573967"
x2="360.56195"
y2="97.573967" /><linearGradient
id="linearGradient2515"
y2="45.016998"
gradientUnits="userSpaceOnUse"
x2="20.759001"
y1="1"
x1="20.759001"><stop
id="stop3311"
style="stop-color:#f6f6f6"
offset="0" /><stop
id="stop3313"
style="stop-color:#d2d2d2"
offset="1" /></linearGradient><linearGradient
y2="97.573967"
x2="360.56195"
y1="97.573967"
x1="257.69382"
gradientTransform="matrix(0.79012935,0,0,-0.8717404,-322.99681,0.94435315)"
gradientUnits="userSpaceOnUse"
id="linearGradient6249"
xlink:href="#linearGradient2515"
inkscape:collect="always" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2515"
id="linearGradient6782"
x1="-189.35655"
y1="50.600105"
x2="-86.646011"
y2="50.600105"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(256.87149,236.10798)" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2515-8"
id="linearGradient6782-8"
x1="-189.35655"
y1="50.600105"
x2="-86.646011"
y2="50.600105"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient2515-8"
y2="45.016998"
gradientUnits="userSpaceOnUse"
x2="20.759001"
y1="1"
x1="20.759001"><stop
id="stop3311-0"
style="stop-color:#f6f6f6"
offset="0" /><stop
id="stop3313-1"
style="stop-color:#d2d2d2"
offset="1" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2515-0"
id="linearGradient6782-86"
x1="-189.35655"
y1="50.600105"
x2="-86.646011"
y2="50.600105"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient2515-0"
y2="45.016998"
gradientUnits="userSpaceOnUse"
x2="20.759001"
y1="1"
x1="20.759001"><stop
id="stop3311-00"
style="stop-color:#f6f6f6"
offset="0" /><stop
id="stop3313-2"
style="stop-color:#d2d2d2"
offset="1" /></linearGradient><filter
id="filter4056-0"
inkscape:label="Inner Shadow"
inkscape:menu="Shadows and Glows"
inkscape:menu-tooltip="Adds a colorizable drop shadow inside"
color-interpolation-filters="sRGB"><feGaussianBlur
id="feGaussianBlur4058-2"
stdDeviation="7"
result="result8" /><feOffset
id="feOffset4060-8"
dx="4"
dy="4"
result="result11" /><feComposite
id="feComposite4062-72"
in2="result11"
result="result6"
in="SourceGraphic"
operator="in" /><feFlood
id="feFlood4064-7"
result="result10"
in="result6"
flood-opacity="1"
flood-color="rgb(0,0,0)" /><feBlend
id="feBlend4066-4"
in2="result10"
mode="normal"
in="result6"
result="result12" /><feComposite
id="feComposite4068-6"
in2="SourceGraphic"
result="result2"
operator="in" /></filter><filter
id="filter6856"
inkscape:label="Inner Shadow"
inkscape:menu="Shadows and Glows"
inkscape:menu-tooltip="Adds a colorizable drop shadow inside"
color-interpolation-filters="sRGB"><feGaussianBlur
id="feGaussianBlur6858"
stdDeviation="7"
result="result8" /><feOffset
id="feOffset6860"
dx="4"
dy="4"
result="result11" /><feComposite
id="feComposite6862"
in2="result11"
result="result6"
in="SourceGraphic"
operator="in" /><feFlood
id="feFlood6864"
result="result10"
in="result6"
flood-opacity="1"
flood-color="rgb(0,0,0)" /><feBlend
id="feBlend6866"
in2="result10"
mode="normal"
in="result6"
result="result12" /><feComposite
id="feComposite6868"
in2="SourceGraphic"
result="result2"
operator="in" /></filter></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1280"
inkscape:window-height="1000"
id="namedview4"
showgrid="false"
inkscape:zoom="0.827"
inkscape:cx="250"
inkscape:cy="250"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1"
inkscape:current-layer="g10" /><g
id="g10"
inkscape:groupmode="layer"
inkscape:label="wpzoom_developer"
transform="matrix(1.25,0,0,-1.25,0,500)"><g
inkscape:label="wpzoom_developer"
id="g3030" /><path
style="fill:url(#linearGradient6782);fill-opacity:1;stroke:#666666;stroke-width:2.4000001;stroke-miterlimit:4;stroke-dasharray:none"
d="m 68.714941,286.07266 c 0,0 23.66646,-8.02015 37.597189,-45.03399 1.64953,-4.38281 10.12858,-5.05491 11.65393,-1.89465 5.43194,11.25397 -0.91188,30.81556 -0.91188,30.81556 25.49151,0.25225 68.09402,-6.4894 45.65374,16.52624 0.96991,0.49199 14.81442,12.22014 -3.51183,17.14597 12.05736,9.74191 4.64063,13.34392 -2.85665,16.64724 16.78512,28.64473 -72.954709,11.50982 -87.211339,3.80398 z"
id="path4017"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csscccccc"
inkscape:export-filename="/home/jrbastien/Ubuntu One/CoverArt/0.8 icons/Thumb_down.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" /><path
style="fill:#333333;fill-opacity:1;stroke:none"
d="m 70.253351,159.8587 c 0,0 23.66646,-8.02015 37.597189,-45.03398 1.64953,-4.38281 10.12858,-5.05491 11.65393,-1.89465 5.43194,11.25397 -0.91188,30.81555 -0.91188,30.81555 25.49151,0.25226 68.09402,-6.4894 45.65373,16.52624 0.96992,0.49199 14.81443,12.22014 -3.51182,17.14597 12.05736,9.74191 4.64063,13.34392 -2.85664,16.64725 16.78511,28.64472 -72.954719,11.50981 -87.211349,3.80397 z"
id="path4017-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csscccccc"
inkscape:export-filename="/home/jrbastien/Ubuntu One/CoverArt/0.8 dark icons/Thumb_down.png"
inkscape:export-xdpi="91.586052"
inkscape:export-ydpi="91.586052" /><path
style="fill:#b3b3b3;fill-opacity:1;stroke:none;filter:url(#filter6856)"
d="m 212.08526,159.8587 c 0,0 23.66646,-8.02015 37.59719,-45.03399 1.64953,-4.3828 10.12858,-5.0549 11.65393,-1.89464 5.43194,11.25397 -0.91188,30.81556 -0.91188,30.81556 25.49151,0.25225 68.09402,-6.48941 45.65372,16.52623 0.96992,0.49199 14.81444,12.22014 -3.51182,17.14597 12.05737,9.74191 4.64063,13.34392 -2.85664,16.64725 16.78512,28.64472 -72.95471,11.50981 -87.21134,3.80397 z"
id="path4017-21"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csscccccc"
inkscape:export-filename="/home/jrbastien/Ubuntu One/CoverArt/0.8 light/Thumb_down_light.png"
inkscape:export-xdpi="73.268837"
inkscape:export-ydpi="73.268837" /></g></svg>

Depois

Largura:  |  Altura:  |  Tamanho: 8.3 KiB

Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais