Merge lp:entertainer/future into lp:entertainer

Proposed by Matt Layman
Status: Merged
Approved by: Matt Layman
Approved revision: not available
Merge reported by: Matt Layman
Merged at revision: not available
Proposed branch: lp:entertainer/future
Merge into: lp:entertainer
Diff against target: 17252 lines (+6079/-7223)
180 files modified
MANIFEST.in (+7/-0)
README (+6/-0)
cfg/content.conf (+22/-14)
cfg/preferences.conf (+0/-16)
docs/COPYING (+1/-0)
docs/DEPENDENCIES (+1/-1)
entertainer (+2/-1)
entertainer-backend (+1/-5)
entertainer-manager (+6/-7)
entertainer-preferences (+0/-14)
entertainer-server (+7/-0)
entertainerlib/backend/backend_server.py (+5/-16)
entertainerlib/backend/components/feeds/feed_fetcher.py (+2/-2)
entertainerlib/backend/components/feeds/feed_manager.py (+3/-4)
entertainerlib/backend/components/feeds/feed_utils.py (+1/-2)
entertainerlib/backend/components/mediacache/image_cache.py (+4/-6)
entertainerlib/backend/components/mediacache/media_cache_manager.py (+9/-9)
entertainerlib/backend/components/mediacache/music_cache.py (+5/-5)
entertainerlib/backend/components/mediacache/video_cache.py (+3/-3)
entertainerlib/backend/components/mediacache/video_metadata_search.py (+2/-2)
entertainerlib/backend/core/client_connection.py (+1/-1)
entertainerlib/backend/core/connection_server.py (+1/-1)
entertainerlib/backend/core/message_bus.py (+2/-13)
entertainerlib/backend/core/message_bus_proxy.py (+2/-2)
entertainerlib/backend/core/message_type_priority.py (+9/-38)
entertainerlib/client/__init__.py (+9/-9)
entertainerlib/client/backend_connection.py (+1/-29)
entertainerlib/client/client.py (+41/-47)
entertainerlib/client/media_player.py (+15/-9)
entertainerlib/client/medialibrary/feeds.py (+1/-1)
entertainerlib/client/medialibrary/images.py (+4/-13)
entertainerlib/client/medialibrary/music.py (+6/-15)
entertainerlib/client/medialibrary/videos.py (+9/-18)
entertainerlib/client/translation_setup.py (+36/-26)
entertainerlib/configuration.py (+257/-489)
entertainerlib/db/connection.py (+1/-1)
entertainerlib/db/models.py (+3/-2)
entertainerlib/dialog.py (+1113/-0)
entertainerlib/download.py (+455/-0)
entertainerlib/gui/screen_history.py (+2/-2)
entertainerlib/gui/screens/album.py (+5/-6)
entertainerlib/gui/screens/artist.py (+5/-5)
entertainerlib/gui/screens/audio_play.py (+4/-4)
entertainerlib/gui/screens/disc.py (+9/-11)
entertainerlib/gui/screens/factory.py (+20/-19)
entertainerlib/gui/screens/feed.py (+6/-6)
entertainerlib/gui/screens/feed_entry.py (+6/-6)
entertainerlib/gui/screens/main.py (+11/-17)
entertainerlib/gui/screens/movie.py (+13/-10)
entertainerlib/gui/screens/music.py (+7/-6)
entertainerlib/gui/screens/photo.py (+20/-21)
entertainerlib/gui/screens/photo_albums.py (+16/-17)
entertainerlib/gui/screens/photographs.py (+6/-7)
entertainerlib/gui/screens/question.py (+3/-3)
entertainerlib/gui/screens/rss.py (+6/-6)
entertainerlib/gui/screens/screen.py (+4/-6)
entertainerlib/gui/screens/tv_episodes.py (+7/-7)
entertainerlib/gui/screens/tv_series.py (+5/-6)
entertainerlib/gui/screens/video.py (+6/-5)
entertainerlib/gui/screens/video_osd.py (+38/-22)
entertainerlib/gui/screens/weather.py (+6/-5)
entertainerlib/gui/system_tray_icon.py (+24/-31)
entertainerlib/gui/tabs/albums_tab.py (+7/-6)
entertainerlib/gui/tabs/artists_tab.py (+7/-7)
entertainerlib/gui/tabs/lyrics_tab.py (+4/-4)
entertainerlib/gui/tabs/movies_tab.py (+7/-6)
entertainerlib/gui/tabs/playing_tab.py (+3/-3)
entertainerlib/gui/tabs/series_tab.py (+6/-6)
entertainerlib/gui/tabs/tab.py (+6/-6)
entertainerlib/gui/tabs/tracks_tab.py (+7/-6)
entertainerlib/gui/tabs/video_clips_tab.py (+7/-6)
entertainerlib/gui/transitions/factory.py (+8/-7)
entertainerlib/gui/transitions/fade.py (+1/-1)
entertainerlib/gui/transitions/no_effect.py (+2/-1)
entertainerlib/gui/transitions/slide.py (+15/-11)
entertainerlib/gui/transitions/zoom_and_fade.py (+1/-1)
entertainerlib/gui/user_event.py (+1/-1)
entertainerlib/gui/user_interface.py (+38/-46)
entertainerlib/gui/widgets/arrow_texture.py (+24/-15)
entertainerlib/gui/widgets/base.py (+6/-5)
entertainerlib/gui/widgets/clock_label.py (+1/-1)
entertainerlib/gui/widgets/eyecandy_texture.py (+3/-3)
entertainerlib/gui/widgets/grid_menu.py (+24/-20)
entertainerlib/gui/widgets/image_menu.py (+10/-6)
entertainerlib/gui/widgets/label.py (+11/-11)
entertainerlib/gui/widgets/list_indicator.py (+5/-4)
entertainerlib/gui/widgets/loading_animation.py (+12/-16)
entertainerlib/gui/widgets/menu_item.py (+1/-1)
entertainerlib/gui/widgets/menu_overlay.py (+3/-3)
entertainerlib/gui/widgets/progress_bar.py (+6/-7)
entertainerlib/gui/widgets/reflection_texture.py (+6/-6)
entertainerlib/gui/widgets/rounded_texture.py (+6/-5)
entertainerlib/gui/widgets/scroll_area.py (+6/-7)
entertainerlib/gui/widgets/scroll_menu.py (+5/-5)
entertainerlib/gui/widgets/selector.py (+20/-23)
entertainerlib/gui/widgets/special_behaviours.py (+0/-2)
entertainerlib/gui/widgets/tab_group.py (+8/-7)
entertainerlib/gui/widgets/text_menu.py (+13/-12)
entertainerlib/gui/widgets/texture.py (+3/-3)
entertainerlib/gui/widgets/volume_indicator.py (+9/-5)
entertainerlib/indexing/__init__.py (+1/-0)
entertainerlib/indexing/handlers.py (+144/-0)
entertainerlib/indexing/indexer.py (+57/-0)
entertainerlib/indexing/utilities.py (+49/-0)
entertainerlib/logger.py (+2/-2)
entertainerlib/network/__init__.py (+27/-0)
entertainerlib/network/local/__init__.py (+1/-0)
entertainerlib/network/local/client.py (+35/-0)
entertainerlib/network/local/commands.py (+14/-0)
entertainerlib/network/local/server.py (+40/-0)
entertainerlib/network/storage.py (+1/-1)
entertainerlib/storage/__init__.py (+0/-5)
entertainerlib/tests/__init__.py (+4/-8)
entertainerlib/tests/mock.py (+24/-42)
entertainerlib/tests/test_arrowtexture.py (+4/-3)
entertainerlib/tests/test_base.py (+3/-2)
entertainerlib/tests/test_configuration.py (+40/-129)
entertainerlib/tests/test_database.py (+1/-1)
entertainerlib/tests/test_eyecandytexture.py (+3/-2)
entertainerlib/tests/test_feedconfigtools.py (+8/-6)
entertainerlib/tests/test_feedentryparser.py (+1/-1)
entertainerlib/tests/test_filehandlers.py (+165/-0)
entertainerlib/tests/test_frontendfeed.py (+3/-3)
entertainerlib/tests/test_frontendfeedentry.py (+3/-3)
entertainerlib/tests/test_frontendfeedlibrary.py (+8/-6)
entertainerlib/tests/test_gridmenu.py (+3/-3)
entertainerlib/tests/test_imagemenu.py (+3/-3)
entertainerlib/tests/test_imagemenuitem.py (+3/-3)
entertainerlib/tests/test_indexer.py (+56/-0)
entertainerlib/tests/test_indexerutilities.py (+32/-0)
entertainerlib/tests/test_label.py (+3/-3)
entertainerlib/tests/test_listindicator.py (+3/-2)
entertainerlib/tests/test_loadinganimation.py (+3/-3)
entertainerlib/tests/test_logger.py (+1/-1)
entertainerlib/tests/test_lyricsdownloader.py (+2/-1)
entertainerlib/tests/test_mediaplayer.py (+3/-3)
entertainerlib/tests/test_menuitem.py (+2/-2)
entertainerlib/tests/test_models.py (+1/-204)
entertainerlib/tests/test_motionbuffer.py (+2/-2)
entertainerlib/tests/test_music.py (+3/-9)
entertainerlib/tests/test_opmlparser.py (+1/-1)
entertainerlib/tests/test_progressbar.py (+2/-2)
entertainerlib/tests/test_reflectiontexture.py (+3/-3)
entertainerlib/tests/test_resources.py (+39/-0)
entertainerlib/tests/test_roundedtexture.py (+3/-2)
entertainerlib/tests/test_screen.py (+7/-7)
entertainerlib/tests/test_screenfactory.py (+21/-21)
entertainerlib/tests/test_screenhistory.py (+3/-3)
entertainerlib/tests/test_scrollarea.py (+3/-6)
entertainerlib/tests/test_scrollmenu.py (+2/-2)
entertainerlib/tests/test_storage.py (+1/-2)
entertainerlib/tests/test_tabgroup.py (+4/-4)
entertainerlib/tests/test_textmenu.py (+2/-2)
entertainerlib/tests/test_textmenuitem.py (+2/-2)
entertainerlib/tests/test_texture.py (+3/-3)
entertainerlib/tests/test_theme.py (+3/-3)
entertainerlib/tests/test_transitionfactory.py (+9/-8)
entertainerlib/tests/test_userinterface.py (+5/-15)
entertainerlib/tests/test_volumeindicator.py (+3/-2)
entertainerlib/tests/test_weather.py (+1/-1)
entertainerlib/thumbnailer.py (+1/-1)
entertainerlib/uis/log_dialog.ui (+55/-57)
entertainerlib/uis/manager.ui (+1885/-1317)
entertainerlib/uis/open_feed_source_dialog.ui (+54/-51)
entertainerlib/uis/system_tray_icon_menu.ui (+51/-77)
entertainerlib/utils/__init__.py (+0/-2)
entertainerlib/utils/albumart_downloader.py (+0/-346)
entertainerlib/utils/cd_utils.py (+0/-8)
entertainerlib/utils/content_management_dialog.py (+0/-782)
entertainerlib/utils/glade/entertainer-preferences.glade (+0/-1223)
entertainerlib/utils/log_viewer.py (+0/-177)
entertainerlib/utils/lyrics_downloader.py (+0/-112)
entertainerlib/utils/open_feed_source_dialog.py (+0/-142)
entertainerlib/utils/preferences_dialog.py (+0/-315)
entertainerlib/weather.py (+1/-1)
pylintrc (+1/-1)
setup.py (+21/-19)
tools/messagebus-notifier (+0/-51)
tools/po/entertainer.pot (+544/-658)
tools/translations_generator.py (+7/-7)
To merge this branch: bzr merge lp:entertainer/future
Reviewer Review Type Date Requested Status
Matt Layman Approve
Review via email: mp+16371@code.launchpad.net

Commit message

Merge in all the good work that had been happening in future. This should get trunk working again. It was badly neglected.

To post a comment you must log in.
Revision history for this message
Matt Layman (mblayman) wrote :

So... stuff is broken. Very broken.

Trunk is broken for Karmic users and so is future. However, we're doing all of our work on future. This would be good *except* we lose the benefit of Tarmac landing our branches.

The whole reason we started using Tarmac was to avoid the problem of a human gatekeeper who may get too busy to merge code. Since we are now only using future, we totally lose what Tarmac was doing for us so there is code that has been idly sitting approved. I think we can all agree that unmerged *approved* code that is not merged is a delta that is a bad thing.

Therefore, I'm requesting we merge future into trunk, primarily for the sole benefit of not being blocked by human gatekeeper so we can make forward progress again (no offense Paul, I realize you're really busy).

I would love to use future again in the future, but I think it suffers from the human gatekeeper problem and should be configured to use Tarmac if possible.

I'm making this request at the jeopardy of the stability of trunk just because of how broken things are anyway. At this point, I think future is much better code than trunk.

-Matt

P.S. Let me try filing this against the correct branch this time. :P

lp:entertainer/future updated
382. By Matt Layman

Removes some crufty backend connection messages that did nothing. (Matt Layman)

383. By Matt Layman

Converted glade to gtk builder. (Francesco Marella)

384. By Matt Layman

Fix Open feed dialog. (Francesco Marella)

385. By Matt Layman

Final cleanup of clutter 1.0 transition. Fixed the fullscreen size problem.
(Matt Layman)

Revision history for this message
Matt Layman (mblayman) wrote :

Discussed on IRC with Paul. Trunk is so broken that this needs to be done.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'MANIFEST.in'
2--- MANIFEST.in 2008-10-05 17:11:31 +0000
3+++ MANIFEST.in 2010-01-23 03:34:10 +0000
4@@ -1,3 +1,10 @@
5 include entertainer*
6+include docs/COPYING docs/DEPENDENCIES
7+include docs/entertainer.1 docs/entertainer.desktop
8 recursive-include cfg *
9+recursive-include entertainerlib *
10+recursive-include icons *
11+recursive-include themes *
12+recursive-include tools *
13+global-exclude *pyc
14
15
16=== added file 'README'
17--- README 1970-01-01 00:00:00 +0000
18+++ README 2010-01-23 03:34:10 +0000
19@@ -0,0 +1,6 @@
20+To install Entertainer, run:
21+
22+python setup.py install
23+
24+For information on how to contribute, read docs/HACKING.
25+
26
27=== modified file 'cfg/content.conf'
28--- cfg/content.conf 2009-05-31 17:11:16 +0000
29+++ cfg/content.conf 2010-01-23 03:34:11 +0000
30@@ -1,26 +1,34 @@
31 # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
32-[Images]
33-folders =
34-display_hidden_files_folders = False
35+[Media]
36+download_lyrics = False
37+download_album_art = True
38+download_metadata = True
39+display_eject_in_menu = False
40+folders =
41
42 [Weather]
43 location = Bath,England
44 display_in_menu = True
45-metric_units = True
46-
47-[Music]
48-folders =
49-download_lyrics = False
50-download_album_art = True
51-
52-[Videos]
53-folders =
54-download_metadata = True
55
56 [RSS]
57-feeds = http://theironlion.net/feeds/blog;http://www.joshuascotton.com/main/archives/tag/entertainer/feed;http://laymanstermsdev.wordpress.com/feed
58+feeds = http://theironlion.net/blog/feed;http://www.joshuascotton.com/main/archives/tag/entertainer/feed;http://laymanstermsdev.wordpress.com/feed
59 fetch_interval = 15
60
61 [CD]
62 display_eject_in_menu = False
63
64+[Photographs]
65+slideshow_step = 5
66+
67+[General]
68+stage_width = 1366
69+stage_height = 768
70+show_effects = True
71+start_in_fullscreen = True
72+theme = Default
73+backend_port = 45054
74+history_size = 8
75+transition_effect = Slide
76+start_server_auto = True
77+display_icon = False
78+
79
80=== removed file 'cfg/preferences.conf'
81--- cfg/preferences.conf 2009-05-06 03:40:22 +0000
82+++ cfg/preferences.conf 1970-01-01 00:00:00 +0000
83@@ -1,16 +0,0 @@
84-# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
85-[Photographs]
86-slideshow_step = 5
87-
88-[General]
89-stage_width = 1366
90-stage_height = 768
91-show_effects = True
92-start_in_fullscreen = True
93-theme = Default
94-backend_port = 45054
95-history_size = 8
96-transition_effect = Slide
97-start_server_auto = True
98-display_icon = False
99-
100
101=== modified file 'docs/COPYING'
102--- docs/COPYING 2009-05-09 15:45:18 +0000
103+++ docs/COPYING 2010-01-23 03:34:11 +0000
104@@ -359,6 +359,7 @@
105 GNU General Public License for more details.
106
107 Entertainer Developers, as referenced in each file's copyright, refers to:
108+ * Francesco Marella <francesco.marella@gmail.com>
109 * Jamie Bennett <jamie@linuxuk.org>
110 * Joshua Scotton <josh@joshuascotton.com>
111 * Lauri Taimila <lauri@taimila.com>
112
113=== modified file 'docs/DEPENDENCIES'
114--- docs/DEPENDENCIES 2009-05-09 19:08:39 +0000
115+++ docs/DEPENDENCIES 2010-01-23 03:34:11 +0000
116@@ -4,7 +4,6 @@
117 python-ctypes
118 python-eyed3
119 python-feedparser
120-python-glade2
121 python-gobject
122 python-gst0.10
123 python-gtk2
124@@ -14,3 +13,4 @@
125 python-pyvorbis
126 python-storm
127 python-twisted
128+python-xdg
129
130=== modified file 'entertainer'
131--- entertainer 2009-05-06 02:58:08 +0000
132+++ entertainer 2010-01-23 03:34:10 +0000
133@@ -1,8 +1,9 @@
134 #!/usr/bin/env python
135 # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
136+'''Main client executable'''
137 '''Main frontend executable'''
138
139-from entertainerlib.frontend import main
140+from entertainerlib.client import main
141
142 if __name__ == '__main__':
143 main()
144
145=== modified file 'entertainer-backend'
146--- entertainer-backend 2009-05-06 02:58:08 +0000
147+++ entertainer-backend 2010-01-23 03:34:10 +0000
148@@ -51,11 +51,7 @@
149 sys.exit(0)
150
151 if len(sys.argv) > 1 and sys.argv[1] == "--foreground":
152- try:
153- backend = BackendServer()
154- except KeyboardInterrupt:
155- backend.quitBackend()
156- sys.exit()
157+ backend = BackendServer()
158 else:
159 print "Entertainer backend starting..."
160 libc = ctypes.CDLL('libc.so.6')
161
162=== renamed file 'entertainer-content-manager' => 'entertainer-manager'
163--- entertainer-content-manager 2009-04-28 23:54:49 +0000
164+++ entertainer-manager 2010-01-23 03:34:10 +0000
165@@ -1,15 +1,14 @@
166 #!/usr/bin/env python
167 # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
168-'''Content Management executable'''
169+'''Manager executable'''
170
171 import gtk
172
173-from entertainerlib.frontend.translation_setup import TranslationSetup
174+from entertainerlib.client.translation_setup import TranslationSetup
175 TranslationSetup()
176
177-from entertainerlib.utils.content_management_dialog import (
178- ContentManagementDialog)
179-
180-
181-ContentManagementDialog(True)
182+from entertainerlib.dialog import ManagerDialog
183+
184+
185+ManagerDialog(True)
186 gtk.main()
187
188=== removed file 'entertainer-preferences'
189--- entertainer-preferences 2009-04-28 23:54:49 +0000
190+++ entertainer-preferences 1970-01-01 00:00:00 +0000
191@@ -1,14 +0,0 @@
192-#!/usr/bin/env python
193-# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
194-'''A preferences editing tool'''
195-
196-import gtk
197-
198-from entertainerlib.frontend.translation_setup import TranslationSetup
199-TranslationSetup()
200-
201-from entertainerlib.utils.preferences_dialog import PreferencesDialog
202-
203-
204-PreferencesDialog(True)
205-gtk.main()
206
207=== added file 'entertainer-server'
208--- entertainer-server 1970-01-01 00:00:00 +0000
209+++ entertainer-server 2010-01-23 03:34:10 +0000
210@@ -0,0 +1,7 @@
211+#!/usr/bin/env python
212+'''Server executable'''
213+
214+from entertainerlib.network import server_main
215+
216+server_main()
217+
218
219=== modified file 'entertainerlib/backend/backend_server.py'
220--- entertainerlib/backend/backend_server.py 2009-05-06 02:58:08 +0000
221+++ entertainerlib/backend/backend_server.py 2010-01-23 03:34:11 +0000
222@@ -3,8 +3,8 @@
223
224 import gobject
225
226-from entertainerlib.utils.configuration import Configuration
227-from entertainerlib.utils.logger import Logger
228+from entertainerlib.configuration import Configuration
229+from entertainerlib.logger import Logger
230
231 # Entertainer backend core
232 from entertainerlib.backend.core.message import Message
233@@ -39,18 +39,13 @@
234 self.config = Configuration()
235 self.logger = Logger().getLogger('backend.BackendServer')
236 self.message_bus = MessageBus()
237- self._port = self.config.get_port()
238+ self._port = self.config.port
239
240 # Connection server - Thread that listens incoming socket connections
241 self.connection_server = None
242
243- # Config files
244- self.content_config = None
245- self.preferences = None
246-
247 self.scheduler = None
248 self.feed_manager = None
249- self.guide_updater = None
250 self.media_manager = None
251
252 # The order of the initialize method calls is significant! Don't change
253@@ -65,15 +60,13 @@
254 """Initialize configuration"""
255 cfg_dict = {
256 MessageType.CONTENT_CONF_UPDATED : MessagePriority.VERY_HIGH,
257- MessageType.PREFERENCES_CONF_UPDATED : MessagePriority.VERY_HIGH
258 }
259 self.message_bus.registerMessageHandler(self.config, cfg_dict)
260 self.logger.debug("Configuration intialized successfully")
261
262 def initialize_connection_server(self):
263 """Initialize connection server."""
264- self.connection_server = ConnectionServer(self._port,
265- self.message_bus)
266+ self.connection_server = ConnectionServer(self._port, self.message_bus)
267 # Start listening incoming connections
268 self.connection_server.start()
269
270@@ -81,7 +74,7 @@
271 """Initialize message scheduler."""
272 self.scheduler = MessageScheduler(self.message_bus)
273 self.scheduler.addMessage(Message(MessageType.UPDATE_FEEDS),
274- 60 * self.config.get_feed_fetch_interval())
275+ 60 * self.config.feed_fetch_interval)
276 self.logger.debug("Message scheduler intialized successfully")
277
278 def initialize_feed_manager(self):
279@@ -105,7 +98,3 @@
280 self.message_bus.registerMessageHandler(self.media_manager, media_dict)
281 self.logger.debug("Media Manager intialized successfully")
282
283- def quitBackend(self):
284- '''Close the backend server'''
285- self.message_bus.unregisterAllMessageHandlers()
286-
287
288=== modified file 'entertainerlib/backend/components/feeds/feed_fetcher.py'
289--- entertainerlib/backend/components/feeds/feed_fetcher.py 2009-05-06 02:58:08 +0000
290+++ entertainerlib/backend/components/feeds/feed_fetcher.py 2010-01-23 03:34:11 +0000
291@@ -7,8 +7,8 @@
292 from datetime import datetime
293 from pysqlite2 import dbapi2 as sqlite
294
295-from entertainerlib.utils.configuration import Configuration
296-from entertainerlib.utils.logger import Logger
297+from entertainerlib.configuration import Configuration
298+from entertainerlib.logger import Logger
299
300 # Messaging system
301 from entertainerlib.backend.core.message import Message
302
303=== modified file 'entertainerlib/backend/components/feeds/feed_manager.py'
304--- entertainerlib/backend/components/feeds/feed_manager.py 2009-05-06 02:58:08 +0000
305+++ entertainerlib/backend/components/feeds/feed_manager.py 2010-01-23 03:34:11 +0000
306@@ -5,8 +5,8 @@
307 from pysqlite2 import dbapi2 as sqlite
308 from entertainerlib.backend.components.feeds.feed_fetcher import FeedFetcher
309
310-from entertainerlib.utils.configuration import Configuration
311-from entertainerlib.utils.logger import Logger
312+from entertainerlib.configuration import Configuration
313+from entertainerlib.logger import Logger
314
315 # Messaging system
316 from entertainerlib.backend.core.message_type_priority import MessageType
317@@ -35,8 +35,7 @@
318 Update all feeds to cache in a new thread and after
319 that emit FEED_DB_UPDATED message to the messagebus.
320 """
321- fetch_thread = FeedFetcher(self.message_bus,
322- self.config.get_feeds())
323+ fetch_thread = FeedFetcher(self.message_bus, self.config.feeds)
324 fetch_thread.start()
325
326 def createFeedCacheDatabase(self):
327
328=== renamed file 'entertainerlib/utils/feed_utils.py' => 'entertainerlib/backend/components/feeds/feed_utils.py'
329--- entertainerlib/utils/feed_utils.py 2009-05-06 02:02:20 +0000
330+++ entertainerlib/backend/components/feeds/feed_utils.py 2010-01-23 03:34:11 +0000
331@@ -6,9 +6,8 @@
332 from xml.dom import minidom
333
334 import gtk
335-import gtk.glade
336
337-from entertainerlib.utils.configuration import Configuration
338+from entertainerlib.configuration import Configuration
339
340
341 class FeedEntryParser:
342
343=== modified file 'entertainerlib/backend/components/mediacache/image_cache.py'
344--- entertainerlib/backend/components/mediacache/image_cache.py 2009-06-01 09:55:40 +0000
345+++ entertainerlib/backend/components/mediacache/image_cache.py 2010-01-23 03:34:11 +0000
346@@ -8,8 +8,8 @@
347 from pysqlite2 import dbapi2 as sqlite
348
349 from entertainerlib.thumbnailer import ImageThumbnailer
350-from entertainerlib.utils.configuration import Configuration
351-from entertainerlib.utils.logger import Logger
352+from entertainerlib.configuration import Configuration
353+from entertainerlib.logger import Logger
354
355 from entertainerlib.backend.components.mediacache.cache import Cache
356
357@@ -117,15 +117,13 @@
358 "Path doesn't exist: " + path)
359 else:
360 for root, dirs, files in os.walk(path):
361- if os.path.split(root)[-1][0] == "." and not \
362- self.config.display_hidden_files_folders():
363+ if os.path.split(root)[-1][0] == ".":
364 continue
365 if not self.isDirectoryInCache(root):
366 self._addAlbum(root)
367
368 for name in files:
369- if os.path.split(name)[-1][0] == "." and not \
370- self.config.display_hidden_files_folders():
371+ if os.path.split(name)[-1][0] == ".":
372 continue
373 if self.isSupportedFormat(name):
374 self.addFile(os.path.join(root, name))
375
376=== modified file 'entertainerlib/backend/components/mediacache/media_cache_manager.py'
377--- entertainerlib/backend/components/mediacache/media_cache_manager.py 2009-05-06 02:58:08 +0000
378+++ entertainerlib/backend/components/mediacache/media_cache_manager.py 2010-01-23 03:34:11 +0000
379@@ -1,8 +1,8 @@
380 # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
381 '''MediaCacheManager - Downloads metadata and keeps media cache up-to-date'''
382
383-from entertainerlib.utils.configuration import Configuration
384-from entertainerlib.utils.logger import Logger
385+from entertainerlib.configuration import Configuration
386+from entertainerlib.logger import Logger
387
388 from entertainerlib.backend.core.message_type_priority import MessageType
389 from entertainerlib.backend.core.message_handler import MessageHandler
390@@ -13,7 +13,7 @@
391 from entertainerlib.backend.components.mediacache.video_cache import VideoCache
392
393 class MediaCacheManager(MessageHandler):
394- """Makes sure that frontend has all the data available."""
395+ """Makes sure that client has all the data available."""
396
397 def __init__(self):
398 """
399@@ -23,11 +23,11 @@
400 self.logger = Logger().getLogger(
401 'backend.components.mediacache.MediaCacheManager')
402 self.config = Configuration()
403- self.video_folders = self.config.get_video_folders()
404+ self.video_folders = self.config.media_folders
405 self._index_videos(self.video_folders)
406- self.music_folders = self.config.get_music_folders()
407+ self.music_folders = self.config.media_folders
408 self._index_music(self.music_folders)
409- self.image_folders = self.config.get_image_folders()
410+ self.image_folders = self.config.media_folders
411 self._index_images(self.image_folders)
412
413 # Should we rebuild to detect files that were removed while backend was
414@@ -104,9 +104,9 @@
415 we need to index them. If folders are removed, we need to remove
416 them from the cache and also from FileSystemObeserver.
417 """
418- updated_video_folders = self.config.get_video_folders()
419- updated_music_folders = self.config.get_music_folders()
420- updated_image_folders = self.config.get_image_folders()
421+ updated_video_folders = self.config.media_folders
422+ updated_music_folders = self.config.media_folders
423+ updated_image_folders = self.config.media_folders
424
425 # Handle image folder changes
426 current_images = set(self.image_folders)
427
428=== modified file 'entertainerlib/backend/components/mediacache/music_cache.py'
429--- entertainerlib/backend/components/mediacache/music_cache.py 2009-05-09 17:03:51 +0000
430+++ entertainerlib/backend/components/mediacache/music_cache.py 2010-01-23 03:34:11 +0000
431@@ -9,11 +9,11 @@
432 import ogg.vorbis
433 from pysqlite2 import dbapi2 as sqlite
434
435-from entertainerlib.utils.albumart_downloader import AlbumArtDownloader
436-from entertainerlib.utils.configuration import Configuration
437-from entertainerlib.utils.logger import Logger
438-
439 from entertainerlib.backend.components.mediacache.cache import Cache
440+from entertainerlib.configuration import Configuration
441+from entertainerlib.download import AlbumArtDownloader
442+from entertainerlib.logger import Logger
443+
444
445 class MusicCache(Cache):
446 """
447@@ -433,7 +433,7 @@
448 album_art_file)
449 # Local not found -> try internet
450 else:
451- if self.config.download_album_art():
452+ if self.config.download_album_art:
453 if album != "Unknown album" and artist != "Unknown Artist":
454 loader_thread = AlbumArtDownloader(album, artist,
455 self.config.ALBUM_ART_DIR)
456
457=== modified file 'entertainerlib/backend/components/mediacache/video_cache.py'
458--- entertainerlib/backend/components/mediacache/video_cache.py 2009-05-06 02:58:08 +0000
459+++ entertainerlib/backend/components/mediacache/video_cache.py 2010-01-23 03:34:11 +0000
460@@ -6,8 +6,8 @@
461 from pysqlite2 import dbapi2 as sqlite
462
463 from entertainerlib.thumbnailer import VideoThumbnailer
464-from entertainerlib.utils.configuration import Configuration
465-from entertainerlib.utils.logger import Logger
466+from entertainerlib.configuration import Configuration
467+from entertainerlib.logger import Logger
468
469 from entertainerlib.backend.components.mediacache.cache import Cache
470 from entertainerlib.backend.components.mediacache.video_metadata_search import (
471@@ -243,7 +243,7 @@
472 VALUES (:fn)""",
473 { "fn" : filename } )
474 self.__db_conn.commit()
475- if self.config.download_video_metadata():
476+ if self.config.download_metadata:
477 self.__searchMetadata(filename)
478
479 def __searchMetadata(self, filename):
480
481=== modified file 'entertainerlib/backend/components/mediacache/video_metadata_search.py'
482--- entertainerlib/backend/components/mediacache/video_metadata_search.py 2009-05-06 02:58:08 +0000
483+++ entertainerlib/backend/components/mediacache/video_metadata_search.py 2010-01-23 03:34:11 +0000
484@@ -9,8 +9,8 @@
485 import threading
486 from pysqlite2 import dbapi2 as sqlite
487
488-from entertainerlib.utils.logger import Logger
489-from entertainerlib.utils.configuration import Configuration
490+from entertainerlib.logger import Logger
491+from entertainerlib.configuration import Configuration
492
493 class VideoMetadataSearch(threading.Thread):
494 """
495
496=== modified file 'entertainerlib/backend/core/client_connection.py'
497--- entertainerlib/backend/core/client_connection.py 2009-05-06 02:58:08 +0000
498+++ entertainerlib/backend/core/client_connection.py 2010-01-23 03:34:11 +0000
499@@ -8,7 +8,7 @@
500 # Messaging system
501 from entertainerlib.backend.core.message_handler import MessageHandler
502
503-from entertainerlib.utils.logger import Logger
504+from entertainerlib.logger import Logger
505
506 class ClientConnection(threading.Thread, MessageHandler):
507 """
508
509=== modified file 'entertainerlib/backend/core/connection_server.py'
510--- entertainerlib/backend/core/connection_server.py 2009-05-06 02:58:08 +0000
511+++ entertainerlib/backend/core/connection_server.py 2010-01-23 03:34:11 +0000
512@@ -7,7 +7,7 @@
513
514 from entertainerlib.backend.core.client_connection import ClientConnection
515
516-from entertainerlib.utils.logger import Logger
517+from entertainerlib.logger import Logger
518
519 class ConnectionServer(threading.Thread):
520 """
521
522=== modified file 'entertainerlib/backend/core/message_bus.py'
523--- entertainerlib/backend/core/message_bus.py 2009-05-06 02:58:08 +0000
524+++ entertainerlib/backend/core/message_bus.py 2010-01-23 03:34:11 +0000
525@@ -6,7 +6,7 @@
526 from entertainerlib.backend.core.message import Message
527 from entertainerlib.backend.core.message_handler import MessageHandler
528 from entertainerlib.backend.core.message_type_priority import MessageType
529-from entertainerlib.utils.logger import Logger
530+from entertainerlib.logger import Logger
531
532 class MessageBus:
533 """
534@@ -23,12 +23,7 @@
535 When MessageHandler is registered to the MessageBus there is also another
536 parameter besides handler itself. Second parameter is a dictionary that
537 defines MessageTypes that registered handler wants to be notified of and
538- also priorities for those message types.
539-
540- Example of second parameter:
541- dict = {MessageType.FRONTEND_OPENED : MessagePriority.LOW ,
542- MessageType.FRONTEND_CLOSED : MessagePriority.NORMAL }
543- """
544+ also priorities for those message types."""
545
546 # This determines number of message types avaialble. In other words, this
547 # variable tells how many variables is defined in MessageType class.
548@@ -89,12 +84,6 @@
549 self.logger.debug("MessageHandler '" + str(message_handler) +
550 "' unregistered from the message bus.")
551
552- def unregisterAllMessageHandlers(self):
553- """
554- Unregisters all MessageHandler from this MessageBus.
555- """
556- self.message_handlers[:] = []
557-
558 def notifyMessage(self, message):
559 """
560 Emit a new Message to this MessageBus.
561
562=== modified file 'entertainerlib/backend/core/message_bus_proxy.py'
563--- entertainerlib/backend/core/message_bus_proxy.py 2009-05-06 02:58:08 +0000
564+++ entertainerlib/backend/core/message_bus_proxy.py 2010-01-23 03:34:11 +0000
565@@ -6,7 +6,7 @@
566 import threading
567 from cStringIO import StringIO
568
569-from entertainerlib.utils.configuration import Configuration
570+from entertainerlib.configuration import Configuration
571
572 class MessageBusProxy(threading.Thread):
573 """
574@@ -57,7 +57,7 @@
575 MessageHandler.
576 """
577 # Open socket
578- self.socket_to_server.connect(('localhost', self.config.get_port()))
579+ self.socket_to_server.connect(('localhost', self.config.port))
580
581 # Send client name
582 self.socket_to_server.sendall(self.client_name + "\n")
583
584=== modified file 'entertainerlib/backend/core/message_type_priority.py'
585--- entertainerlib/backend/core/message_type_priority.py 2009-05-06 02:58:08 +0000
586+++ entertainerlib/backend/core/message_type_priority.py 2010-01-23 03:34:11 +0000
587@@ -15,56 +15,27 @@
588 VERY_LOW = 40
589
590 class MessageType:
591- """
592- Determines all allowed Message types. MessageHandler should use these
593- to determine type. This simply makes code more readable.
594-
595- Example:
596- if message.get_type() == MessageType.FRONTEND_OPENED:
597- do_something_useful()
598- """
599-
600- # Indicates that Preferences UI has been used to update preferences.
601- PREFERENCES_CONF_UPDATED = 0
602+ """Determines all allowed Message types. MessageHandler should use these to
603+ determine type. This simply makes code more readable."""
604
605 # Indicates that Content Management UI has been used to update contents.
606- CONTENT_CONF_UPDATED = 1
607-
608- # Indicates that frontend has been opened.
609- FRONTEND_OPENED = 2
610-
611- # Indicates that frontend has been closed.
612- FRONTEND_CLOSED = 3
613+ CONTENT_CONF_UPDATED = 0
614
615 # Indicates that Feed cache has been updated.
616- FEED_DB_UPDATED = 4
617-
618- NOT_USED_5 = 5
619-
620- NOT_USED_1 = 6
621+ FEED_DB_UPDATED = 1
622
623 # Indicates that Feed cache should be updated.
624- UPDATE_FEEDS = 7
625-
626- NOT_USED_6 = 8
627-
628- #This should be left in otherwise message_bus.py breaks
629- #If another message type is needed please use this
630- DO_NOT_DELETE_USE_NEXT = 9
631-
632- NOT_USED_2 = 10
633- NOT_USED_3 = 11
634- NOT_USED_4 = 12
635+ UPDATE_FEEDS = 2
636
637 # Require to rebuild image cache
638- REBUILD_IMAGE_CACHE = 13
639+ REBUILD_IMAGE_CACHE = 3
640
641 # Require to rebuild music cache
642- REBUILD_MUSIC_CACHE = 14
643+ REBUILD_MUSIC_CACHE = 4
644
645 # Require to rebuild video cache
646- REBUILD_VIDEO_CACHE = 15
647+ REBUILD_VIDEO_CACHE = 5
648
649 # Require to rebuild feed cache
650- REBUILD_FEED_CACHE = 16
651+ REBUILD_FEED_CACHE = 6
652
653
654=== renamed directory 'entertainerlib/frontend' => 'entertainerlib/client'
655=== modified file 'entertainerlib/client/__init__.py'
656--- entertainerlib/frontend/__init__.py 2009-05-06 03:40:22 +0000
657+++ entertainerlib/client/__init__.py 2010-01-23 03:34:11 +0000
658@@ -1,12 +1,12 @@
659 # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
660-'''Frontend gui to entertainer'''
661+'''Client code for Entertainer.'''
662 # pylint: disable-msg=W0612
663
664 def main(*args, **kwargs):
665- '''Frontend runner'''
666+ '''Client code main loop.'''
667
668 # Import statements are inside the function so that they aren't imported
669- # every time something from the frontend is imported
670+ # every time something from the client is imported
671
672 # cluttergtk must be imported before the first import of clutter so it
673 # must be imported even though pylint complains about it not being used.
674@@ -15,23 +15,23 @@
675 import gobject
676 import gtk
677
678- from entertainerlib.frontend.translation_setup import TranslationSetup
679+ from entertainerlib.client.translation_setup import TranslationSetup
680 TranslationSetup()
681
682 from entertainerlib.backend.backend_server import BackendServer
683- from entertainerlib.utils.configuration import Configuration
684- from entertainerlib.frontend.frontend_client import FrontendClient
685+ from entertainerlib.configuration import Configuration
686+ from entertainerlib.client.client import Client
687
688 gobject.threads_init()
689 gtk.gdk.threads_init()
690 clutter.threads_init()
691
692 config = Configuration()
693- if config.start_auto_server():
694+ if config.start_auto_server:
695 print "Entertainer backend starting..."
696 BackendServer()
697
698- frontend_client = FrontendClient()
699- frontend_client.start()
700+ client_client = Client()
701+ client_client.start()
702
703
704
705=== modified file 'entertainerlib/client/backend_connection.py'
706--- entertainerlib/frontend/backend_connection.py 2009-05-06 02:58:08 +0000
707+++ entertainerlib/client/backend_connection.py 2010-01-23 03:34:11 +0000
708@@ -7,49 +7,21 @@
709 from entertainerlib.backend.core.message_handler import MessageHandler
710
711 class BackendConnection(MessageHandler):
712- """
713- BackendConnection - Connection to the Entertainer backend. Instance from
714- this class is a gate to backend messagebus.
715- """
716+ """Connection to the Entertainer backend messagebus."""
717
718 def __init__(self):
719- """Initialize connection. Connects to backend."""
720- #messages = {
721- # MessageType.CONTENT_CONF_UPDATED : MessagePriority.NORMAL,
722- # MessageType.PREFERENCES_CONF_UPDATED : MessagePriority.NORMAL,
723- # MessageType.FEED_DB_UPDATED : MessagePriority.HIGH }
724- # XXX: rockstar - The messages above are the "real" messages, and I'm
725- # not sure why they are commented out. Anyone?
726 MessageHandler.__init__(self)
727 messages = {}
728 name = "Entertainer Frontend"
729 self.message_bus_proxy = MessageBusProxy(messages, self, name)
730 self.message_bus_proxy.connectToMessageBus()
731 self.message_bus_proxy.start()
732- self.message_bus_proxy.sendMessage(
733- Message(MessageType.FRONTEND_OPENED))
734
735 def close_connection(self):
736 """Close connection to backend"""
737- self.message_bus_proxy.sendMessage(
738- Message(MessageType.FRONTEND_CLOSED))
739 self.message_bus_proxy.disconnectFromMessageBus()
740
741 def request_feed_update(self):
742 """Request backend to fetch all feeds from the Internet."""
743 self.message_bus_proxy.sendMessage(Message(MessageType.UPDATE_FEEDS))
744
745- def request_weather_update(self):
746- """Request backend to update weather information from the Internet."""
747- self.message_bus_proxy.sendMessage(Message(MessageType.UPDATE_WEATHER))
748-
749- # Implements MessageHandler interface
750- def handleMessage(self, message):
751- """Handle received messages. (Implements MessageHandler interface)"""
752- if message.get_type() == MessageType.CONTENT_CONF_UPDATED:
753- pass
754- elif message.get_type() == MessageType.PREFERENCES_CONF_UPDATED:
755- pass
756- elif message.get_type() == MessageType.FEED_DB_UPDATED:
757- pass
758-
759
760=== renamed file 'entertainerlib/frontend/frontend_client.py' => 'entertainerlib/client/client.py'
761--- entertainerlib/frontend/frontend_client.py 2009-05-06 02:58:08 +0000
762+++ entertainerlib/client/client.py 2010-01-23 03:34:11 +0000
763@@ -1,71 +1,65 @@
764 # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
765-'''Entertainer Frontend - This is a client side of Entertainer.'''
766+'''Entertainer client.'''
767
768 import sys
769
770 import gtk
771-
772-from entertainerlib.frontend.backend_connection import BackendConnection
773-from entertainerlib.frontend.gui.user_interface import UserInterface
774-from entertainerlib.frontend.medialibrary.feeds import FeedLibrary
775-from entertainerlib.frontend.medialibrary.music import MusicLibrary
776-from entertainerlib.frontend.medialibrary.images import ImageLibrary
777-from entertainerlib.frontend.medialibrary.videos import VideoLibrary
778-from entertainerlib.utils.configuration import Configuration
779-from entertainerlib.utils.logger import Logger
780-from entertainerlib.utils.system_tray_icon import SystemTrayIcon
781-
782-class FrontendClient:
783- '''
784- Entertainer frontend
785-
786- This is a frontend application of the Entertainer. Frontend is a GUI part
787- that user sees on the screen. This class is a core of the frontend. Frontend
788- connects to the backend's messagebus at startup.
789- '''
790+from twisted.internet import gtk2reactor
791+gtk2reactor.install() # Install the gtk2 reactor before import the real reactor
792+from twisted.internet import reactor
793+from twisted.internet.protocol import ClientCreator
794+from twisted.python.log import startLogging
795+
796+from entertainerlib.client.backend_connection import BackendConnection
797+from entertainerlib.client.medialibrary.feeds import FeedLibrary
798+from entertainerlib.client.medialibrary.music import MusicLibrary
799+from entertainerlib.client.medialibrary.images import ImageLibrary
800+from entertainerlib.client.medialibrary.videos import VideoLibrary
801+from entertainerlib.configuration import Configuration
802+from entertainerlib.gui.user_interface import UserInterface
803+from entertainerlib.gui.system_tray_icon import SystemTrayIcon
804+from entertainerlib.network.local.client import EntertainerLocalClientProtocol
805+
806+
807+class Client:
808+ '''This is a client application of the Entertainer. Entertainer's client
809+ hooks into the server, and then provides a user interface for the data the
810+ server creates.'''
811
812 def __init__(self):
813- '''
814- Create a new frontend.
815-
816- This initializes all frontend stuff like media librarys, remote
817- control receiver and GUI. After this we just wait user actions.
818- '''
819 config = Configuration()
820- self.logger = Logger().getLogger('frontend.FrontendClient')
821- self.backend_connection = self.initialize_backend_connection()
822+ self.backend_connection = BackendConnection()
823 feed_library = FeedLibrary(self.backend_connection)
824- music_library = MusicLibrary(self.backend_connection)
825- image_library = ImageLibrary(self.backend_connection)
826- video_library = VideoLibrary(self.backend_connection)
827+ music_library = MusicLibrary()
828+ image_library = ImageLibrary()
829+ video_library = VideoLibrary()
830 self.ui = UserInterface(
831 feed_library, image_library, music_library, video_library,
832- self.quit_frontend)
833-
834- if config.tray_icon_enabled():
835- SystemTrayIcon(
836- self.quit_frontend, self.toggle_interface_visibility)
837+ self.quit_client)
838+
839+ if config.tray_icon_enabled:
840+ SystemTrayIcon(self.quit_client, self.toggle_interface_visibility)
841+
842+ startLogging(sys.stdout)
843+ client = EntertainerLocalClientProtocol
844+
845+ ClientCreator(reactor, client).connectTCP(
846+ config.network_options['host'],
847+ config.network_options['port'])
848
849 def start(self):
850 '''Start the necessary main loop.'''
851 self.ui.start_up()
852 self.interface_visible = True
853 gtk.gdk.threads_enter()
854- gtk.main()
855+ reactor.run()
856 gtk.gdk.threads_leave()
857
858- def initialize_backend_connection(self):
859- '''Connect to the backend server.'''
860- backend_connection = BackendConnection()
861- self.logger.debug('Connected to the Entertainer backend server.')
862-
863- return backend_connection
864-
865- def quit_frontend(self):
866- '''Clean up the connection to the backend then close the frontend.'''
867+ def quit_client(self):
868+ '''Clean up the connection to the backend then close the client.'''
869 self.backend_connection.close_connection()
870
871- gtk.main_quit()
872+ reactor.stop()
873 sys.exit(0)
874
875 def toggle_interface_visibility(self):
876
877=== modified file 'entertainerlib/client/media_player.py'
878--- entertainerlib/frontend/media_player.py 2009-06-29 19:41:35 +0000
879+++ entertainerlib/client/media_player.py 2010-01-23 03:34:11 +0000
880@@ -9,10 +9,10 @@
881 import gobject
882 import gst
883
884-from entertainerlib.frontend.gui.widgets.motion_buffer import MotionBuffer
885-from entertainerlib.frontend.gui.widgets.texture import Texture
886-from entertainerlib.frontend.medialibrary.playable import Playable
887-from entertainerlib.utils.logger import Logger
888+from entertainerlib.gui.widgets.motion_buffer import MotionBuffer
889+from entertainerlib.gui.widgets.texture import Texture
890+from entertainerlib.client.medialibrary.playable import Playable
891+from entertainerlib.logger import Logger
892
893 class MediaPlayer(gobject.GObject, object):
894 '''
895@@ -72,7 +72,7 @@
896 self.is_playing = False # Is media player currently playing
897 self.is_reactive_allowed = False # Is the video_texture reactive
898
899- self.logger = Logger().getLogger('frontend.MediaPlayer')
900+ self.logger = Logger().getLogger('client.MediaPlayer')
901
902 self._internal_callback_timeout_key = None
903
904@@ -105,7 +105,9 @@
905 self.next()
906 elif message.type == gst.MESSAGE_ERROR:
907 self.video_texture.set_playing(False)
908- self.video_texture.set_property("position", 0)
909+ # XXX: laymansterms - I don't know the implications of removing the
910+ # position property.
911+ #self.video_texture.set_property("position", 0)
912 err, debug = message.parse_error()
913 self.logger.error("Error: %(err)s, %(debug)s" % \
914 {'err': err, 'debug': debug})
915@@ -167,7 +169,9 @@
916 or self.media.get_type() == Playable.VIDEO_STREAM:
917 self.video_texture.set_playing(False)
918 self.video_texture.set_uri(playable.get_uri())
919- self.video_texture.set_property("position", 0)
920+ # XXX: laymansterms - I don't know the implications of removing the
921+ # position property.
922+ #self.video_texture.set_property("position", 0)
923
924 def get_media(self):
925 '''Get URI of the current media stream.'''
926@@ -248,7 +252,9 @@
927 self.stage.set_color(self.bgcolor)
928 self.stage.remove(self.video_texture)
929 self.video_texture.set_playing(False)
930- self.video_texture.set_property("position", 0)
931+ # XXX: laymansterms - I don't know the implications of removing the
932+ # position property.
933+ #self.video_texture.set_property("position", 0)
934 self.emit('stop')
935
936 if self._internal_callback_timeout_key is not None:
937@@ -510,7 +516,7 @@
938 def get_texture(self):
939 '''Get media's texture. This is a video texture or album art texture.'''
940 if self.media.get_type() == Playable.VIDEO_STREAM:
941- return clutter.CloneTexture(self.video_texture)
942+ return clutter.Clone(self.video_texture)
943
944 elif self.media.get_type() == Playable.AUDIO_STREAM:
945 url = self.media.get_album_art_url()
946
947=== modified file 'entertainerlib/client/medialibrary/feeds.py'
948--- entertainerlib/frontend/medialibrary/feeds.py 2009-06-25 19:49:48 +0000
949+++ entertainerlib/client/medialibrary/feeds.py 2010-01-23 03:34:11 +0000
950@@ -3,7 +3,7 @@
951
952 from pysqlite2 import dbapi2 as sqlite
953
954-from entertainerlib.utils.configuration import Configuration
955+from entertainerlib.configuration import Configuration
956
957 class FeedLibrary(object):
958 '''This library can be used to handle RSS feeds in Entertainer.'''
959
960=== modified file 'entertainerlib/client/medialibrary/images.py'
961--- entertainerlib/frontend/medialibrary/images.py 2009-05-06 03:40:22 +0000
962+++ entertainerlib/client/medialibrary/images.py 2010-01-23 03:34:11 +0000
963@@ -4,25 +4,16 @@
964 import os
965 from pysqlite2 import dbapi2 as sqlite
966
967-from entertainerlib.utils.configuration import Configuration
968+from entertainerlib.configuration import Configuration
969
970 class ImageLibrary:
971- """
972- Image library.
973-
974- Entertainer's image cache.
975- """
976-
977- def __init__(self, backend_connection):
978- """
979- Initialize image library
980- @param backend_connection: BackendConnection object
981- """
982+ """Entertainer's image cache."""
983+
984+ def __init__(self):
985 self.config = Configuration()
986
987 if not os.path.exists(self.config.IMAGE_DB):
988 raise Exception("Image database doesn't exist!")
989- self.backend_connection = backend_connection
990
991 def get_all_images(self):
992 """
993
994=== modified file 'entertainerlib/client/medialibrary/music.py'
995--- entertainerlib/frontend/medialibrary/music.py 2009-07-19 19:27:31 +0000
996+++ entertainerlib/client/medialibrary/music.py 2010-01-23 03:34:11 +0000
997@@ -5,10 +5,10 @@
998 import CDDB, DiscID
999 from pysqlite2 import dbapi2 as sqlite
1000
1001-from entertainerlib.utils.configuration import Configuration
1002+from entertainerlib.configuration import Configuration
1003
1004-from entertainerlib.frontend.medialibrary.playable import Playable
1005-from entertainerlib.utils.lyrics_downloader import LyricsDownloader
1006+from entertainerlib.client.medialibrary.playable import Playable
1007+from entertainerlib.download import LyricsDownloader
1008
1009
1010 class MusicLibraryException(Exception):
1011@@ -28,22 +28,13 @@
1012 pass
1013
1014 class MusicLibrary:
1015- """
1016- Music library.
1017-
1018- Interface for Entertainer's music cache.
1019- """
1020-
1021- def __init__(self, backend_connection):
1022- """
1023- Initialize library.
1024- @param backend_connection: BackendConnection object
1025- """
1026+ """Interface for Entertainer's music cache."""
1027+
1028+ def __init__(self):
1029 self.config = Configuration()
1030
1031 if not os.path.exists(self.config.MUSIC_DB):
1032 raise Exception("Music database doesn't exist!")
1033- self.backend_connection = backend_connection
1034 self.db_connection = sqlite.connect(self.config.MUSIC_DB)
1035 self.cursor = self.db_connection.cursor()
1036
1037
1038=== modified file 'entertainerlib/client/medialibrary/videos.py'
1039--- entertainerlib/frontend/medialibrary/videos.py 2009-05-06 02:58:08 +0000
1040+++ entertainerlib/client/medialibrary/videos.py 2010-01-23 03:34:11 +0000
1041@@ -4,28 +4,19 @@
1042 import os
1043 from pysqlite2 import dbapi2 as sqlite
1044
1045-from entertainerlib.utils.configuration import Configuration
1046-from entertainerlib.utils.logger import Logger
1047+from entertainerlib.configuration import Configuration
1048+from entertainerlib.logger import Logger
1049
1050-from entertainerlib.frontend.medialibrary.playable import Playable
1051+from entertainerlib.client.medialibrary.playable import Playable
1052
1053 class VideoLibrary:
1054- """
1055- Video library.
1056-
1057- Interface for Entertainer's video cache.
1058- """
1059-
1060- def __init__(self, backend_connection):
1061- """
1062- Initialize video library
1063- @param backend_connection: BackendConnection object
1064- """
1065+ """Interface for Entertainer's video cache."""
1066+
1067+ def __init__(self):
1068 self.config = Configuration()
1069
1070 if not os.path.exists(self.config.VIDEO_DB):
1071 raise Exception("Video database doesn't exist!")
1072- self.backend_connection = backend_connection
1073
1074 def get_movies(self):
1075 """
1076@@ -133,7 +124,7 @@
1077
1078 #Setting default values
1079 self.logger = Logger().getLogger(
1080- 'frontend.medialibrary.videos.VideoItem')
1081+ 'client.medialibrary.videos.VideoItem')
1082 self.__title = ""
1083 self.__filename = ""
1084 self.__length = 0
1085@@ -220,12 +211,12 @@
1086 else:
1087 self.logger.error("Thumbnail does not exist for " + \
1088 self.get_filename() + ", using default art instead")
1089- return os.path.join(self.config.get_theme_path(),
1090+ return os.path.join(self.config.theme_path,
1091 "images/default_movie_art.png")
1092 else:
1093 self.logger.error("Thumbnail does not exist for " + \
1094 self.get_filename() + ", using default art instead")
1095- return os.path.join(self.config.get_theme_path(),
1096+ return os.path.join(self.config.theme_path,
1097 "images/default_movie_art.png")
1098
1099 def has_thumbnail(self):
1100
1101=== modified file 'entertainerlib/client/translation_setup.py'
1102--- entertainerlib/frontend/translation_setup.py 2009-07-26 07:59:56 +0000
1103+++ entertainerlib/client/translation_setup.py 2010-01-23 03:34:11 +0000
1104@@ -1,38 +1,48 @@
1105 # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
1106 '''Translation Setup Code'''
1107
1108+import locale
1109 import os
1110
1111 import gettext
1112 import gtk
1113-import gtk.glade
1114-
1115-TRANSLATION_SOURCE = {
1116- 'branch' : os.path.abspath(os.path.dirname(__file__) + '/../../locale'),
1117- # Hardcoded path for a package install
1118- 'package' : "/usr/share/locale"
1119- }
1120+from xdg import BaseDirectory
1121
1122 class TranslationSetup:
1123 def __init__(self):
1124+ '''Because of how early translation setup has to occur. This should be
1125+ the only file outside of Configuration that imports xdg.'''
1126+
1127+ def install_locale(locale_dir):
1128+ '''Install locale data from the provided directory.'''
1129+ # This sets up the _ function
1130+ gettext.install('entertainer', localedir=locale_dir,
1131+ unicode=True)
1132+ # Call the C library gettext functions and set the codeset
1133+ # to avoid locale-dependent translation of the message catalog
1134+ locale.bindtextdomain('entertainer', locale_dir)
1135+ locale.bind_textdomain_codeset('entertainer', "UTF-8")
1136+ # XXX: fmarl - setlocale load in current locale properly
1137+ # We can remove it and get feedback from users to see if
1138+ # this hack it's really needed.
1139+ try:
1140+ locale.setlocale(locale.LC_ALL, "")
1141+ except locale.Error, e:
1142+ pass
1143+
1144 # Find locale data from a dev branch if we can
1145- if os.path.exists(TRANSLATION_SOURCE['branch']):
1146- #This setups the _ function
1147- gettext.install('entertainer', TRANSLATION_SOURCE['branch'])
1148-
1149- #This setups the glade translations
1150- gtk.glade.bindtextdomain('entertainer',
1151- TRANSLATION_SOURCE['branch'])
1152- # Install locale data from the hardcoded package path
1153- elif os.path.exists(TRANSLATION_SOURCE['package']):
1154- #This setups the _ function
1155- gettext.install('entertainer', TRANSLATION_SOURCE['package'])
1156-
1157- #This setups the glade translations
1158- gtk.glade.bindtextdomain('entertainer',
1159- TRANSLATION_SOURCE['package'])
1160+ dev_locale = os.path.abspath(os.path.dirname(__file__) +
1161+ '/../../locale')
1162+ if os.path.exists(dev_locale):
1163+ install_locale(dev_locale)
1164+
1165+ # Install locale data from the system location
1166 else:
1167- pass
1168-
1169- gtk.glade.textdomain ('entertainer')
1170-
1171+ system_data_dirs = [data_dir for data_dir in
1172+ BaseDirectory.xdg_data_dirs if not
1173+ data_dir.startswith(BaseDirectory.xdg_data_home)]
1174+ # Since we don't know for certain where the mo files were installed,
1175+ # we try to install from both /usr/share and /usr/local/share.
1176+ for data_dir in system_data_dirs:
1177+ system_locale = os.path.join(data_dir, 'locale')
1178+ install_locale(system_locale)
1179
1180=== renamed file 'entertainerlib/utils/configuration.py' => 'entertainerlib/configuration.py'
1181--- entertainerlib/utils/configuration.py 2009-06-01 09:55:40 +0000
1182+++ entertainerlib/configuration.py 2010-01-23 03:34:11 +0000
1183@@ -2,475 +2,217 @@
1184 '''Configuration - Class represents Entertainer's configuration'''
1185
1186 import os
1187-import sys
1188 import shutil
1189 import ConfigParser
1190-from ConfigParser import ParsingError, NoSectionError, NoOptionError
1191-
1192+from ConfigParser import NoSectionError, NoOptionError
1193+
1194+from xdg import BaseDirectory
1195+
1196+from entertainerlib.backend.core.message_handler import MessageHandler
1197 from entertainerlib.backend.core.message_type_priority import MessageType
1198-from entertainerlib.backend.core.message_handler import MessageHandler
1199-
1200-from entertainerlib.utils.theme import Theme
1201-
1202-SOURCE_CONFIG = {
1203- 'branch' : os.path.abspath(os.path.dirname(__file__) + '/../../cfg'),
1204- # Hardcoded path for a package install
1205- 'package' : "/usr/share/entertainer/cfg"
1206- }
1207+from entertainerlib.db.connection import Database
1208+from entertainerlib.gui.theme import Theme
1209
1210 class Configuration(MessageHandler):
1211- """
1212- Configuration of Entertainer
1213-
1214- This class is an interface to all configuration parameters. All components
1215- of Entertainer should get configuration values through this class. Object
1216- from this class reads current config files and returns values based on
1217- those files. If there are missing lines or other errors in config file,
1218- then object return default values defined in this class.
1219- """
1220-
1221- CFG_DIR = os.path.expanduser("~/.config/entertainer")
1222- TEST_DIR = None
1223-
1224- # This dictionary keeps track of data values that are written to config
1225- # files but need to be returned to a known state while testing.
1226- _tainted = {}
1227-
1228- # This dictionary keeps track of data values that are tainted in memory
1229- # instead of a config file
1230- _tainted_in_memory = {}
1231+ '''Interface to all configuration parameters. All components of Entertainer
1232+ should get configuration values through this class.'''
1233
1234 _shared_state = {}
1235
1236 def __init__(self, test_dir=None):
1237- """
1238- Read configuration files and setup this object.
1239- """
1240 self.__dict__ = self._shared_state
1241 MessageHandler.__init__(self)
1242
1243- if not self._shared_state:
1244- self.TEST_DIR = test_dir
1245-
1246+ if not self._shared_state or test_dir is not None:
1247 # Set in a production mode or a test mode
1248- if self.TEST_DIR is None:
1249- self.cfg_dir = self.CFG_DIR
1250+ if test_dir is None:
1251+ self.resources = Resources()
1252 else:
1253- self.cfg_dir = self.TEST_DIR
1254-
1255- if not os.path.exists(os.path.expanduser(self.cfg_dir)):
1256- self.create_cfg_dir()
1257-
1258- self.ENTERTAINER_LOG = os.path.join(self.cfg_dir,
1259- u'entertainer.log')
1260-
1261- self.FEED_DB = os.path.join(self.cfg_dir, 'cache/feed.db')
1262- self.IMAGE_DB = os.path.join(self.cfg_dir, 'cache/image.db')
1263- self.MUSIC_DB = os.path.join(self.cfg_dir, 'cache/music.db')
1264- self.VIDEO_DB = os.path.join(self.cfg_dir, 'cache/video.db')
1265-
1266- self.THUMB_DIR = os.path.join(self.cfg_dir, 'cache', 'thumbnails')
1267+ self.resources = Resources(config_testing_dir=test_dir)
1268+
1269+ self.cache_dir = self.resources.cache_dir
1270+ self.config_dir = self.resources.config_dir
1271+ self.data_dir = self.resources.data_dir
1272+
1273+ self.LOG = os.path.join(self.cache_dir, u'entertainer.log')
1274+
1275+ self.MEDIA_DB = Database(os.path.join(self.cache_dir, 'media'))
1276+
1277+ self.FEED_DB = os.path.join(self.cache_dir, 'feed.db')
1278+ self.IMAGE_DB = os.path.join(self.cache_dir, 'image.db')
1279+ self.MUSIC_DB = os.path.join(self.cache_dir, 'music.db')
1280+ self.VIDEO_DB = os.path.join(self.cache_dir, 'video.db')
1281+
1282+ self.THUMB_DIR = os.path.join(self.cache_dir, 'thumbnails')
1283 self.IMAGE_THUMB_DIR = os.path.join(self.THUMB_DIR, 'image')
1284 self.VIDEO_THUMB_DIR = os.path.join(self.THUMB_DIR, 'video')
1285- self.ALBUM_ART_DIR = os.path.join(self.cfg_dir, 'cache/album_art')
1286- self.MOVIE_ART_DIR = os.path.join(self.cfg_dir, 'cache/movie_art')
1287-
1288- # Preferences file
1289- self.preferences_conf = os.path.join(self.cfg_dir,
1290- 'preferences.conf')
1291-
1292- # Content file
1293- self.content_conf = os.path.join(self.cfg_dir, 'content.conf')
1294-
1295- self.content_config = ConfigParser.ConfigParser()
1296- self.preferences = ConfigParser.ConfigParser()
1297- try:
1298- self.content_config.readfp(open(self.content_conf))
1299- self.preferences.readfp(open(self.preferences_conf))
1300- except ParsingError:
1301- print("ParsingError with configuration file.")
1302- sys.exit(1)
1303- except IOError:
1304- print("IOError: Couldn't read configuration file.")
1305- sys.exit(1)
1306-
1307- self.theme = Theme(self.get_theme_path())
1308-
1309- self.stage_width = None
1310- self.stage_height = None
1311-
1312- def create_cfg_dir(self):
1313- '''Create a configuration directory and default config files.'''
1314-
1315- def create_cache_hierarchy(config_dir):
1316- '''Create the directories needed for the cache'''
1317- directories = [
1318- 'cache',
1319- 'cache/album_art',
1320- 'cache/movie_art',
1321- 'cache/thumbnails',
1322- 'cache/thumbnails/image',
1323- 'cache/thumbnails/video'
1324- ]
1325-
1326- for directory in directories:
1327- new_dir = os.path.join(config_dir, directory)
1328- os.mkdir(new_dir)
1329-
1330- config_dir = self.get_cfg_dir()
1331- try:
1332- # Copy configuration data from a dev branch if we can
1333- if os.path.exists(SOURCE_CONFIG['branch']):
1334- shutil.copytree(SOURCE_CONFIG['branch'], config_dir)
1335- create_cache_hierarchy(config_dir)
1336- # Install configuration data from the hardcoded package path
1337- elif os.path.exists(SOURCE_CONFIG['package']):
1338- shutil.copytree(SOURCE_CONFIG['package'], config_dir)
1339- create_cache_hierarchy(config_dir)
1340- else:
1341- print "Couldn't find configuration data. Execution aborted."
1342- sys.exit(1)
1343- except OSError:
1344- print "Couldn't copy configuration data to %s. Execution aborted." \
1345- % config_dir
1346- sys.exit(1)
1347+ self.ALBUM_ART_DIR = os.path.join(self.cache_dir, 'album_art')
1348+ self.MOVIE_ART_DIR = os.path.join(self.cache_dir, 'movie_art')
1349+
1350+ self.read_config_file()
1351+
1352+ self.theme = Theme(self.theme_path)
1353+
1354+ self._stage_width = None
1355+ self._stage_height = None
1356+
1357+ # Network options specify the server type and extra options
1358+ self.network_options = {
1359+ 'type': 'local',
1360+ 'host': 'localhost',
1361+ 'port': 55545}
1362+
1363+ def read_config_file(self):
1364+ '''Read in the config file.'''
1365+ self.content_conf = os.path.join(self.config_dir, 'content.conf')
1366+ self.content = ConfigParser.ConfigParser()
1367+ self.content.readfp(open(self.content_conf))
1368
1369 def update_configuration(self):
1370- """
1371- Read configuration files again and update this object.
1372- """
1373- try:
1374- self.content_config.readfp(open(self.content_conf))
1375- self.preferences.readfp(open(self.preferences_conf))
1376- except ParsingError:
1377- raise Exception("Syntax error in configuration file.")
1378- except IOError:
1379- raise IOError("Couldn't read config file.")
1380+ '''Read configuration file again and update this object.'''
1381+ self.content.readfp(open(self.content_conf))
1382
1383- def write_content_value(self, section, option, value, sanitize=False):
1384+ def write_content_value(self, section, option, value):
1385 """Write a new value to the content configuration file."""
1386- if self.TEST_DIR and not sanitize:
1387- self.taint('content', section, option)
1388
1389- try:
1390- self.content_config.set(section, option, value)
1391+ def write_value(section, option, value):
1392+ '''Actually write the value to the section and option.'''
1393+ self.content.set(section, option, value)
1394 cfg_file = file(self.content_conf, 'w')
1395- self.content_config.write(cfg_file)
1396+ self.content.write(cfg_file)
1397+
1398+ try:
1399+ write_value(section, option, value)
1400 except NoSectionError:
1401- raise Exception("No Section to set in content.conf file")
1402+ # Provide an upgrade path to additions of new sections.
1403+ shutil.rmtree(self.config_dir)
1404+ self.resources.create_configuration()
1405+ self.read_config_file()
1406+ write_value(section, option, value)
1407 except NoOptionError:
1408 raise Exception("No Option to set in content.conf file")
1409
1410- def write_preference_value(self, section, option, value, sanitize=False):
1411- """Write a new value to the preferences configuration file."""
1412- if self.TEST_DIR and not sanitize:
1413- self.taint('preferences', section, option)
1414-
1415- try:
1416- self.preferences.set(section, option, value)
1417- cfg_file = file(self.preferences_conf, 'w')
1418- self.preferences.write(cfg_file)
1419- except NoSectionError:
1420- raise Exception("No Section to set in preferences.conf file")
1421- except NoOptionError:
1422- raise Exception("No Option to set in preferences.conf file")
1423-
1424- def get_stage_width(self):
1425- '''Get the stage width from the preferences'''
1426- if self.stage_width:
1427- return self.stage_width
1428-
1429- try:
1430- self.stage_width = self.preferences.getint("General", "stage_width")
1431- except (NoSectionError, NoOptionError):
1432- self.stage_width = 1366 # Default
1433-
1434- return self.stage_width
1435-
1436- def set_stage_width(self, new_width, sanitize=False):
1437- '''Set the stage width for the in memory instance'''
1438- # Taint data if in test mode
1439- if self.TEST_DIR and not sanitize:
1440- kwargs = {'new_width' : self.get_stage_width(),
1441- 'sanitize' : True}
1442- self.taint_in_memory(self.set_stage_width, kwargs)
1443-
1444- self.stage_width = new_width
1445-
1446- def get_stage_height(self):
1447- '''Get the stage height from the preferences'''
1448- if self.stage_height:
1449- return self.stage_height
1450-
1451- try:
1452- self.stage_height = self.preferences.getint("General",
1453- "stage_height")
1454- except (NoSectionError, NoOptionError):
1455- self.stage_height = 768 # Default
1456-
1457- return self.stage_height
1458-
1459- def set_stage_height(self, new_height, sanitize=False):
1460- '''Set the stage height for the in memory instance'''
1461- # Taint data if in test mode
1462- if self.TEST_DIR and not sanitize:
1463- kwargs = {'new_height' : self.get_stage_height(),
1464- 'sanitize' : True}
1465- self.taint_in_memory(self.set_stage_height, kwargs)
1466-
1467- self.stage_height = new_height
1468-
1469- def get_theme_path(self):
1470- """
1471- Get absolute path of the used theme.
1472- @return: string (path)
1473- """
1474- theme_path = os.path.join(self.get_cfg_dir(), 'themes')
1475- try:
1476- theme = self.preferences.get("General", "theme")
1477- except (NoSectionError, NoOptionError):
1478- return os.path.join(theme_path, "Default")
1479-
1480- if os.path.exists(os.path.join(theme_path, theme)):
1481- return os.path.join(theme_path, theme)
1482- else:
1483- return os.path.join(theme_path, "Default")
1484-
1485+ def _get_stage_width(self):
1486+ '''stage_width property getter.'''
1487+ if self._stage_width:
1488+ return self._stage_width
1489+
1490+ self._stage_width = self.content.getint("General", "stage_width")
1491+ return self._stage_width
1492+
1493+ def _set_stage_width(self, new_width):
1494+ '''stage_width property setter.'''
1495+ self._stage_width = new_width
1496+
1497+ stage_width = property(_get_stage_width, _set_stage_width)
1498+
1499+ def _get_stage_height(self):
1500+ '''stage_height property getter.'''
1501+ if self._stage_height:
1502+ return self._stage_height
1503+
1504+ self._stage_height = self.content.getint("General", "stage_height")
1505+ return self._stage_height
1506+
1507+ def _set_stage_height(self, new_height):
1508+ '''stage_height property setter.'''
1509+ self._stage_height = new_height
1510+
1511+ stage_height = property(_get_stage_height, _set_stage_height)
1512+
1513+ @property
1514+ def theme_path(self):
1515+ '''Path to the currently selected theme.'''
1516+ theme_path = os.path.join(self.data_dir, 'themes')
1517+ theme = self.content.get("General", "theme")
1518+ return os.path.join(theme_path, theme)
1519+
1520+ @property
1521 def tray_icon_enabled(self):
1522- """
1523- Display tray icon.
1524- @return: boolean
1525- """
1526- try:
1527- result = self.preferences.getboolean("General", "display_icon")
1528- except (NoSectionError, NoOptionError):
1529- return False
1530- return result
1531-
1532- def get_feed_fetch_interval(self):
1533- """
1534- Get time interval (in minutes) for feed fetching.
1535- @return: Integer
1536- """
1537- try:
1538- result = self.content_config.getint("RSS", "fetch_interval")
1539- except (NoSectionError, NoOptionError):
1540- return 60 # Default is one hour
1541- return result
1542-
1543- def get_port(self):
1544- """
1545- Get backend server's port number.
1546- @return: Integer
1547- """
1548- try:
1549- result = self.preferences.getint("General", "backend_port")
1550- except (NoSectionError, NoOptionError):
1551- return 45054 # Default port
1552- return result
1553-
1554- def get_video_folders(self):
1555- """
1556- Get list of video folders
1557- @return:String Array
1558- """
1559- try:
1560- video_list = self.content_config.get("Videos", "folders")
1561- result = self._is_valid_media_folder(video_list.split(';'))
1562- except (NoSectionError, NoOptionError):
1563- return []
1564- return result
1565-
1566- def get_music_folders(self):
1567- """
1568- Get list of music folders
1569- @return: String Array
1570- """
1571- try:
1572- music_list = self.content_config.get("Music", "folders")
1573- result = music_list.split(';')
1574- except (NoSectionError, NoOptionError):
1575- return []
1576- return result
1577-
1578- def get_image_folders(self):
1579- """
1580- Get list of image folders
1581- @return: String Array
1582- """
1583- try:
1584- image_list = self.content_config.get("Images", "folders")
1585- result = self._is_valid_media_folder(image_list.split(';'))
1586- except (NoSectionError, NoOptionError):
1587- return []
1588- return result
1589-
1590- def get_feeds(self):
1591- """
1592- Get list of feeds.
1593- @return: String Array
1594- """
1595- try:
1596- rss_feeds = self.content_config.get("RSS", "feeds")
1597- result = rss_feeds.split(';')
1598- except (NoSectionError, NoOptionError):
1599- return []
1600- return result
1601-
1602- def get_weather_location(self):
1603- """
1604- Get list of weather location codes.
1605- @return: List of strings
1606- """
1607- try:
1608- location = self.content_config.get("Weather", "location")
1609- except (NoSectionError, NoOptionError):
1610- location = ''
1611- return location
1612-
1613- def display_weather_in_frontend(self):
1614- """
1615- Should we display weather in frontend
1616- @return: Boolean
1617- """
1618- try:
1619- result = self.content_config.getboolean("Weather",
1620- "display_in_menu")
1621- except (NoSectionError, NoOptionError):
1622- return False
1623- return result
1624-
1625- def display_cd_eject_in_frontend(self):
1626- """
1627- Should we display the cd eject button in frontend
1628- @return: Boolean
1629- """
1630- try:
1631- result = self.content_config.getboolean("CD",
1632- "display_eject_in_menu")
1633- except (NoSectionError, NoOptionError):
1634- return False
1635- return result
1636-
1637- def download_video_metadata(self):
1638- """
1639- Get True if video metadata should be downloaded, otherwise False
1640- """
1641- try:
1642- result = self.content_config.getboolean("Videos",
1643- "download_metadata")
1644- except (NoSectionError, NoOptionError):
1645- return False
1646- return result
1647-
1648+ '''Boolean to indicate whether or not to show the tray icon.'''
1649+ return self.content.getboolean("General", "display_icon")
1650+
1651+ @property
1652+ def feed_fetch_interval(self):
1653+ '''Time interval (in minutes) for feed fetching.'''
1654+ return self.content.getint("RSS", "fetch_interval")
1655+
1656+ @property
1657+ def port(self):
1658+ '''Server's port number.'''
1659+ return self.content.getint("General", "backend_port")
1660+
1661+ @property
1662+ def media_folders(self):
1663+ '''Return a list of folders for media.'''
1664+ media = self.content.get("Media", "folders")
1665+ return self._is_valid_media_folder(media.split(';'))
1666+
1667+ @property
1668+ def feeds(self):
1669+ '''List of feeds.'''
1670+ rss_feeds = self.content.get("RSS", "feeds")
1671+ return rss_feeds.split(';')
1672+
1673+ @property
1674+ def weather_location(self):
1675+ '''User's weather location.'''
1676+ return self.content.get("Weather", "location")
1677+
1678+ @property
1679+ def display_weather_in_client(self):
1680+ '''Boolean to tell if weather should be displayed.'''
1681+ return self.content.getboolean("Weather", "display_in_menu")
1682+
1683+ @property
1684+ def download_metadata(self):
1685+ '''Boolean to tell if metadata should be downloaded.'''
1686+ return self.content.getboolean("Media", "download_metadata")
1687+
1688+ @property
1689 def download_album_art(self):
1690- """
1691- Download album art from the Internet.
1692- @return: boolean
1693- """
1694- try:
1695- result = self.content_config.getboolean("Music",
1696- "download_album_art")
1697- except (NoSectionError, NoOptionError):
1698- return False
1699- return result
1700+ '''Boolean to tell if album art should be downloaded.'''
1701+ return self.content.getboolean("Media", "download_album_art")
1702
1703+ @property
1704 def download_lyrics(self):
1705- """
1706- Download song lyrics from the Internet.
1707- @return: boolean
1708- """
1709- try:
1710- result = self.content_config.getboolean("Music", "download_lyrics")
1711- except (NoSectionError, NoOptionError):
1712- return False
1713- return result
1714-
1715- def display_hidden_files_folders(self):
1716- """If True hidden files and folders will be added to image library."""
1717- try:
1718- result = self.content_config.getboolean("Images",
1719- "display_hidden_files_folders")
1720- except (NoSectionError, NoOptionError):
1721- return False
1722- return result
1723-
1724+ '''Boolean to tell if lyrics should be downloaded.'''
1725+ return self.content.getboolean("Media", "download_lyrics")
1726+
1727+ @property
1728 def show_effects(self):
1729- """
1730- Use animations on user interface.
1731- @return: boolean
1732- """
1733- try:
1734- result = self.preferences.getboolean("General", "show_effects")
1735- except (NoSectionError, NoOptionError):
1736- return False
1737- return result
1738+ '''Boolean to tell if effects should be shown.'''
1739+ return self.content.getboolean("General", "show_effects")
1740
1741+ @property
1742 def transition_effect(self):
1743- """
1744- Which transition effect should be used when switching screens.
1745- @return: string (name of the effect)
1746- """
1747- try:
1748- result = self.preferences.get("General", "transition_effect")
1749- except (NoSectionError, NoOptionError):
1750- return False
1751- return result
1752-
1753- def get_theme_name(self):
1754- """
1755- Get the name of the current theme.
1756- @return: string (theme name)
1757- """
1758- try:
1759- result = self.preferences.get("General", "theme")
1760- except (NoSectionError, NoOptionError):
1761- result = "Default"
1762- return result
1763-
1764+ '''Internal name of the user's selected screen transition style.'''
1765+ return self.content.get("General", "transition_effect")
1766+
1767+ @property
1768+ def theme_name(self):
1769+ '''Name of the current theme.'''
1770+ return self.content.get("General", "theme")
1771+
1772+ @property
1773 def start_in_fullscreen(self):
1774 '''Boolean to determine whether to start in fullscreen mode or not.'''
1775- try:
1776- result = self.preferences.getboolean("General",
1777- "start_in_fullscreen")
1778- except (NoSectionError, NoOptionError):
1779- return True
1780- return result
1781+ return self.content.getboolean("General", "start_in_fullscreen")
1782
1783+ @property
1784 def start_auto_server(self):
1785- """
1786- Return True if backend server should auto start
1787- @return: boolean
1788- """
1789- try:
1790- result = self.preferences.getboolean("General",
1791- "start_server_auto")
1792- except (NoSectionError, NoOptionError):
1793- return False
1794- return result
1795+ '''Boolean to tell if the server should be auto started.'''
1796+ return self.content.getboolean("General", "start_server_auto")
1797
1798+ @property
1799 def history_size(self):
1800- """
1801- Get screen history size. This determines how many screen are kept in
1802- memory when scrolling.
1803- @return: Integer
1804- """
1805- try:
1806- result = self.preferences.getint("General", "history_size")
1807- except (NoSectionError, NoOptionError):
1808- return 10
1809- return result
1810+ '''Number of screens to hold in history.'''
1811+ return self.content.getint("General", "history_size")
1812
1813- def get_slideshow_step(self):
1814- """
1815- Get the slideshow step in seconds used by the photo slideshow
1816- @return: Integer
1817- """
1818- try:
1819- result = self.preferences.getint("Photographs", "slideshow_step")
1820- except (NoSectionError, NoOptionError):
1821- return 5
1822- return result
1823+ @property
1824+ def slideshow_step(self):
1825+ '''Amount of seconds before the slideshow steps to the next image.'''
1826+ return self.content.getint("Photographs", "slideshow_step")
1827
1828 # Implements MessageHandler interface
1829 def handleMessage(self, message):
1830@@ -480,73 +222,99 @@
1831 """
1832 if message.get_type() == MessageType.CONTENT_CONF_UPDATED:
1833 self.update_configuration()
1834- elif message.get_type() == MessageType.PREFERENCES_CONF_UPDATED:
1835- self.update_configuration()
1836-
1837- def get_cfg_dir(self):
1838- """
1839- Get the entertainer configuration dir.
1840- @return: strings
1841- """
1842- return self.cfg_dir
1843-
1844- def taint(self, kind, section, option):
1845- '''Taint any data that is written to config files so that it can be
1846- returned to a known state later'''
1847- if kind == 'content':
1848- value = self.content_config.get(section, option)
1849- elif kind == 'preferences':
1850- value = self.preferences.get(section, option)
1851- else:
1852- raise Exception(
1853- "Taint type must be either 'content' or 'preferences'")
1854-
1855- key = "%s %s %s" % (kind, section, option)
1856- self._tainted[key] = value
1857-
1858- def taint_in_memory(self, callback, kwargs):
1859- '''Taint any data that is stored in memory. Callback is used as the
1860- key to a tainted_in_memory dictionary whose stored value is a
1861- dictionary of arguments (kwargs) needed to restore the original
1862- value'''
1863- self._tainted_in_memory[callback] = kwargs
1864-
1865- def sanitize(self):
1866- '''Restore all tainted values to their original state and clear the
1867- tainted dictionary'''
1868- # Sanitize values in config files
1869- for key in self._tainted.keys():
1870- # tokens will contain type, section, and option
1871- tokens = key.split()
1872- if tokens[0] == 'content':
1873- # Write in the sanitize mode so taint checking will be skipped
1874- self.write_content_value(tokens[1], tokens[2],
1875- self._tainted[key], sanitize=True)
1876- elif tokens[0] == 'preferences':
1877- # Write in the sanitize mode so taint checking will be skipped
1878- self.write_preference_value(tokens[1], tokens[2],
1879- self._tainted[key], sanitize=True)
1880-
1881- self.update_configuration()
1882- self._tainted = {}
1883-
1884- # Sanitize values in memory
1885- for callback in self._tainted_in_memory.keys():
1886- kwargs = self._tainted_in_memory[callback]
1887- callback(**kwargs)
1888-
1889- self._tainted_in_memory = {}
1890
1891 def _is_valid_media_folder(self, folder_list):
1892 """Return a folder list where eventual cache folders are removed"""
1893- cache_dir = os.path.join(self.get_cfg_dir(), 'cache')
1894 valid_folder_list = []
1895
1896 for folder in folder_list:
1897- common_prefix = os.path.commonprefix([cache_dir, folder])
1898- if common_prefix != cache_dir:
1899- # folder is not a subfolder of cache_dir we accept it
1900+ common_prefix = os.path.commonprefix([self.cache_dir, folder])
1901+ if common_prefix != self.cache_dir:
1902+ # if folder is not a subfolder of cache_dir, we accept it
1903 valid_folder_list.append(folder)
1904
1905 return valid_folder_list
1906
1907+
1908+class Resources(object):
1909+ '''A Wrapper for the XDG directories. Also handles creation of a new setup
1910+ if the Entertainer directories within the XDG directories are missing. This
1911+ class is meant solely to support Configuration and should NOT be publicly
1912+ used because of testing conflicts that would occur.'''
1913+
1914+ def __init__(self, resource='entertainer', config_testing_dir=None):
1915+ if config_testing_dir is None:
1916+ self.cache_dir = os.path.join(BaseDirectory.xdg_cache_home,
1917+ resource)
1918+ self.config_dir = os.path.join(BaseDirectory.xdg_config_home,
1919+ resource)
1920+ self.data_dir = os.path.join(BaseDirectory.xdg_data_home, resource)
1921+ else:
1922+ # Running in the test suite so don't create the XDG locations.
1923+ self.cache_dir = os.path.join(config_testing_dir, 'cache')
1924+ self.config_dir = os.path.join(config_testing_dir, 'config')
1925+ self.data_dir = os.path.join(config_testing_dir, 'data')
1926+
1927+ # Ensure that the directories exist.
1928+ if not os.path.exists(self.cache_dir):
1929+ self.create_cache_hierarchy()
1930+ if not os.path.exists(self.config_dir):
1931+ self.create_configuration()
1932+ if not os.path.exists(self.data_dir):
1933+ self.create_initial_data()
1934+
1935+ def create_cache_hierarchy(self):
1936+ '''Create the cache hierarchy that is assumed to exist.'''
1937+ os.makedirs(os.path.join(self.cache_dir, 'album_art'))
1938+ os.makedirs(os.path.join(self.cache_dir, 'movie_art'))
1939+ os.makedirs(os.path.join(self.cache_dir, 'thumbnails', 'image'))
1940+ os.makedirs(os.path.join(self.cache_dir, 'thumbnails', 'video'))
1941+
1942+ def create_configuration(self):
1943+ '''Create the user's configuration area and populate with the default
1944+ content configuration.'''
1945+
1946+ # Copy configuration data from a dev branch if we can
1947+ dev_config = os.path.abspath(os.path.dirname(__file__) + '/../cfg')
1948+ if os.path.exists(dev_config):
1949+ shutil.copytree(dev_config, self.config_dir)
1950+ return
1951+
1952+ # Must be a proper installation so install from the system location.
1953+ installed_config = os.path.join(self.installed_data_dir, 'cfg')
1954+ shutil.copytree(installed_config, self.config_dir)
1955+
1956+ def create_initial_data(self):
1957+ '''Create the initial data directory and populate with the default data
1958+ used by Entertainer.'''
1959+ os.mkdir(self.data_dir)
1960+
1961+ # Copy configuration data from a dev branch if we can
1962+ dev_config = os.path.abspath(os.path.dirname(__file__) + '/../themes')
1963+ if os.path.exists(dev_config):
1964+ shutil.copytree(dev_config, os.path.join(self.data_dir, 'themes'))
1965+ return
1966+
1967+ # Must be a proper installation so install from the system location.
1968+ themes_dir = os.path.join(self.installed_data_dir, 'themes')
1969+ shutil.copytree(themes_dir, os.path.join(self.data_dir, 'themes'))
1970+
1971+ @property
1972+ def installed_data_dir(self):
1973+ '''Since different distros decide on the install path differently, scan
1974+ through the system data directories to find where Entertainer was
1975+ installed.'''
1976+
1977+ # Get rid of the user's home data directory because we don't want it.
1978+ system_data_dirs = [data_dir for data_dir in BaseDirectory.xdg_data_dirs
1979+ if not data_dir.startswith(BaseDirectory.xdg_data_home)]
1980+
1981+ installed_data_dir = None
1982+ for directory in system_data_dirs:
1983+ possible_data_dir = os.path.join(directory, 'entertainer')
1984+ if os.path.exists(possible_data_dir):
1985+ installed_data_dir = possible_data_dir
1986+ break
1987+
1988+ return installed_data_dir
1989+
1990
1991=== renamed directory 'entertainerlib/backend/core/db' => 'entertainerlib/db'
1992=== modified file 'entertainerlib/db/connection.py'
1993--- entertainerlib/backend/core/db/connection.py 2009-05-11 23:37:27 +0000
1994+++ entertainerlib/db/connection.py 2010-01-23 03:34:11 +0000
1995@@ -27,7 +27,7 @@
1996 'PhotoImage': '''CREATE TABLE `photoimage` (
1997 id INTEGER PRIMARY KEY AUTOINCREMENT,
1998 filename TEXT,
1999- thumb VARCHAR(32),
2000+ thumbnail VARCHAR(32),
2001 title TEXT,
2002 description TEXT,
2003 creation_date DATETIME,
2004
2005=== modified file 'entertainerlib/db/models.py'
2006--- entertainerlib/backend/core/db/models.py 2009-04-28 23:54:49 +0000
2007+++ entertainerlib/db/models.py 2010-01-23 03:34:11 +0000
2008@@ -6,6 +6,7 @@
2009 from storm.properties import Bool, DateTime, Int, Unicode
2010 from storm.references import Reference, ReferenceSet
2011
2012+
2013 class BaseModel(Storm):
2014 '''Abstract class from which all Entertainer models inherit.'''
2015
2016@@ -96,7 +97,7 @@
2017 __storm_table__ = 'photoimage'
2018 id = Int(primary=True)
2019 filename = Unicode()
2020- thumb = Unicode()
2021+ thumbnail = Unicode()
2022 title = Unicode()
2023 description = Unicode()
2024 creation_date = DateTime()
2025@@ -108,7 +109,7 @@
2026 ret = {
2027 'id': self.id,
2028 'filename': self.filename,
2029- 'thumb': self.thumb,
2030+ 'thumbnail': self.thumbnail,
2031 'title': self.title,
2032 'description': self.description,
2033 'creation_date': self.creation_date,
2034
2035=== added file 'entertainerlib/dialog.py'
2036--- entertainerlib/dialog.py 1970-01-01 00:00:00 +0000
2037+++ entertainerlib/dialog.py 2010-01-23 03:34:11 +0000
2038@@ -0,0 +1,1113 @@
2039+# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
2040+'''Dialogs for Entertainer.'''
2041+# pylint: disable-msg=C0302
2042+
2043+import os
2044+import shutil
2045+import socket
2046+import sys
2047+import tarfile
2048+
2049+import gtk
2050+
2051+from entertainerlib.backend.core.message import Message
2052+from entertainerlib.backend.core.message_bus_proxy import MessageBusProxy
2053+from entertainerlib.backend.core.message_type_priority import MessageType
2054+from entertainerlib.configuration import Configuration
2055+from entertainerlib.logger import Logger
2056+from entertainerlib.gui.theme import Theme
2057+from entertainerlib.backend.components.feeds.feed_utils import (OPMLParser,
2058+ FeedConfigTools)
2059+from entertainerlib.weather import Weather
2060+
2061+
2062+class ManagerDialog:
2063+ """
2064+ This is a content management tool for Entertainer media center application.
2065+ """
2066+
2067+ # Temporary storage for entered URL
2068+ url = ""
2069+ UI_DIR = os.path.join(os.path.dirname(__file__), "uis")
2070+
2071+ def __init__(self, stand_alone):
2072+ """
2073+ Initialize content management dialog
2074+ @param stand_alone: Boolean, Is this dialog running as a stand alone
2075+ process
2076+ """
2077+ self.stand_alone = stand_alone
2078+ self.config = Configuration()
2079+ self.themes = []
2080+ self.weather = Weather()
2081+
2082+ # Load UI with gtk.Builder
2083+ uifile = os.path.join(self.UI_DIR, 'manager.ui')
2084+ self.builder = gtk.Builder()
2085+ self.builder.set_translation_domain('entertainer')
2086+ self.builder.add_from_file(uifile)
2087+
2088+ # Get content management dialog and bind signal callbacks
2089+ self.dialog = self.builder.get_object("ManagerDialog")
2090+ if (self.dialog):
2091+ callback_dic = {
2092+ # Dialog-wide callbacks
2093+ "on_button_open_list_clicked" :
2094+ self.on_button_open_list_clicked,
2095+ "on_close_button_clicked" : self.on_close_button_clicked,
2096+ "on_ManagerDialog_destroy" : self.on_dialog_closed,
2097+
2098+ # Media tab
2099+ "on_button_remove_media_clicked" :
2100+ self.on_button_remove_media_clicked,
2101+ "on_button_add_media_clicked" :
2102+ self.on_button_add_media_clicked,
2103+ "on_button_edit_media_clicked" :
2104+ self.on_button_edit_media_clicked,
2105+ "on_checkbutton_video_metadata_toggled" :
2106+ self.on_checkbutton_video_metadata_toggled,
2107+ "on_lyrics_checkbox_toggled" : self.on_lyrics_checkbox_toggled,
2108+ "on_art_checkbox_toggled" : self.on_art_checkbox_toggled,
2109+ "on_button_media_rebuild_clicked" :
2110+ self.on_button_media_rebuild_clicked,
2111+
2112+ # Feed tab
2113+ "on_button_add_feed_clicked" :
2114+ self.on_button_add_feed_clicked,
2115+ "on_button_remove_feed_clicked" :
2116+ self.on_button_remove_feed_clicked,
2117+ "on_button_edit_feed_clicked" :
2118+ self.on_button_edit_feed_clicked,
2119+ "on_fetch_interval_spinbutton_value_changed" :
2120+ self.on_fetch_interval_spinbutton_value_changed,
2121+ "on_url_dialog_delete_event" : self.on_url_dialog_delete_event,
2122+ "on_url_dialog_ok_button_clicked" :
2123+ self.on_url_dialog_ok_button_clicked,
2124+ "on_url_dialog_cancel_button_clicked" :
2125+ self.on_url_dialog_cancel_button_clicked,
2126+ "on_button_feed_rebuild_clicked" :
2127+ self.on_button_feed_rebuild_clicked,
2128+
2129+ # Weather tab
2130+ "on_button_add_weather_clicked" :
2131+ self.on_button_add_weather_clicked,
2132+ "on_button_remove_weather_clicked" :
2133+ self.on_button_remove_weather_clicked,
2134+ "on_weather_display_checkbox_toggled" :
2135+ self.on_weather_display_checkbox_toggled,
2136+ "on_location_find_button_clicked" :
2137+ self.on_location_find_button_clicked,
2138+ "on_location_cancel_button_clicked" :
2139+ self.on_location_cancel_button_clicked,
2140+ "on_location_add_button_clicked" :
2141+ self.on_location_add_button_clicked,
2142+ "on_location_entry_activate" : self.on_location_entry_activate,
2143+
2144+ # User Interface tab
2145+ "on_theme_list_cursor_changed" :
2146+ self.on_theme_list_cursor_changed,
2147+ "on_theme_add_button_clicked" :
2148+ self.on_theme_add_button_clicked,
2149+ "on_theme_remove_button_clicked" :
2150+ self.on_theme_remove_button_clicked,
2151+ "on_checkbutton_effects_toggled" :
2152+ self.on_checkbutton_effects_toggled,
2153+ "on_combobox_effects_changed" :
2154+ self.on_combobox_effects_changed,
2155+
2156+ # General tab
2157+ "on_checkbutton_fullscreen_toggled" :
2158+ self.on_checkbutton_fullscreen_toggled,
2159+ "on_checkbutton_autostart_toggled" :
2160+ self.on_checkbutton_autostart_toggled,
2161+ "on_checkbutton_systray_icon_toggled" :
2162+ self.on_checkbutton_systray_icon_toggled,
2163+ "on_spinbutton_slideshow_step_value_changed":
2164+ self.on_spinbutton_slideshow_step_value_changed
2165+ }
2166+
2167+ self.builder.connect_signals(callback_dic)
2168+
2169+ # Initialize dialog widgets with correct values and show dialog
2170+ self.init_dialog_values_from_configure_file()
2171+ self.dialog.resize(500, 300)
2172+ self.dialog.show()
2173+
2174+ # Initialize location list in search dialog
2175+ result_list = self.builder.get_object("location_results_treeview")
2176+ store = gtk.ListStore(str)
2177+ result_list.set_model(store)
2178+ cell_renderer = gtk.CellRendererText()
2179+ column = gtk.TreeViewColumn(_("Location"), cell_renderer, text=0)
2180+ result_list.append_column(column)
2181+
2182+ def on_dialog_closed(self, widget):
2183+ """Callback function for dialog's close button"""
2184+ try:
2185+ proxy = MessageBusProxy(client_name = "Manager GUI")
2186+ proxy.connectToMessageBus()
2187+ proxy.sendMessage(Message(MessageType.CONTENT_CONF_UPDATED))
2188+ proxy.disconnectFromMessageBus()
2189+ except socket.error:
2190+ error = gtk.MessageDialog(
2191+ None, gtk.DIALOG_MODAL,
2192+ gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _(
2193+ "Entertainer backend is not running. "
2194+ "Cache cannot be rebuilt."
2195+ ))
2196+ error.run()
2197+ error.destroy()
2198+
2199+ if(self.stand_alone):
2200+ self.dialog.hide()
2201+ self.dialog.destroy()
2202+ gtk.main_quit()
2203+ else:
2204+ self.dialog.hide()
2205+ self.dialog.destroy()
2206+
2207+ def on_close_button_clicked(self, widget):
2208+ """Callback function for dialog's close button"""
2209+ if(self.stand_alone):
2210+ self.dialog.hide()
2211+ self.dialog.destroy()
2212+ gtk.main_quit()
2213+ else:
2214+ self.dialog.hide()
2215+ self.dialog.destroy()
2216+
2217+ def on_button_add_media_clicked(self, widget):
2218+ """Opens add URL dialog. """
2219+ widget = self.builder.get_object("treeview_media")
2220+ model = widget.get_model()
2221+ # Open "Select folder" dialog
2222+ dialog = gtk.FileChooserDialog(_("Select folder"), None,
2223+ gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
2224+ (gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,
2225+ gtk.STOCK_OPEN,gtk.RESPONSE_OK),
2226+ None)
2227+ status = dialog.run()
2228+ # If folder was selected we add it to model and update config file
2229+ if(status == gtk.RESPONSE_OK):
2230+ self.add_to_model_and_config(dialog.get_current_folder(), model,
2231+ self.media_folders, "Media")
2232+ dialog.destroy()
2233+
2234+ def on_button_remove_media_clicked(self, widget):
2235+ """Remove currently selected folder from media folders"""
2236+ widget = self.builder.get_object("treeview_media")
2237+ model = widget.get_model()
2238+ selection = widget.get_selection().get_selected()
2239+ if selection[1] == None:
2240+ return
2241+ rm_folder = model.get_value(selection[1], 0)
2242+ self.media_folders.remove(rm_folder)
2243+ str_folders = ";".join(self.media_folders)
2244+ self.config.write_content_value("Media", "folders", str_folders)
2245+ model.remove(selection[1])
2246+
2247+ def on_button_edit_media_clicked(self, widget):
2248+ """Edit currently selected folder"""
2249+ widget = self.builder.get_object("treeview_media")
2250+ url_dialog = self.builder.get_object("url_dialog")
2251+ url_entry = self.builder.get_object("url_entry")
2252+ model = widget.get_model()
2253+ selection = widget.get_selection().get_selected()
2254+ if selection[1] == None:
2255+ return
2256+ folder = model.get_value(selection[1], 0)
2257+ url_entry.set_text(folder)
2258+ url_dialog.set_title(_("Edit URL"))
2259+ status = url_dialog.run()
2260+ if status == gtk.RESPONSE_OK and os.path.exists(self.url):
2261+ # Update list model
2262+ model.set_value(selection[1], 0, self.url)
2263+ # Update configure file
2264+ pos = self.media_folders.index(folder)
2265+ self.media_folders.remove(folder)
2266+ self.media_folders.insert(pos, self.url)
2267+ str_folders = ";".join(self.media_folders)
2268+ self.config.write_content_value("Media", "folders",
2269+ str_folders)
2270+
2271+ def on_checkbutton_autostart_toggled(self, widget):
2272+ '''Server autostart checkbox toggled.'''
2273+ self.config.write_content_value("General", "start_server_auto",
2274+ widget.get_active())
2275+
2276+ def on_checkbutton_fullscreen_toggled(self, widget):
2277+ '''Start in fullscreen checkbox toggled.'''
2278+ self.config.write_content_value("General", "start_in_fullscreen",
2279+ widget.get_active())
2280+
2281+ def on_checkbutton_systray_icon_toggled(self, widget):
2282+ """System Tray Icon checkbox toggled"""
2283+ self.config.write_content_value("General", "display_icon",
2284+ widget.get_active())
2285+
2286+ def on_checkbutton_video_metadata_toggled(self, widget):
2287+ """
2288+ Download video file metadata from internet
2289+ @param widget: GTK-Widget
2290+ """
2291+ self.config.write_content_value("Media", "download_metadata",
2292+ widget.get_active())
2293+
2294+ def on_button_add_feed_clicked(self, widget):
2295+ """Opens add feed dialog. """
2296+ widget = self.builder.get_object("treeview_feeds")
2297+ url_dialog = self.builder.get_object("url_dialog")
2298+ model = widget.get_model()
2299+ # Open dialog
2300+ url_dialog.set_title(_("Add RSS-feed"))
2301+ status = url_dialog.run()
2302+ # If folder was selected we add it to model and update config file
2303+ if(status == gtk.RESPONSE_OK):
2304+ model.append([self.url])
2305+ self.feeds.append(self.url)
2306+ str_folders = ";".join(self.feeds)
2307+ self.config.write_content_value("RSS", "feeds", str_folders)
2308+
2309+ def on_button_remove_feed_clicked(self, widget):
2310+ """Remove currently selected reed from RSS-feeds"""
2311+ widget = self.builder.get_object("treeview_feeds")
2312+ model = widget.get_model()
2313+ selection = widget.get_selection().get_selected()
2314+ if selection[1] == None:
2315+ return
2316+ rm_folder = model.get_value(selection[1], 0)
2317+ self.feeds.remove(rm_folder)
2318+ str_folders = ";".join(self.feeds)
2319+ self.config.write_content_value("RSS", "feeds", str_folders)
2320+ model.remove(selection[1])
2321+
2322+ def on_button_edit_feed_clicked(self, widget):
2323+ """Edit currently selected feed"""
2324+ widget = self.builder.get_object("treeview_feeds")
2325+ url_dialog = self.builder.get_object("url_dialog")
2326+ url_entry = self.builder.get_object("url_entry")
2327+ model = widget.get_model()
2328+ selection = widget.get_selection().get_selected()
2329+ if selection[1] == None:
2330+ return
2331+ feed = model.get_value(selection[1], 0)
2332+ url_entry.set_text(feed)
2333+ url_dialog.set_title(_("Edit feed"))
2334+ status = url_dialog.run()
2335+ if status == gtk.RESPONSE_OK:
2336+ # Update list model
2337+ model.set_value(selection[1], 0, self.url)
2338+ # Update configure file
2339+ pos = self.feeds.index(feed)
2340+ self.feeds.remove(feed)
2341+ self.feeds.insert(pos, self.url)
2342+ str_feeds = ";".join(self.feeds)
2343+ self.config.write_content_value("RSS", "feeds", str_feeds)
2344+
2345+ def on_button_open_list_clicked(self, widget):
2346+ """Opens the open feed source dialog"""
2347+ open_dialog = OpenFeedSourceDialog(
2348+ self.builder.get_object("treeview_feeds"), self.feeds)
2349+ open_dialog.dialog.connect("destroy",
2350+ open_dialog.on_closeButton_clicked)
2351+
2352+ def on_fetch_interval_spinbutton_value_changed(self, widget):
2353+ self.config.write_content_value("RSS", "fetch_interval",
2354+ widget.get_value_as_int())
2355+
2356+ def on_spinbutton_slideshow_step_value_changed(self, widget):
2357+ """Activation of slideshow effects"""
2358+ self.config.write_content_value("Photographs", "slideshow_step",
2359+ int(widget.get_value()))
2360+
2361+ def on_lyrics_checkbox_toggled(self, widget):
2362+ self.config.write_content_value("Media", "download_lyrics",
2363+ widget.get_active())
2364+
2365+ def on_art_checkbox_toggled(self, widget):
2366+ self.config.write_content_value("Media", "download_album_art",
2367+ widget.get_active())
2368+
2369+ def on_url_dialog_ok_button_clicked(self, widget):
2370+ """URL dialog OK button pressed. Sets self.url"""
2371+ url_dialog = self.builder.get_object("url_dialog")
2372+ url_entry = self.builder.get_object("url_entry")
2373+ url_dialog.hide()
2374+ self.url = url_entry.get_text()
2375+ url_entry.set_text("")
2376+ url_dialog.response(gtk.RESPONSE_OK)
2377+
2378+ def on_url_dialog_cancel_button_clicked(self, widget):
2379+ """URL dialog cancelled. Hides dialog"""
2380+ url_dialog = self.builder.get_object("url_dialog")
2381+ url_entry = self.builder.get_object("url_entry")
2382+ url_dialog.hide()
2383+ url_entry.set_text("")
2384+ url_dialog.response(gtk.RESPONSE_CANCEL)
2385+
2386+ def on_url_dialog_delete_event(self, widget, data):
2387+ """Dialog's X clicked. Hides dialog"""
2388+ url_dialog = self.builder.get_object("url_dialog")
2389+ url_entry = self.builder.get_object("url_entry")
2390+ url_dialog.hide()
2391+ url_entry.set_text("")
2392+ url_dialog.response(gtk.RESPONSE_CANCEL)
2393+ return True
2394+
2395+ def on_button_add_weather_clicked(self, widget):
2396+ """
2397+ Open location search dialog
2398+ @param widget: GTK-Widget
2399+ """
2400+ location_dialog = self.builder.get_object("weather_search_dialog")
2401+ location_dialog.set_title(_("Add location"))
2402+
2403+ # Clear results
2404+ result_list = self.builder.get_object("location_results_treeview")
2405+ model = result_list.get_model()
2406+ model.clear()
2407+
2408+ status = location_dialog.run()
2409+ if(status == gtk.RESPONSE_OK):
2410+ print "Added"
2411+
2412+ def on_button_remove_weather_clicked(self, widget):
2413+ """
2414+ Remove currently selected weather location from the location list
2415+ @param widget: GTK-Widget
2416+ """
2417+ widget = self.builder.get_object("treeview_locations")
2418+ model = widget.get_model()
2419+ self.weather_locations = []
2420+ str_folders = ""
2421+ self.config.write_content_value("Weather", "location", str_folders)
2422+ model.clear()
2423+
2424+ def on_weather_display_checkbox_toggled(self, widget):
2425+ """
2426+ Checkbox that defines should we use weather conditions
2427+ @param widget: GTK-Widget
2428+ """
2429+ self.config.write_content_value("Weather", "display_in_menu",
2430+ widget.get_active())
2431+ if widget.get_active():
2432+ self.builder.get_object("button_add_weather").set_sensitive(True)
2433+ self.builder.get_object(
2434+ "button_remove_weather").set_sensitive(True)
2435+ self.builder.get_object("treeview_locations").set_sensitive(True)
2436+ else:
2437+ self.builder.get_object("button_add_weather").set_sensitive(False)
2438+ self.builder.get_object(
2439+ "button_remove_weather").set_sensitive(False)
2440+ self.builder.get_object("treeview_locations").set_sensitive(False)
2441+
2442+ def on_location_find_button_clicked(self, widget):
2443+ """
2444+ Find location by search string
2445+ @param widget: GTK-Widget
2446+ """
2447+ add_button = self.builder.get_object("location_add_button")
2448+ search_term = self.builder.get_object("location_entry").get_text()
2449+ result_list = self.builder.get_object("location_results_treeview")
2450+ model = result_list.get_model()
2451+ model.clear()
2452+ if search_term != "":
2453+ self.weather.location = search_term
2454+ self.weather.refresh()
2455+ results = self.weather.forecasts
2456+ if len(results) > 0:
2457+ add_button.set_sensitive(True)
2458+ model.append([search_term])
2459+ result_list.set_cursor(0)
2460+ else:
2461+ model.clear()
2462+ model.append([_("Location Not Found!")])
2463+ add_button.set_sensitive(False)
2464+
2465+ def on_location_cancel_button_clicked(self, widget):
2466+ """
2467+ Close location search dialog without taking any actions.0
2468+ @param widget: GTK-Widget
2469+ """
2470+ location_dialog = self.builder.get_object("weather_search_dialog")
2471+ location_entry = self.builder.get_object("location_entry")
2472+ location_dialog.hide()
2473+ location_entry.set_text("")
2474+ location_dialog.response(gtk.RESPONSE_CANCEL)
2475+
2476+ def on_location_add_button_clicked(self, widget):
2477+ """
2478+ Add selected location to location list and close search dialog
2479+ @param widget: GTK-Widget
2480+ """
2481+ self.weather_locations = []
2482+ result_list = self.builder.get_object("location_results_treeview")
2483+ model = result_list.get_model()
2484+ selection = result_list.get_selection().get_selected()
2485+ if selection[1] == None:
2486+ return
2487+ location_string = model.get_value(selection[1], 0)
2488+
2489+ location_list = self.builder.get_object("treeview_locations")
2490+ loc_model = location_list.get_model()
2491+ loc_model.clear()
2492+ loc_model.append([location_string])
2493+
2494+ self.weather_locations.append(location_string)
2495+ str_locations = ";".join(self.weather_locations)
2496+ self.config.write_content_value("Weather", "location", str_locations)
2497+
2498+ location_dialog = self.builder.get_object("weather_search_dialog")
2499+ location_entry = self.builder.get_object("location_entry")
2500+ location_dialog.hide()
2501+ location_entry.set_text("")
2502+ location_dialog.response(gtk.RESPONSE_CANCEL)
2503+
2504+ def on_location_entry_activate(self, widget):
2505+ """
2506+ User hit enter on location entry to start search
2507+ @param widget: GTK-Widget
2508+ """
2509+ self.on_location_find_button_clicked(widget)
2510+
2511+ def on_button_media_rebuild_clicked(self, widget):
2512+ '''Rebuild media cache requested.'''
2513+ try:
2514+ proxy = MessageBusProxy(client_name = "Manager GUI")
2515+ proxy.connectToMessageBus()
2516+ proxy.sendMessage(Message(MessageType.REBUILD_IMAGE_CACHE))
2517+ proxy.sendMessage(Message(MessageType.REBUILD_MUSIC_CACHE))
2518+ proxy.sendMessage(Message(MessageType.REBUILD_VIDEO_CACHE))
2519+ proxy.disconnectFromMessageBus()
2520+ except socket.error:
2521+ error = gtk.MessageDialog(
2522+ None, gtk.DIALOG_MODAL,
2523+ gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _(
2524+ "Entertainer backend is not running. "
2525+ "Cache cannot be rebuilt."
2526+ ))
2527+ error.run()
2528+ error.destroy()
2529+
2530+ def on_button_feed_rebuild_clicked(self, widget):
2531+ """
2532+ Rebuild feed cache requested
2533+ @param widget: GTK-Widget
2534+ """
2535+ #We need the user to confirm the rebuild feed cache request
2536+ dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING,
2537+ gtk.BUTTONS_OK_CANCEL,
2538+ _("This will completely remove any feed entries in the cache!"))
2539+ status = dialog.run()
2540+ #If user has ok'd the request send the message to the message bus
2541+ if(status == gtk.RESPONSE_OK):
2542+ try:
2543+ proxy = MessageBusProxy(client_name = "Manager GUI")
2544+ proxy.connectToMessageBus()
2545+ proxy.sendMessage(Message(MessageType.REBUILD_FEED_CACHE))
2546+ proxy.disconnectFromMessageBus()
2547+ except socket.error:
2548+ error = gtk.MessageDialog(
2549+ None, gtk.DIALOG_MODAL,
2550+ gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _(
2551+ "Entertainer backend is not running. "
2552+ "Cache cannot be rebuilt."
2553+ ))
2554+ error.run()
2555+ error.destroy()
2556+
2557+ dialog.destroy()
2558+
2559+ def on_theme_add_button_clicked(self, widget):
2560+ """Add theme button clicked"""
2561+ themelist = self.builder.get_object("theme_list")
2562+ model = themelist.get_model()
2563+ # Open "Select folder" dialog
2564+ dialog = gtk.FileChooserDialog(_("Select theme package file"),
2565+ None, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL,
2566+ gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK), None)
2567+ file_filter = gtk.FileFilter()
2568+ file_filter.set_name(_("Theme package (tar.gz)"))
2569+ file_filter.add_pattern("*.tar.gz")
2570+ dialog.add_filter(file_filter)
2571+ status = dialog.run()
2572+
2573+ # If theme was selected with file chooser
2574+ if(status == gtk.RESPONSE_OK):
2575+ package = dialog.get_filename()
2576+ tar = tarfile.open(package, 'r:gz') # Open tar.gz package
2577+
2578+ # Make sure that package contains configuration file (is theme)
2579+ content = tar.getnames()
2580+ theme_name = None
2581+ is_theme = False
2582+ for element in content:
2583+ if element[-10:] == "theme.conf":
2584+ theme_name = element[:-11]
2585+ is_theme = True
2586+
2587+ # Install them
2588+ if is_theme:
2589+ tar.extractall(os.path.join(self.config.data_dir, 'themes'))
2590+ model.insert(len(model), [theme_name])
2591+ else:
2592+ error = gtk.MessageDialog(None, gtk.DIALOG_MODAL,
2593+ gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("Invalid theme file!"))
2594+ error.run()
2595+ error.destroy()
2596+
2597+ dialog.destroy()
2598+
2599+ def on_theme_list_cursor_changed(self, widget):
2600+ """Executed when theme is changed in theme list. Update preview."""
2601+ # Get currently selected theme
2602+ themelist = self.builder.get_object("theme_list")
2603+ model = themelist.get_model()
2604+ selection = themelist.get_selection().get_selected()
2605+ name = model.get_value(selection[1], 0)
2606+ themedir = os.path.join(self.config.data_dir, 'themes', name)
2607+ theme = Theme(theme_path=themedir)
2608+
2609+ # Update preview
2610+ image = self.builder.get_object("theme_image")
2611+ image.set_from_file(os.path.join(themedir, "thumbnail.png"))
2612+ name = self.builder.get_object("name_label")
2613+ name.set_text(theme.getName())
2614+ author = self.builder.get_object("author_label")
2615+ author.set_text(theme.getAuthor())
2616+ license_label = self.builder.get_object("license_label")
2617+ license_label.set_text(theme.getLicence())
2618+ copyright_label = self.builder.get_object("copyright_label")
2619+ copyright_label.set_text(theme.getCopyright())
2620+ comment = self.builder.get_object("comment_label")
2621+ comment.set_text(theme.getComment())
2622+
2623+ self.config.write_content_value("General", "theme", name.get_text())
2624+
2625+ def on_theme_remove_button_clicked(self, widget):
2626+ """Remove theme button clicked"""
2627+ # Get currently selected theme
2628+ themelist = self.builder.get_object("theme_list")
2629+ model = themelist.get_model()
2630+ selection = themelist.get_selection().get_selected()
2631+ name = model.get_value(selection[1], 0)
2632+
2633+ confirm = gtk.MessageDialog(None,
2634+ gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO,
2635+ _("Are you sure you want to delete\ntheme: %(name)s") % \
2636+ {'name': name})
2637+ status = confirm.run()
2638+ confirm.destroy()
2639+ if(status == gtk.RESPONSE_YES):
2640+ themedir = os.path.join(self.config.data_dir, 'themes', name)
2641+ shutil.rmtree(themedir)
2642+ model.remove(selection[1])
2643+ themelist.set_cursor(0)
2644+ self.themes.remove(name)
2645+
2646+ def on_checkbutton_effects_toggled(self, widget):
2647+ """Effect checkbox toggled"""
2648+ combobox = self.builder.get_object("combobox_effects")
2649+ combobox.set_sensitive(widget.get_active())
2650+ self.config.write_content_value("General", "show_effects",
2651+ widget.get_active())
2652+
2653+ def on_combobox_effects_changed(self, widget):
2654+ """User changed effect for screen transitions"""
2655+ text = widget.get_active_text()
2656+ if text == _("No effect"):
2657+ english_text = "No effect"
2658+ if text == _("Crossfade"):
2659+ english_text = "Crossfade"
2660+ if text == _("Zoom and fade"):
2661+ english_text = "Zoom and fade"
2662+ if text == _("Slide"):
2663+ english_text = "Slide"
2664+ self.config.write_content_value("General", "transition_effect",
2665+ english_text)
2666+
2667+ def init_dialog_values_from_configure_file(self):
2668+ """Read configuration and set dialog widget values with read values.
2669+ """
2670+ # == Videos ==
2671+ medialist_widget = self.builder.get_object("treeview_media")
2672+ mediastore = gtk.ListStore(str)
2673+
2674+ cell_renderer = gtk.CellRendererText()
2675+ column = gtk.TreeViewColumn(_("Folders"), cell_renderer, text=0)
2676+ medialist_widget.append_column(column)
2677+
2678+ self.media_folders = self.config.media_folders
2679+
2680+ # Fill model with folders read from config file
2681+ self.init_model(mediastore, self.media_folders)
2682+
2683+ medialist_widget.set_model(mediastore)
2684+
2685+ # Checkboxes
2686+ metadata_checkbox = self.builder.get_object("video_metadata_checkbox")
2687+ metadata_checkbox.set_active(self.config.download_metadata)
2688+
2689+ art_checkbox = self.builder.get_object("art_checkbox")
2690+ art_checkbox.set_active(self.config.download_album_art)
2691+
2692+ lyrics_checkbox = self.builder.get_object("lyrics_checkbox")
2693+ lyrics_checkbox.set_active(self.config.download_lyrics)
2694+
2695+ # == RSS-feeds ==
2696+ feedlist_widget = self.builder.get_object("treeview_feeds")
2697+ feed_model = gtk.ListStore(str)
2698+
2699+ rss_cell = gtk.CellRendererText()
2700+ rss_column = gtk.TreeViewColumn(_("Feeds"), rss_cell, text=0)
2701+ feedlist_widget.append_column(rss_column)
2702+
2703+ self.feeds = self.config.feeds
2704+
2705+ # Fill model with folders read from config file
2706+ for i in range(len(self.feeds)):
2707+ feed_model.insert(i, [self.feeds[i]])
2708+
2709+ feedlist_widget.set_model(feed_model)
2710+
2711+ # Interval spinner
2712+ interval_spinner = self.builder.get_object("fetch_interval_spinbutton")
2713+ interval_val = self.config.feed_fetch_interval
2714+ if interval_val < 15:
2715+ interval_val = 15
2716+ elif interval_val > 900:
2717+ interval_val = 900
2718+ interval_spinner.set_value(interval_val)
2719+
2720+ # == Weather ==
2721+ locationlist_widget = self.builder.get_object("treeview_locations")
2722+ location_model = gtk.ListStore(str)
2723+
2724+ loc_cell = gtk.CellRendererText()
2725+ location_column = gtk.TreeViewColumn(_("Location"), loc_cell, text=0)
2726+ locationlist_widget.append_column(location_column)
2727+
2728+ self.weather_location = self.config.weather_location
2729+
2730+ # Fill model with location read from config file
2731+ location_model.insert(0, [self.weather_location])
2732+ locationlist_widget.set_model(location_model)
2733+
2734+ weather_display_checkbox = self.builder.get_object(
2735+ "weather_display_checkbox")
2736+ display_val = self.config.display_weather_in_client
2737+ weather_display_checkbox.set_active(display_val)
2738+ if not display_val:
2739+ self.builder.get_object("button_add_weather").set_sensitive(False)
2740+ self.builder.get_object("button_remove_weather").set_sensitive(
2741+ False)
2742+ self.builder.get_object("treeview_locations").set_sensitive(False)
2743+
2744+ # == User Interface ==
2745+ self.load_themes()
2746+ current_theme = self.config.theme_name
2747+
2748+ themelist_widget = self.builder.get_object("theme_list")
2749+ model = gtk.ListStore(str)
2750+
2751+ cell_renderer = gtk.CellRendererText()
2752+ column = gtk.TreeViewColumn("Themes", cell_renderer, text=0)
2753+ themelist_widget.append_column(column)
2754+
2755+ # Fill model with installed themes
2756+ for i in range(len(self.themes)):
2757+ model.insert(i, [self.themes[i]])
2758+
2759+ themelist_widget.set_model(model)
2760+
2761+ # Set current theme selected in theme list
2762+ index = model.get_iter_first()
2763+ unselected = True
2764+ index_counter = 0
2765+ while(unselected):
2766+ name = model.get_value(index, 0)
2767+ if name == current_theme:
2768+ unselected = False
2769+ themelist_widget.set_cursor(index_counter)
2770+ index = model.iter_next(index)
2771+ index_counter += 1
2772+
2773+ effect_checkbox = self.builder.get_object("checkbutton_effects")
2774+ effect_combobox = self.builder.get_object("combobox_effects")
2775+ if self.config.show_effects:
2776+ effect_checkbox.set_active(True)
2777+ effect_combobox.set_sensitive(True)
2778+ else:
2779+ effect_checkbox.set_active(False)
2780+ effect_combobox.set_sensitive(False)
2781+
2782+ # Set Effect Combobox value (Text values are set in ui file)
2783+ effect = self.config.transition_effect
2784+ if effect == "No effect":
2785+ effect_combobox.set_active(0)
2786+ if effect == "Crossfade":
2787+ effect_combobox.set_active(1)
2788+ if effect == "Zoom and fade":
2789+ effect_combobox.set_active(2)
2790+ if effect == "Slide":
2791+ effect_combobox.set_active(3)
2792+
2793+ # == General ==
2794+ checkbutton_fullscreen = self.builder.get_object(
2795+ "checkbutton_fullscreen")
2796+ if self.config.start_in_fullscreen:
2797+ checkbutton_fullscreen.set_active(True)
2798+ else:
2799+ checkbutton_fullscreen.set_active(False)
2800+
2801+ checkbutton_autostart = self.builder.get_object("checkbutton_autostart")
2802+ if self.config.start_auto_server:
2803+ checkbutton_autostart.set_active(True)
2804+ else:
2805+ checkbutton_autostart.set_active(False)
2806+
2807+ checkbutton_systray_icon = self.builder.get_object(
2808+ "checkbutton_systray_icon")
2809+ if self.config.tray_icon_enabled:
2810+ checkbutton_systray_icon.set_active(True)
2811+ else:
2812+ checkbutton_systray_icon.set_active(False)
2813+
2814+ spinbutton_slideshow_step = self.builder.get_object(
2815+ "spinbutton_slideshow_step")
2816+ spinbutton_slideshow_step.set_value(self.config.slideshow_step)
2817+
2818+ def add_to_model_and_config(self, selected_folder, model, folders, kind):
2819+ """
2820+ Add selected_folder to the model and the folders list while updating
2821+ the configuration item section specified by type
2822+ """
2823+ if not selected_folder in folders:
2824+ model.append([selected_folder])
2825+
2826+ if(folders == None):
2827+ folders = [selected_folder]
2828+ else:
2829+ folders.append(selected_folder)
2830+
2831+ if "" in folders:
2832+ folders.remove("")
2833+ str_folders = ";".join(folders)
2834+ self.config.write_content_value(kind, "folders", str_folders)
2835+
2836+ def init_model(self, model, items):
2837+ """Fill model with items from supplied list"""
2838+ for i in range(len(items)):
2839+ if not str(items[i]).strip() == "":
2840+ model.insert(i, [items[i]])
2841+
2842+ def load_themes(self):
2843+ """Load themes"""
2844+ themes = os.listdir(os.path.join(self.config.data_dir, 'themes'))
2845+ for element in themes:
2846+ theme = os.path.join(self.config.data_dir, 'themes', element)
2847+ if os.path.isdir(theme):
2848+ self.themes.append(element)
2849+
2850+
2851+class LogViewer:
2852+ """
2853+ Implements dialog that allows user to see logged events.
2854+
2855+ This dialog is used to check Entertainer logfiles. It reads all data from
2856+ selected file and saves rows to self.log_rows. Then it filters unwanted
2857+ rows away by calling self.filterMessages(). This method adds rows to
2858+ ListStore, which is the model of TreeView object.
2859+
2860+ Combobox and refresh -button actions read files again
2861+ Checkbox actions just filter current rows again
2862+ """
2863+
2864+ UI_DIR = os.path.join(os.path.dirname(__file__), "uis")
2865+
2866+ # Is this dialog running as a stand alone process
2867+ __STAND_ALONE = None
2868+
2869+ widgets = None
2870+ dialog = None
2871+ log_store = None
2872+ log_rows = []
2873+
2874+ def __init__(self, stand_alone):
2875+ self.logfile_entertainer = Configuration().LOG
2876+ self.logger = Logger().getLogger('utils.log_viewer')
2877+
2878+ self.__STAND_ALONE = stand_alone
2879+ try:
2880+ uifile = os.path.join(self.UI_DIR, "log_dialog.ui")
2881+ self.builder = gtk.Builder()
2882+ self.builder.set_translation_domain('entertainer')
2883+ self.builder.add_from_file(uifile)
2884+ except RuntimeError:
2885+ self.logger.critical("Couldn't open ui file: " + uifile)
2886+ sys.exit(1)
2887+ callback_dic = {
2888+ "on_close_log_button_clicked" : self.on_close_log_button_clicked,
2889+ "on_log_refresh_button_clicked" : self.update_log_rows,
2890+ "on_checkbutton_debug_toggled" : self.filter_messages,
2891+ "on_checkbutton_critical_toggled" : self.filter_messages,
2892+ "on_checkbutton_error_toggled" : self.filter_messages,
2893+ "on_checkbutton_warning_toggled" : self.filter_messages,
2894+ "on_checkbutton_info_toggled" : self.filter_messages }
2895+
2896+ self.builder.connect_signals(callback_dic)
2897+
2898+ # Create log treeview
2899+ treeview = self.builder.get_object("treeview_log")
2900+ cell_renderer1 = gtk.CellRendererText()
2901+ cell_renderer2 = gtk.CellRendererText()
2902+ cell_renderer3 = gtk.CellRendererText()
2903+ cell_renderer4 = gtk.CellRendererText()
2904+
2905+ column1 = gtk.TreeViewColumn("Date")
2906+ column1.pack_start(cell_renderer1, True)
2907+ column1.set_attributes(cell_renderer1, text = 0)
2908+
2909+ column2 = gtk.TreeViewColumn("Time")
2910+ column2.pack_start(cell_renderer2, True)
2911+ column2.set_attributes(cell_renderer2, text = 1)
2912+
2913+ column3 = gtk.TreeViewColumn("Type")
2914+ column3.pack_start(cell_renderer3, True)
2915+ column3.set_attributes(cell_renderer3, text = 2)
2916+
2917+ column4 = gtk.TreeViewColumn("Message")
2918+ column4.pack_end(cell_renderer4, True)
2919+ column4.set_attributes(cell_renderer4, text = 3)
2920+
2921+ treeview.append_column(column1)
2922+ treeview.append_column(column2)
2923+ treeview.append_column(column3)
2924+ treeview.append_column(column4)
2925+ treeview.set_headers_visible(True)
2926+
2927+ # Set model to view and read data from logfile
2928+ self.log_store = gtk.ListStore(str, str, str, str)
2929+ treeview.set_model(self.log_store)
2930+ self.update_log_rows()
2931+
2932+ # Show Log viewer dialog
2933+ self.dialog = self.builder.get_object("LogDialog")
2934+ self.dialog.resize(750, 500)
2935+ self.dialog.connect("destroy", self.on_close_log_button_clicked)
2936+ self.dialog.show()
2937+
2938+ def update_log_rows(self, widget=None):
2939+ """Read logfile and udpate treeview"""
2940+ self.log_rows[:] = []
2941+
2942+ try:
2943+ for line in open(self.logfile_entertainer, 'r'):
2944+ try:
2945+ line_table = line.split()
2946+ message = ' '.join(line_table[3:])
2947+ row = line_table[:3] + [message]
2948+ parsed_row = parse_row(row)
2949+ self.log_rows.append(parsed_row)
2950+ except IndexError:
2951+ print "Cannot parse log line: ", line
2952+ except IOError:
2953+ print "Cannot find logfile: ", self.logfile_entertainer
2954+
2955+ # Reverse so that the latest message is at top
2956+ self.log_rows.reverse()
2957+ # Filter unwated message types
2958+ self.filter_messages()
2959+
2960+ def filter_messages(self, widget = None):
2961+ """Checks which message types should be displayed on treeview"""
2962+ if self.log_store:
2963+ self.log_store.clear()
2964+
2965+ debug = self.builder.get_object("checkbutton_debug").get_active()
2966+ critical = self.builder.get_object("checkbutton_critical").get_active()
2967+ error = self.builder.get_object("checkbutton_error").get_active()
2968+ warning = self.builder.get_object("checkbutton_warning").get_active()
2969+ info = self.builder.get_object("checkbutton_info").get_active()
2970+
2971+ for element in self.log_rows:
2972+ if element[2] == "DEBUG" and debug:
2973+ self.log_store.append(element)
2974+ elif element[2] == "CRITICAL" and critical:
2975+ self.log_store.append(element)
2976+ elif element[2] == "ERROR" and error:
2977+ self.log_store.append(element)
2978+ elif element[2] == "WARNING" and warning:
2979+ self.log_store.append(element)
2980+ elif element[2] == "INFO" and info:
2981+ self.log_store.append(element)
2982+
2983+ # Signal handlers
2984+ def on_close_log_button_clicked(self, widget):
2985+ """
2986+ If running as a stand alone process, quit.
2987+ Otherwise only destroy dialog.
2988+ """
2989+ self.dialog.hide()
2990+ self.dialog.destroy()
2991+ if(self.__STAND_ALONE):
2992+ gtk.main_quit()
2993+
2994+
2995+def parse_row(row):
2996+ """
2997+ This parses the input list into a list suitable for the logviewer
2998+ @author Joshua Scotton
2999+ @param row The input list [Date, Time, Class, Type + Description]
3000+ """
3001+ if row[3][:5] == "DEBUG":
3002+ return [row[0], row[1], "DEBUG",
3003+ row[2] + ": " + row[3][5:]]
3004+ elif row[3][:8] == "CRITICAL":
3005+ return [row[0], row[1], "CRITICAL",
3006+ row[2] + ": " + row[3][8:]]
3007+ elif row[3][:5] == "ERROR":
3008+ return [row[0], row[1], "ERROR",
3009+ row[2] + ": " + row[3][5:]]
3010+ elif row[3][:7] == "WARNING":
3011+ return [row[0], row[1], "WARNING",
3012+ row[2] + ": " + row[3][7:]]
3013+ elif row[3][:4] == "INFO":
3014+ return [row[0], row[1], "INFO",
3015+ row[2] + ": " + row[3][4:]]
3016+
3017+
3018+class OpenFeedSourceDialog:
3019+ '''Feed source reader dialog'''
3020+
3021+ UI_DIR = os.path.join(os.path.dirname(__file__), "uis")
3022+
3023+ widgets = None
3024+ dialog = None
3025+ tree_widget = None
3026+ feeds = None
3027+ url = None
3028+
3029+ def __init__(self, the_widget, the_feeds):
3030+ """initialises the gtk window and displays it"""
3031+
3032+ #feeds is a pointer to a list of feeds from the config file
3033+ self.feeds = the_feeds
3034+
3035+ #needed so we can add feeds to the feed list widget
3036+ self.tree_widget = the_widget
3037+
3038+ # Load UI with gtk.Builder
3039+ uifile = os.path.join(self.UI_DIR, "open_feed_source_dialog.ui")
3040+ self.builder = gtk.Builder()
3041+ self.builder.set_translation_domain('entertainer')
3042+ self.builder.add_from_file(uifile)
3043+
3044+ # Get content management dialog and bind signal callbacks
3045+ self.dialog = self.builder.get_object("open_source_dialog")
3046+ if (self.dialog):
3047+ callback_dic = {
3048+ "on_fileOpen_clicked" : self.on_fileOpen_clicked,
3049+ "on_lifereaButton_clicked" : self.on_lifereaButton_clicked,
3050+ "on_enterURL_clicked" : self.on_enterURL_clicked,
3051+ "on_closeButton_clicked" : self.on_closeButton_clicked,
3052+ "on_url_dialog_ok_button_clicked" :
3053+ self.on_url_dialog_ok_button_clicked,
3054+ "on_url_dialog_cancel_button_clicked" :
3055+ self.on_url_dialog_cancel_button_clicked,
3056+ "on_url_dialog_delete_event" : self.on_url_dialog_delete_event,
3057+ "on_destroy" : self.on_closeButton_clicked
3058+ }
3059+
3060+ self.builder.connect_signals(callback_dic)
3061+
3062+ # Initilize dialog widgets with correct values and show dialog
3063+ self.dialog.resize(300, 200)
3064+ self.dialog.run()
3065+
3066+ def on_fileOpen_clicked(self, widget):
3067+ """Opens add file dialog and then adds all feeds in the opml file
3068+ selected """
3069+
3070+ #get the model for the feed list widget so we can add the new feeds
3071+ model = self.tree_widget.get_model()
3072+
3073+ #create select file dialog
3074+ dialog = gtk.FileChooserDialog(_("Select OPML file"), None,
3075+ gtk.FILE_CHOOSER_ACTION_OPEN,
3076+ (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN,
3077+ gtk.RESPONSE_OK))
3078+
3079+ #set dialog up to filter for only opml files
3080+ file_filter = gtk.FileFilter()
3081+ file_filter.set_name(_("OPML files"))
3082+ file_filter.add_pattern("*.opml")
3083+ dialog.add_filter(file_filter)
3084+
3085+ #set dialog up to allow multiple selections
3086+ dialog.set_select_multiple(True)
3087+
3088+ #run the dialog
3089+ status = dialog.run()
3090+
3091+ # if file was selected, get list of feeds from it and add to
3092+ #model and update config file
3093+ if(status == gtk.RESPONSE_OK):
3094+ FeedConfigTools().add_file_feeds_to_widget(
3095+ dialog.get_filenames(), model, self.feeds)
3096+ dialog.destroy()
3097+
3098+ def on_lifereaButton_clicked(self, widget):
3099+ """Adds any liferea feeds it finds to the feed widget and config file
3100+ """
3101+ #get the model
3102+ model = self.tree_widget.get_model()
3103+ #get the liferea feeds and then send everything to the
3104+ #add_file_feeds_to_widget method
3105+ FeedConfigTools().add_file_feeds_to_widget(
3106+ [OPMLParser().get_liferea_opml()], model, self.feeds)
3107+
3108+ def on_enterURL_clicked(self, widget):
3109+ """gets a opml file link from a user and adds any feeds it finds to the
3110+ feed widget and config file"""
3111+ url_dialog = self.builder.get_object("url_dialog")
3112+ model = self.tree_widget.get_model()
3113+ # Open dialog
3114+ url_dialog.set_title(_("Add OPML File"))
3115+ status = url_dialog.run()
3116+ # If folder was selected we add it to model and update config file
3117+ if(status == gtk.RESPONSE_OK):
3118+ FeedConfigTools().add_file_feeds_to_widget([self.url],
3119+ model, self.feeds)
3120+
3121+ def on_closeButton_clicked(self, widget):
3122+ self.dialog.hide()
3123+ self.dialog.destroy()
3124+
3125+ def on_url_dialog_ok_button_clicked(self, widget):
3126+ """URL dialog OK button pressed. Sets self.url"""
3127+ url_dialog = self.builder.get_object("url_dialog")
3128+ url_entry = self.builder.get_object("url_entry")
3129+ url_dialog.hide()
3130+ self.url = url_entry.get_text()
3131+ url_entry.set_text("")
3132+ url_dialog.response(gtk.RESPONSE_OK)
3133+
3134+ def on_url_dialog_cancel_button_clicked(self, widget):
3135+ """URL dialog cancelled. Hides dialog"""
3136+ url_dialog = self.builder.get_object("url_dialog")
3137+ url_entry = self.builder.get_object("url_entry")
3138+ url_dialog.hide()
3139+ url_entry.set_text("")
3140+ url_dialog.response(gtk.RESPONSE_CANCEL)
3141+
3142+ def on_url_dialog_delete_event(self, widget, data):
3143+ """Dialog's X clicked. Hides dialog"""
3144+ url_dialog = self.builder.get_object("url_dialog")
3145+ url_entry = self.builder.get_object("url_entry")
3146+ url_dialog.hide()
3147+ url_entry.set_text("")
3148+ url_dialog.response(gtk.RESPONSE_CANCEL)
3149+ return True
3150+
3151+
3152
3153=== added file 'entertainerlib/download.py'
3154--- entertainerlib/download.py 1970-01-01 00:00:00 +0000
3155+++ entertainerlib/download.py 2010-01-23 03:34:11 +0000
3156@@ -0,0 +1,455 @@
3157+# Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
3158+'''Downloader classes.'''
3159+
3160+import locale
3161+import os
3162+import re
3163+import socket
3164+import threading
3165+import urllib
3166+from xml.dom import minidom
3167+
3168+import gobject
3169+
3170+# Amazon licence for Entertainer
3171+LICENSE_KEY = "1YCWYZ0SPPAJ27YZZ482"
3172+DEFAULT_LOCALE = "en_US"
3173+ASSOCIATE = "webservices-20"
3174+
3175+# We are not allowed to batch more than 2 requests at once
3176+# http://docs.amazonwebservices.com/AWSEcommerceService/4-0/
3177+# PgCombiningOperations.html
3178+MAX_BATCH_JOBS = 2
3179+
3180+
3181+class AlbumArtDownloader(threading.Thread):
3182+ """
3183+ Search and download album art from the internet.
3184+
3185+ This class is heavily based on Rhythmbox - AlbumArt plugin's class
3186+ 'AmazonCoverArtSearch'. That plugin is released under GPLv2 (or higher)
3187+ and it's copyrights belong to Gareth Murphy and Martin Szulecki.
3188+
3189+ See more: http://www.gnome.org/projects/rhythmbox/
3190+
3191+ If you want better cover search, please contribute to Rhyhtmbox project.
3192+ """
3193+
3194+ def __init__(self, album, artist, art_file_path, callback = None):
3195+ """
3196+ Initialize album art downloader
3197+ @param album: Album title
3198+ @param artist: Artist name
3199+ @param art_file_path: Path where albumart is saved
3200+ @param callback: Callback function that is called after search if set
3201+ """
3202+ threading.Thread.__init__(self)
3203+ self.setName("AlbumArt Downloader")
3204+ self.callback_function = callback # Callback function
3205+ self.album = album # Album title
3206+ self.artist = artist # Artist name
3207+ # Album art files are in this directory
3208+ self.path = art_file_path
3209+ (self.tld, self.encoding) = self.__get_locale ()
3210+
3211+ def run(self):
3212+ """Start searching and downloading albumart."""
3213+ self.search()
3214+
3215+ def __get_locale (self):
3216+ '''Get locale information from user\'s machine'''
3217+ # "JP is the only locale that correctly takes UTF8 input.
3218+ # All other locales use LATIN1."
3219+ # http://developer.amazonwebservices.com/connect/
3220+ # entry.jspa?externalID=1295&categoryID=117
3221+ supported_locales = {
3222+ "en_US" : ("com", "latin1"),
3223+ "en_GB" : ("co.uk", "latin1"),
3224+ "de" : ("de", "latin1"),
3225+ "ja" : ("jp", "utf8")
3226+ }
3227+
3228+ lc_id = DEFAULT_LOCALE
3229+ default = locale.getdefaultlocale ()[0]
3230+ if default:
3231+ if supported_locales.has_key (default):
3232+ lc_id = default
3233+ else:
3234+ lang = default.split("_")[0]
3235+ if supported_locales.has_key (lang):
3236+ lc_id = lang
3237+
3238+ return supported_locales[lc_id]
3239+
3240+ def __valid_match (self, item):
3241+ '''Determine if item matches tag criteria'''
3242+ return (hasattr (item, "LargeImage") or hasattr (item, "MediumImage")) \
3243+ and hasattr (item, "ItemAttributes")
3244+
3245+ def __tidy_up_string (self, str_input):
3246+ """
3247+ Tidy up string. Remove spaces, convert to lowercase and replace chars.
3248+ """
3249+ # Lowercase
3250+ str_input = str_input.lower ()
3251+ # Strip
3252+ str_input = str_input.strip ()
3253+
3254+ # TODO: Convert accented to unaccented
3255+ str_input = str_input.replace (" - ", " ")
3256+ str_input = str_input.replace (": ", " ")
3257+ str_input = str_input.replace (" & ", " and ")
3258+
3259+ return str_input
3260+
3261+ def search(self):
3262+ """Search album art from Amazon"""
3263+ self.searching = True
3264+ self.cancel = False
3265+ self.keywords = []
3266+
3267+ st_artist = self.artist or u'Unknown'
3268+ st_album = self.album or u'Unknown'
3269+
3270+ if st_artist == st_album == u'Unknown':
3271+ self.on_search_completed (None)
3272+ return
3273+
3274+ # Tidy up
3275+
3276+ # Replace quote characters
3277+ # don't replace single quote: could be important punctuation
3278+ for char in ["\""]:
3279+ st_artist = st_artist.replace (char, '')
3280+ st_album = st_album.replace (char, '')
3281+
3282+
3283+ self.st_album = st_album
3284+ self.st_artist = st_artist
3285+
3286+ # Remove variants of Disc/CD [1-9] from album title before search
3287+ for exp in ["\([Dd]isc *[1-9]+\)", "\([Cc][Dd] *[1-9]+\)"]:
3288+ p = re.compile (exp)
3289+ st_album = p.sub ('', st_album)
3290+
3291+ st_album_no_vol = st_album
3292+ for exp in ["\(*[Vv]ol.*[1-9]+\)*"]:
3293+ p = re.compile (exp)
3294+ st_album_no_vol = p.sub ('', st_album_no_vol)
3295+
3296+ self.st_album_no_vol = st_album_no_vol
3297+
3298+ # Save current search's entry properties
3299+ self.search_album = st_album
3300+ self.search_artist = st_artist
3301+ self.search_album_no_vol = st_album_no_vol
3302+
3303+ # TODO: Improve to decrease wrong cover downloads, maybe add severity?
3304+ # Assemble list of search keywords (and thus search queries)
3305+ if st_album == u'Unknown':
3306+ self.keywords.append ("%s Best of" % (st_artist))
3307+ self.keywords.append ("%s Greatest Hits" % (st_artist))
3308+ self.keywords.append ("%s Essential" % (st_artist))
3309+ self.keywords.append ("%s Collection" % (st_artist))
3310+ self.keywords.append ("%s" % (st_artist))
3311+ elif st_artist == u'Unknown':
3312+ self.keywords.append ("%s" % (st_album))
3313+ if st_album_no_vol != st_artist:
3314+ self.keywords.append ("%s" % (st_album_no_vol))
3315+ self.keywords.append ("Various %s" % (st_album))
3316+ else:
3317+ if st_album != st_artist:
3318+ self.keywords.append ("%s %s" % (st_artist, st_album))
3319+ if st_album_no_vol != st_album:
3320+ self.keywords.append ("%s %s" %
3321+ (st_artist, st_album_no_vol))
3322+ self.keywords.append ("Various %s" % (st_album))
3323+ self.keywords.append ("%s" % (st_artist))
3324+
3325+ # Initiate asynchronous search
3326+ self.search_next ()
3327+
3328+ def search_next(self):
3329+ """Search again, because the last one didn't find any covers."""
3330+ if len (self.keywords) == 0:
3331+ # No keywords left to search -> no results
3332+ self.on_search_completed (None)
3333+ return False
3334+
3335+ self.searching = True
3336+
3337+ url = "http://ecs.amazonaws." + self.tld + "/onca/xml" \
3338+ "?Service=AWSECommerceService" \
3339+ "&AWSAccessKeyId=" + LICENSE_KEY + \
3340+ "&AssociateTag=" + ASSOCIATE + \
3341+ "&ResponseGroup=Images,ItemAttributes" \
3342+ "&Operation=ItemSearch" \
3343+ "&ItemSearch.Shared.SearchIndex=Music"
3344+
3345+ job = 1
3346+ while job <= MAX_BATCH_JOBS and len (self.keywords) > 0:
3347+ keyword = self.keywords.pop (0)
3348+ keyword = keyword.encode (self.encoding, "ignore")
3349+ keyword = keyword.strip ()
3350+ keyword = urllib.quote (keyword)
3351+ url += "&ItemSearch.%d.Keywords=%s" % (job, keyword)
3352+ job += 1
3353+
3354+ # Retrieve search for keyword
3355+ temp = urllib.urlopen(url)
3356+ search_results = temp.read()
3357+ self.on_search_response(search_results)
3358+ return True
3359+
3360+ def __unmarshal(self, element):
3361+ rc = object()
3362+ child_elements = [e for e in element.childNodes if isinstance (e,
3363+ minidom.Element)]
3364+ if child_elements:
3365+ for child in child_elements:
3366+ key = child.tagName
3367+ if hasattr (rc, key):
3368+ if not isinstance (getattr (rc, key), list):
3369+ setattr (rc, key, [getattr (rc, key)])
3370+ getattr (rc, key).append (self.__unmarshal (child))
3371+ # get_best_match_urls() wants a list, even if there is only
3372+ # one item/artist
3373+ elif child.tagName in ("Items", "Item", "Artist"):
3374+ setattr (rc, key, [self.__unmarshal(child)])
3375+ else:
3376+ setattr (rc, key, self.__unmarshal(child))
3377+ else:
3378+ rc = "".join ([e.data for e in element.childNodes if isinstance (e,
3379+ minidom.Text)])
3380+ return rc
3381+
3382+ def on_search_response (self, result_data):
3383+ '''Check search results
3384+
3385+ If results are not good, we search again with the next keyword.
3386+ '''
3387+ if result_data is None:
3388+ self.search_next()
3389+ return
3390+
3391+ try:
3392+ xmldoc = minidom.parseString(result_data)
3393+ except (TypeError, AttributeError):
3394+ self.search_next()
3395+ return
3396+
3397+ data = self.__unmarshal (xmldoc)
3398+ if not hasattr (data, "ItemSearchResponse") or \
3399+ not hasattr (data.ItemSearchResponse, "Items"):
3400+ # Something went wrong ...
3401+ self.search_next ()
3402+ else:
3403+ # We got some search results
3404+ self.on_search_results (data.ItemSearchResponse.Items)
3405+
3406+ def on_search_results (self, results):
3407+ '''Results were found, now we need to take action.'''
3408+ self.on_search_completed (results)
3409+
3410+ def on_search_completed (self, result):
3411+ """
3412+ Search completed and results found.
3413+
3414+ Download large album art image from the first result and save it to
3415+ the disk. This function diverges greatly from the rhythmbox
3416+ implementation in order to avoid their loader and CoverArtDatabase
3417+ """
3418+ self.searching = False
3419+ image_urls = self.get_best_match_urls(result)
3420+ if len(image_urls) == 0:
3421+ return
3422+ image_url = image_urls[0]
3423+ image_file = urllib.urlopen(image_url)
3424+ # base64 encode artist and album so there can be a '/' in the artist
3425+ # or album
3426+ artist_album = self.artist + " - " + self.album
3427+ artist_album = artist_album.encode("base64")
3428+
3429+ dest = open(os.path.join(self.path, artist_album + ".jpg"),'w')
3430+ dest.write(image_file.read())
3431+ dest.close()
3432+
3433+ if self.callback_function is not None:
3434+ self.callback_function(self.artist, self.album)
3435+
3436+ def get_best_match_urls (self, search_results):
3437+ """Return tuple of URL's to large and medium cover of the best match"""
3438+ # Default to "no match", our results must match our criteria
3439+
3440+ # This code comes from Rhythmbox so we can't control the use of 'filter'
3441+ # pylint: disable-msg=W0141
3442+
3443+ if not search_results:
3444+ return []
3445+
3446+ best_match = None
3447+
3448+ for result in search_results:
3449+ if not hasattr (result, "Item"):
3450+ # Search was unsuccessful, try next batch job
3451+ continue
3452+
3453+ items = filter(self.__valid_match, result.Item)
3454+ if self.search_album != u'Unknown':
3455+ album_check = self.__tidy_up_string (self.search_album)
3456+ for item in items:
3457+ if not hasattr (item.ItemAttributes, "Title"):
3458+ continue
3459+
3460+ album = self.__tidy_up_string (item.ItemAttributes.Title)
3461+ if album == album_check:
3462+ # Found exact album, can not get better than that
3463+ best_match = item
3464+ break
3465+ # If we already found a best_match, just keep checking for
3466+ # exact one. Check the results for both an album name that
3467+ # contains the name we're searching for, and an album name
3468+ # that's a substring of the name we're searching for
3469+ elif (best_match is None) and \
3470+ (album.find (album_check) != -1 or
3471+ album_check.find (album) != -1):
3472+ best_match = item
3473+
3474+ # If we still have no definite hit, use first result where artist
3475+ # matches
3476+ if (self.search_album == u'Unknown' and \
3477+ self.search_artist != u'Unknown'):
3478+ artist_check = self.__tidy_up_string (self.search_artist)
3479+ if best_match is None:
3480+ # Check if artist appears in the Artists list
3481+ hit = False
3482+ for item in items:
3483+ if not hasattr (item.ItemAttributes, "Artist"):
3484+ continue
3485+
3486+ for artist in item.ItemAttributes.Artist:
3487+ artist = self.__tidy_up_string (artist)
3488+ if artist.find (artist_check) != -1:
3489+ best_match = item
3490+ hit = True
3491+ break
3492+ if hit:
3493+ break
3494+
3495+ urls = [getattr (best_match, size).URL for size in ("LargeImage",
3496+ "MediumImage")
3497+ if hasattr (best_match, size)]
3498+ if urls:
3499+ return urls
3500+
3501+ # No search was successful
3502+ return []
3503+
3504+
3505+class LyricsDownloader(object):
3506+ """
3507+ Search and download song lyrics from the internet.
3508+ Update music cache if lyrics found.
3509+ """
3510+
3511+ # The permanent user ID from Lyricsfly
3512+ # NOTICE: This is the personal user ID for Entertainer, if you want to
3513+ # experiment with the API from lyricsfly you can get an ID here =>
3514+ # http://lyricsfly.com/api/, don't use this one as abuse of our key may
3515+ # invalidate it.
3516+ _LYRICSFLY_KEY = 'YzIxOTM4M2NkNGQ4MmRmODEtZW50ZXJ0YWluZXItcHJvamVjdC5jb20='
3517+
3518+ def __init__(self, title, artist, callback):
3519+ """
3520+ Initialize lyrics downloader.
3521+ @param title: Title of the track
3522+ @param artist: Artist of the track
3523+ @param callback: Callback function which is called when search is over.
3524+ lyrics are given as a parameter to this callback function.
3525+ """
3526+ self.title = title.lower()
3527+ self.artist = artist.lower()
3528+ self.callback = callback
3529+
3530+ def search(self):
3531+ """
3532+ Search lyrics and download if found. Search is done asynchronously.
3533+ This method returns immediately and set callback is called when search
3534+ is over.
3535+ """
3536+ gobject.timeout_add(2000, self._async_search)
3537+
3538+ def _async_search(self):
3539+ """
3540+ Search lyrics and download if found
3541+ """
3542+ lyrics = ""
3543+ self._clean_up_artist_title()
3544+ lyrics_xml = self._get_lyrics_xml()
3545+ if lyrics_xml is not None:
3546+ lyrics = self._parse_lyrics_xml(lyrics_xml)
3547+ self.callback(lyrics)
3548+
3549+ def _clean_up_artist_title(self):
3550+ """
3551+ Clean up the artist and title.
3552+ """
3553+ # Clean spaces
3554+ self.title = self.title.strip()
3555+ self.artist = self.artist.strip()
3556+
3557+ # Convert title and artist to use in url, special symbols have to be
3558+ # replaced by a '%' not '%xx'
3559+ # TODO: Find out what the special symbols are (', &, ...)
3560+ # not letters, digits, spaces and ()$^*=:;|#@}{][!,.-_\
3561+ self.artist = urllib.quote(self.artist.encode('utf-8'),
3562+ "'&()$^*=:;|#@}{][!,\\")
3563+ self.title = urllib.quote(self.title.encode('utf-8'),
3564+ "'&()$^*=:;|#@}{][!,\\")
3565+
3566+ self.artist = self.artist.replace("'", "%").replace("&", "%")
3567+ self.title = self.title.replace("'", "%").replace("&", "%")
3568+
3569+ def _get_lyrics_xml(self):
3570+ """
3571+ Download the lyrics XML-file.
3572+ """
3573+ lyrics_xml = None
3574+
3575+ # timeout in seconds
3576+ timeout = 5
3577+ socket.setdefaulttimeout(timeout)
3578+
3579+ url = "http://lyricsfly.com/api/api.php?i=%s&a=%s&t=%s" \
3580+ % (self._LYRICSFLY_KEY.decode('base64'), self.artist, self.title)
3581+ try:
3582+ temp = urllib.urlopen(url)
3583+ lyrics_xml = temp.read()
3584+ except IOError:
3585+ return None
3586+
3587+ return lyrics_xml
3588+
3589+ def _parse_lyrics_xml(self, lyrics_xml):
3590+ """Parse lyrics XML and return lyrics string"""
3591+ xmldoc = minidom.parseString(lyrics_xml).documentElement
3592+
3593+ # Get the lyric from the XML file
3594+ try:
3595+ lyrics = xmldoc.getElementsByTagName('tx')[0].firstChild.nodeValue
3596+ except IndexError:
3597+ return ''
3598+
3599+ # Clean spaces and enters
3600+ lyrics = lyrics.strip().replace('\n', '')
3601+ lyrics = lyrics.replace('[br]', '\n')
3602+
3603+ # Add the artist and title to the top of the lyric
3604+ lyrics = xmldoc.getElementsByTagName('ar')[0].firstChild.nodeValue + \
3605+ ' - ' + xmldoc.getElementsByTagName('tt')[0].firstChild.nodeValue + \
3606+ '\n\n' + lyrics
3607+
3608+ xmldoc.unlink()
3609+
3610+ return lyrics
3611+
3612
3613=== renamed directory 'entertainerlib/frontend/gui' => 'entertainerlib/gui'
3614=== modified file 'entertainerlib/gui/screen_history.py'
3615--- entertainerlib/frontend/gui/screen_history.py 2009-06-03 01:54:03 +0000
3616+++ entertainerlib/gui/screen_history.py 2010-01-23 03:34:11 +0000
3617@@ -1,7 +1,7 @@
3618 # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
3619 '''Stack container for screen objects'''
3620
3621-from entertainerlib.utils.configuration import Configuration
3622+from entertainerlib.configuration import Configuration
3623
3624 class ScreenHistory(object):
3625 '''ScreenHistory contains the latest screens in is a stack.'''
3626@@ -13,7 +13,7 @@
3627
3628 def add_screen(self, screen):
3629 '''Push the provided screen onto the history stack.'''
3630- if len(self.screens) < self.config.history_size():
3631+ if len(self.screens) < self.config.history_size:
3632 self.screens.append(screen)
3633 else:
3634 self.remove_from_stage_callback(self.screens[0])
3635
3636=== modified file 'entertainerlib/gui/screens/album.py'
3637--- entertainerlib/frontend/gui/screens/album.py 2009-07-14 10:46:47 +0000
3638+++ entertainerlib/gui/screens/album.py 2010-01-23 03:34:11 +0000
3639@@ -5,12 +5,11 @@
3640 import clutter
3641 import pango
3642
3643-from entertainerlib.frontend.gui.screens.screen import Screen
3644-from entertainerlib.frontend.gui.widgets.eyecandy_texture import (
3645- EyeCandyTexture)
3646-from entertainerlib.frontend.gui.widgets.label import Label
3647-from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
3648-from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
3649+from entertainerlib.gui.screens.screen import Screen
3650+from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture
3651+from entertainerlib.gui.widgets.label import Label
3652+from entertainerlib.gui.widgets.list_indicator import ListIndicator
3653+from entertainerlib.gui.widgets.text_menu import TextMenu
3654
3655 class Album(Screen):
3656 '''Screen that allows user to browse and play tracks of the music album.'''
3657
3658=== modified file 'entertainerlib/gui/screens/artist.py'
3659--- entertainerlib/frontend/gui/screens/artist.py 2009-05-12 17:18:10 +0000
3660+++ entertainerlib/gui/screens/artist.py 2010-01-23 03:34:11 +0000
3661@@ -3,11 +3,11 @@
3662
3663 import pango
3664
3665-from entertainerlib.frontend.gui.screens.screen import Screen
3666-from entertainerlib.frontend.gui.tabs.albums_tab import AlbumsTab
3667-from entertainerlib.frontend.gui.tabs.tracks_tab import TracksTab
3668-from entertainerlib.frontend.gui.widgets.label import Label
3669-from entertainerlib.frontend.medialibrary.playlist import Playlist
3670+from entertainerlib.gui.screens.screen import Screen
3671+from entertainerlib.gui.tabs.albums_tab import AlbumsTab
3672+from entertainerlib.gui.tabs.tracks_tab import TracksTab
3673+from entertainerlib.gui.widgets.label import Label
3674+from entertainerlib.client.medialibrary.playlist import Playlist
3675
3676 class Artist(Screen):
3677 '''Screen that allows user to browse music by artist.'''
3678
3679=== modified file 'entertainerlib/gui/screens/audio_play.py'
3680--- entertainerlib/frontend/gui/screens/audio_play.py 2009-05-28 20:23:17 +0000
3681+++ entertainerlib/gui/screens/audio_play.py 2010-01-23 03:34:11 +0000
3682@@ -5,10 +5,10 @@
3683 import gtk
3684 import clutter
3685
3686-from entertainerlib.frontend.gui.screens.screen import Screen
3687-from entertainerlib.frontend.gui.tabs.lyrics_tab import LyricsTab
3688-from entertainerlib.frontend.gui.tabs.playing_tab import PlayingTab
3689-from entertainerlib.frontend.gui.widgets.eyecandy_texture import (
3690+from entertainerlib.gui.screens.screen import Screen
3691+from entertainerlib.gui.tabs.lyrics_tab import LyricsTab
3692+from entertainerlib.gui.tabs.playing_tab import PlayingTab
3693+from entertainerlib.gui.widgets.eyecandy_texture import (
3694 EyeCandyTexture)
3695
3696 class AudioPlay(Screen):
3697
3698=== modified file 'entertainerlib/gui/screens/disc.py'
3699--- entertainerlib/frontend/gui/screens/disc.py 2009-07-19 20:23:56 +0000
3700+++ entertainerlib/gui/screens/disc.py 2010-01-23 03:34:11 +0000
3701@@ -9,17 +9,15 @@
3702 import pango
3703 import clutter
3704
3705-from entertainerlib.frontend.gui.screens.screen import Screen
3706-from entertainerlib.frontend.gui.widgets.eyecandy_texture import (
3707- EyeCandyTexture)
3708-from entertainerlib.frontend.gui.widgets.label import Label
3709-from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
3710-from entertainerlib.frontend.gui.widgets.loading_animation import (
3711- LoadingAnimation)
3712-from entertainerlib.frontend.gui.widgets.texture import Texture
3713-from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
3714-from entertainerlib.frontend.medialibrary.playlist import Playlist
3715-from entertainerlib.utils.albumart_downloader import AlbumArtDownloader
3716+from entertainerlib.gui.screens.screen import Screen
3717+from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture
3718+from entertainerlib.gui.widgets.label import Label
3719+from entertainerlib.gui.widgets.list_indicator import ListIndicator
3720+from entertainerlib.gui.widgets.loading_animation import LoadingAnimation
3721+from entertainerlib.gui.widgets.texture import Texture
3722+from entertainerlib.gui.widgets.text_menu import TextMenu
3723+from entertainerlib.client.medialibrary.playlist import Playlist
3724+from entertainerlib.download import AlbumArtDownloader
3725
3726 class Disc(Screen):
3727 '''Screen allows user to play tracks from the current Audio CD.'''
3728
3729=== modified file 'entertainerlib/gui/screens/factory.py'
3730--- entertainerlib/frontend/gui/screens/factory.py 2009-06-02 01:16:03 +0000
3731+++ entertainerlib/gui/screens/factory.py 2010-01-23 03:34:11 +0000
3732@@ -1,25 +1,26 @@
3733 # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
3734 '''ScreenFactory - Create correct screen based on the type provided.'''
3735
3736-from entertainerlib.frontend.gui.screens.artist import Artist
3737-from entertainerlib.frontend.gui.screens.album import Album
3738-from entertainerlib.frontend.gui.screens.audio_play import AudioPlay
3739-from entertainerlib.frontend.gui.screens.disc import Disc
3740-from entertainerlib.frontend.gui.screens.feed import Feed
3741-from entertainerlib.frontend.gui.screens.feed_entry import FeedEntry
3742-from entertainerlib.frontend.gui.screens.main import Main
3743-from entertainerlib.frontend.gui.screens.movie import Movie
3744-from entertainerlib.frontend.gui.screens.music import Music
3745-from entertainerlib.frontend.gui.screens.photo import Photo
3746-from entertainerlib.frontend.gui.screens.photo_albums import PhotoAlbums
3747-from entertainerlib.frontend.gui.screens.photographs import Photographs
3748-from entertainerlib.frontend.gui.screens.question import Question
3749-from entertainerlib.frontend.gui.screens.rss import Rss
3750-from entertainerlib.frontend.gui.screens.tv_episodes import TvEpisodes
3751-from entertainerlib.frontend.gui.screens.tv_series import TvSeries
3752-from entertainerlib.frontend.gui.screens.video import Video
3753-from entertainerlib.frontend.gui.screens.video_osd import VideoOSD
3754-from entertainerlib.frontend.gui.screens.weather import WeatherScreen
3755+from entertainerlib.gui.screens.artist import Artist
3756+from entertainerlib.gui.screens.album import Album
3757+from entertainerlib.gui.screens.audio_play import AudioPlay
3758+from entertainerlib.gui.screens.disc import Disc
3759+from entertainerlib.gui.screens.feed import Feed
3760+from entertainerlib.gui.screens.feed_entry import FeedEntry
3761+from entertainerlib.gui.screens.main import Main
3762+from entertainerlib.gui.screens.movie import Movie
3763+from entertainerlib.gui.screens.music import Music
3764+from entertainerlib.gui.screens.photo import Photo
3765+from entertainerlib.gui.screens.photo_albums import PhotoAlbums
3766+from entertainerlib.gui.screens.photographs import Photographs
3767+from entertainerlib.gui.screens.question import Question
3768+from entertainerlib.gui.screens.rss import Rss
3769+from entertainerlib.gui.screens.tv_episodes import TvEpisodes
3770+from entertainerlib.gui.screens.tv_series import TvSeries
3771+from entertainerlib.gui.screens.video import Video
3772+from entertainerlib.gui.screens.video_osd import VideoOSD
3773+from entertainerlib.gui.screens.weather import WeatherScreen
3774+
3775
3776 class ScreenFactory(object):
3777 '''Generate a screen based on the type provided.'''
3778
3779=== modified file 'entertainerlib/gui/screens/feed.py'
3780--- entertainerlib/frontend/gui/screens/feed.py 2009-07-12 07:57:05 +0000
3781+++ entertainerlib/gui/screens/feed.py 2010-01-23 03:34:11 +0000
3782@@ -3,12 +3,12 @@
3783
3784 import pango
3785
3786-from entertainerlib.frontend.gui.screens.screen import Screen
3787-from entertainerlib.frontend.gui.widgets.label import Label
3788-from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
3789-from entertainerlib.frontend.gui.widgets.texture import Texture
3790-from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
3791-from entertainerlib.utils.feed_utils import FeedEntryParser
3792+from entertainerlib.gui.screens.screen import Screen
3793+from entertainerlib.gui.widgets.label import Label
3794+from entertainerlib.gui.widgets.list_indicator import ListIndicator
3795+from entertainerlib.gui.widgets.texture import Texture
3796+from entertainerlib.gui.widgets.text_menu import TextMenu
3797+from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser
3798
3799 class Feed(Screen):
3800 '''Screen is displayed when headlines are accessed for a specific feed.'''
3801
3802=== modified file 'entertainerlib/gui/screens/feed_entry.py'
3803--- entertainerlib/frontend/gui/screens/feed_entry.py 2009-06-25 18:56:15 +0000
3804+++ entertainerlib/gui/screens/feed_entry.py 2010-01-23 03:34:11 +0000
3805@@ -3,12 +3,12 @@
3806
3807 import pango
3808
3809-from entertainerlib.frontend.gui.screens.screen import Screen
3810-from entertainerlib.frontend.gui.user_event import UserEvent
3811-from entertainerlib.frontend.gui.widgets.label import Label
3812-from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea
3813-from entertainerlib.frontend.gui.widgets.texture import Texture
3814-from entertainerlib.utils.feed_utils import FeedEntryParser
3815+from entertainerlib.gui.screens.screen import Screen
3816+from entertainerlib.gui.user_event import UserEvent
3817+from entertainerlib.gui.widgets.label import Label
3818+from entertainerlib.gui.widgets.scroll_area import ScrollArea
3819+from entertainerlib.gui.widgets.texture import Texture
3820+from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser
3821
3822 class FeedEntry(Screen):
3823 '''Screen displays one feed entry.'''
3824
3825=== modified file 'entertainerlib/gui/screens/main.py'
3826--- entertainerlib/frontend/gui/screens/main.py 2009-07-19 19:27:31 +0000
3827+++ entertainerlib/gui/screens/main.py 2010-01-23 03:34:11 +0000
3828@@ -3,14 +3,13 @@
3829
3830 import clutter
3831
3832-from entertainerlib.frontend.gui.screens.screen import Screen
3833-from entertainerlib.frontend.gui.widgets.clock_label import ClockLabel
3834-from entertainerlib.frontend.gui.widgets.label import Label
3835-from entertainerlib.frontend.gui.widgets.scroll_menu import ScrollMenu
3836-from entertainerlib.frontend.gui.widgets.texture import Texture
3837-from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
3838-from entertainerlib.utils.cd_utils import eject_cd
3839-from entertainerlib.utils.feed_utils import FeedEntryParser
3840+from entertainerlib.gui.screens.screen import Screen
3841+from entertainerlib.gui.widgets.clock_label import ClockLabel
3842+from entertainerlib.gui.widgets.label import Label
3843+from entertainerlib.gui.widgets.scroll_menu import ScrollMenu
3844+from entertainerlib.gui.widgets.texture import Texture
3845+from entertainerlib.gui.widgets.text_menu import TextMenu
3846+from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser
3847
3848 class Main(Screen):
3849 '''Screen displayed when frontend is opened and provides main navigation.'''
3850@@ -76,12 +75,9 @@
3851 menu.add_item(_("Photographs"), "photo")
3852 menu.add_item(_("Headlines"), "rss")
3853
3854- if self.config.display_weather_in_frontend():
3855+ if self.config.display_weather_in_client:
3856 menu.add_item(_("Weather"), "weather")
3857
3858- if self.config.display_cd_eject_in_frontend():
3859- menu.add_item(_("Eject CD"), "eject_cd")
3860-
3861 if self.media_player.has_media():
3862 menu.add_item(_("Playing now..."), "playing")
3863
3864@@ -90,7 +86,7 @@
3865
3866 # Menu position
3867 menu_clip = menu.visible_items * 70
3868- menu_y = int((self.config.get_stage_height() - menu_clip + 10) / 2)
3869+ menu_y = int((self.config.stage_height - menu_clip + 10) / 2)
3870 menu.set_position(self.get_abs_x(0.75), menu_y)
3871
3872 return menu
3873@@ -222,8 +218,8 @@
3874
3875 # If the preview was updated fade it in.
3876 if update:
3877- fade_in = clutter.Timeline(20, 60)
3878- alpha_in = clutter.Alpha(fade_in, clutter.smoothstep_inc_func)
3879+ fade_in = clutter.Timeline(500)
3880+ alpha_in = clutter.Alpha(fade_in, clutter.EASE_IN_OUT_SINE)
3881 self.behaviour = clutter.BehaviourOpacity(0, 255, alpha_in)
3882 self.behaviour.apply(self.preview)
3883 fade_in.start()
3884@@ -288,8 +284,6 @@
3885 self.callback("video")
3886 elif item.get_name() == "weather":
3887 self.callback("weather")
3888- elif item.get_name() == "eject_cd":
3889- eject_cd()
3890 elif item.get_name() == "photo":
3891 self.callback("photo_albums")
3892 elif item.get_name() == "rss":
3893
3894=== modified file 'entertainerlib/gui/screens/movie.py'
3895--- entertainerlib/frontend/gui/screens/movie.py 2009-07-21 22:22:53 +0000
3896+++ entertainerlib/gui/screens/movie.py 2010-01-23 03:34:11 +0000
3897@@ -5,14 +5,13 @@
3898 import gtk
3899 import pango
3900
3901-from entertainerlib.frontend.gui.screens.screen import Screen
3902-from entertainerlib.frontend.gui.user_event import UserEvent
3903-from entertainerlib.frontend.gui.widgets.eyecandy_texture import (
3904- EyeCandyTexture)
3905-from entertainerlib.frontend.gui.widgets.label import Label
3906-from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea
3907-from entertainerlib.frontend.gui.widgets.texture import Texture
3908-from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
3909+from entertainerlib.gui.screens.screen import Screen
3910+from entertainerlib.gui.user_event import UserEvent
3911+from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture
3912+from entertainerlib.gui.widgets.label import Label
3913+from entertainerlib.gui.widgets.scroll_area import ScrollArea
3914+from entertainerlib.gui.widgets.texture import Texture
3915+from entertainerlib.gui.widgets.text_menu import TextMenu
3916
3917 class Movie(Screen):
3918 '''Screen contains information of one movie.'''
3919@@ -85,10 +84,14 @@
3920
3921 # Stars (rating)
3922 star = Texture(self.theme.getImage("star"))
3923+ star.hide()
3924+ self.add(star)
3925 star2 = Texture(self.theme.getImage("star2"))
3926+ star2.hide()
3927+ self.add(star2)
3928
3929 for i in range(self.movie.get_rating()):
3930- tex = clutter.CloneTexture(star)
3931+ tex = clutter.Clone(star)
3932 tex.set_position(
3933 self.get_abs_x(0.47) + (self.get_abs_x(0.0366) * i),
3934 self.get_abs_y(0.17))
3935@@ -97,7 +100,7 @@
3936
3937 dark_star = 5 - self.movie.get_rating()
3938 for i in range(dark_star):
3939- tex = clutter.CloneTexture(star2)
3940+ tex = clutter.Clone(star2)
3941 tex.set_position(self.get_abs_x(0.47) + (self.get_abs_x(0.0366) * \
3942 (i+ self.movie.get_rating())), self.get_abs_y(0.17))
3943 tex.set_size(self.get_abs_x(0.024), self.get_abs_y(0.04))
3944
3945=== modified file 'entertainerlib/gui/screens/music.py'
3946--- entertainerlib/frontend/gui/screens/music.py 2009-05-11 11:43:56 +0000
3947+++ entertainerlib/gui/screens/music.py 2010-01-23 03:34:11 +0000
3948@@ -1,12 +1,13 @@
3949 # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
3950 '''Music - Screen allows user to select music he/she wants to listen'''
3951
3952-from entertainerlib.frontend.gui.screens.screen import Screen
3953-from entertainerlib.frontend.gui.tabs.albums_tab import AlbumsTab
3954-from entertainerlib.frontend.gui.tabs.artists_tab import ArtistsTab
3955-from entertainerlib.frontend.gui.widgets.label import Label
3956-from entertainerlib.frontend.gui.widgets.texture import Texture
3957-from entertainerlib.frontend.medialibrary.playlist import Playlist
3958+from entertainerlib.gui.screens.screen import Screen
3959+from entertainerlib.gui.tabs.albums_tab import AlbumsTab
3960+from entertainerlib.gui.tabs.artists_tab import ArtistsTab
3961+from entertainerlib.gui.widgets.label import Label
3962+from entertainerlib.gui.widgets.texture import Texture
3963+from entertainerlib.client.medialibrary.playlist import Playlist
3964+
3965
3966 class Music(Screen):
3967 '''Screen that allows user to browse music library content.'''
3968
3969=== modified file 'entertainerlib/gui/screens/photo.py'
3970--- entertainerlib/frontend/gui/screens/photo.py 2009-05-21 16:12:09 +0000
3971+++ entertainerlib/gui/screens/photo.py 2010-01-23 03:34:11 +0000
3972@@ -4,9 +4,9 @@
3973 import clutter
3974 import gobject
3975
3976-from entertainerlib.frontend.gui.screens.screen import Screen
3977-from entertainerlib.frontend.gui.widgets.label import Label
3978-from entertainerlib.frontend.gui.widgets.texture import Texture
3979+from entertainerlib.gui.screens.screen import Screen
3980+from entertainerlib.gui.widgets.label import Label
3981+from entertainerlib.gui.widgets.texture import Texture
3982
3983 class Photo(Screen):
3984 '''Screen displays photograph in fullscreen and allows user to zoom in.'''
3985@@ -18,8 +18,8 @@
3986 def __init__(self, current_photo_index, images):
3987 Screen.__init__(self, 'Photo')
3988
3989- self.animate = self.config.show_effects()
3990- self.slideshow_step = self.config.get_slideshow_step()
3991+ self.animate = self.config.show_effects
3992+ self.slideshow_step = self.config.slideshow_step
3993
3994 self.zoom_level = 1
3995 self.index = current_photo_index
3996@@ -30,8 +30,8 @@
3997
3998 # Create black background
3999 self.background = clutter.Rectangle()
4000- self.background.set_size(self.config.get_stage_width(),
4001- self.config.get_stage_height())
4002+ self.background.set_size(
4003+ self.config.stage_width, self.config.stage_height)
4004 self.background.set_color((0, 0, 0, 255))
4005 self.add(self.background)
4006
4007@@ -60,9 +60,9 @@
4008 self.texture = Texture(image.get_filename())
4009 self._scale_image(self.texture)
4010
4011- timeline = clutter.Timeline(fps=120, duration=1000)
4012- alpha = clutter.Alpha(timeline, clutter.smoothstep_inc_func)
4013- self.opacity_behaviour = clutter.BehaviourOpacity(alpha=alpha,
4014+ timeline = clutter.Timeline(1000)
4015+ alpha = clutter.Alpha(timeline, clutter.EASE_IN_OUT_SINE)
4016+ self.opacity_behaviour = clutter.BehaviourOpacity(alpha=alpha,
4017 opacity_start=0, opacity_end=255)
4018 self.opacity_behaviour.apply(self.texture)
4019 self.texture.set_opacity(0)
4020@@ -79,34 +79,33 @@
4021 # Center position when zoomed
4022 width = texture.get_width()
4023 height = texture.get_height()
4024- x_ratio = self.config.get_stage_width() / float(width)
4025- y_ratio = self.config.get_stage_height() / float(height)
4026+ x_ratio = self.config.stage_width / float(width)
4027+ y_ratio = self.config.stage_height / float(height)
4028
4029 if x_ratio > y_ratio:
4030 texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER)
4031 texture.set_scale(
4032- self.config.get_stage_height() / float(height) * zoom_level,
4033- self.config.get_stage_height() / float(height) * zoom_level)
4034+ self.config.stage_height / float(height) * zoom_level,
4035+ self.config.stage_height / float(height) * zoom_level)
4036 texture.set_anchor_point(0, 0)
4037
4038 if zoom_level == 1: # Center image if in normal size
4039 new_width = int(width *
4040- (self.config.get_stage_height() / float(height)))
4041+ (self.config.stage_height / float(height)))
4042 new_x = int(
4043- (self.config.get_stage_width() - new_width) / float(2))
4044+ (self.config.stage_width - new_width) / float(2))
4045 texture.set_position(new_x, 0)
4046 else:
4047 texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER)
4048 texture.set_scale(
4049- self.config.get_stage_width() / float(width) * zoom_level,
4050- self.config.get_stage_width() / float(width) * zoom_level)
4051+ self.config.stage_width / float(width) * zoom_level,
4052+ self.config.stage_width / float(width) * zoom_level)
4053 texture.set_anchor_point(0, 0)
4054
4055 if zoom_level == 1: # Center image if in normal size
4056 new_height = int(
4057- height * (self.config.get_stage_width() / float(width)))
4058- new_y = int(
4059- (self.config.get_stage_height() - new_height) / float(2))
4060+ height * (self.config.stage_width / float(width)))
4061+ new_y = int((self.config.stage_height - new_height) / float(2))
4062 texture.set_position(0, new_y)
4063
4064 def is_interested_in_play_action(self):
4065
4066=== modified file 'entertainerlib/gui/screens/photo_albums.py'
4067--- entertainerlib/frontend/gui/screens/photo_albums.py 2009-07-19 19:27:31 +0000
4068+++ entertainerlib/gui/screens/photo_albums.py 2010-01-23 03:34:11 +0000
4069@@ -6,13 +6,12 @@
4070 import gobject
4071 import clutter
4072
4073-from entertainerlib.frontend.gui.screens.screen import Screen
4074-from entertainerlib.frontend.gui.widgets.eyecandy_texture import (
4075- EyeCandyTexture)
4076-from entertainerlib.frontend.gui.widgets.label import Label
4077-from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
4078-from entertainerlib.frontend.gui.widgets.texture import Texture
4079-from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
4080+from entertainerlib.gui.screens.screen import Screen
4081+from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture
4082+from entertainerlib.gui.widgets.label import Label
4083+from entertainerlib.gui.widgets.list_indicator import ListIndicator
4084+from entertainerlib.gui.widgets.texture import Texture
4085+from entertainerlib.gui.widgets.text_menu import TextMenu
4086
4087 class PhotoAlbums(Screen):
4088 '''Screen contains a list of photo albums and album previews.'''
4089@@ -159,15 +158,15 @@
4090
4091 new = self._create_album_preview(album)
4092
4093- if self.config.show_effects():
4094+ if self.config.show_effects:
4095 old = self.preview
4096 new.set_opacity(0)
4097 self.preview = new
4098 self.add(self.preview)
4099
4100 #Fade out timeline
4101- timeline1 = clutter.Timeline(10, 26)
4102- alpha1 = clutter.Alpha(timeline1, clutter.smoothstep_inc_func)
4103+ timeline1 = clutter.Timeline(500)
4104+ alpha1 = clutter.Alpha(timeline1, clutter.EASE_IN_OUT_SINE)
4105 self.out_opacity = clutter.BehaviourOpacity(255, 0, alpha1)
4106 self.out_opacity.apply(old)
4107
4108@@ -175,8 +174,8 @@
4109 old)
4110
4111 # Fade in timeline
4112- timeline2 = clutter.Timeline(10, 26)
4113- alpha2 = clutter.Alpha(timeline2, clutter.smoothstep_inc_func)
4114+ timeline2 = clutter.Timeline(500)
4115+ alpha2 = clutter.Alpha(timeline2, clutter.EASE_IN_OUT_SINE)
4116 self.in_opacity = clutter.BehaviourOpacity(0, 255, alpha2)
4117 self.in_opacity.apply(new)
4118
4119@@ -211,16 +210,16 @@
4120 """
4121 if len(self.preview_textures)<=1:
4122 self.preview_textures[0].set_opacity(255)
4123- elif self.config.show_effects():
4124+ elif self.config.show_effects:
4125 #Fade out timeline
4126- fade_out = clutter.Timeline(40, 26)
4127- alpha_out = clutter.Alpha(fade_out, clutter.smoothstep_inc_func)
4128+ fade_out = clutter.Timeline(500)
4129+ alpha_out = clutter.Alpha(fade_out, clutter.EASE_IN_OUT_SINE)
4130 self.out_behaviour = clutter.BehaviourOpacity(255, 0, alpha_out)
4131 self.out_behaviour.apply(self.preview_textures[0])
4132
4133 # Fade in timeline
4134- fade_in = clutter.Timeline(40, 26)
4135- alpha_in = clutter.Alpha(fade_in, clutter.smoothstep_inc_func)
4136+ fade_in = clutter.Timeline(500)
4137+ alpha_in = clutter.Alpha(fade_in, clutter.EASE_IN_OUT_SINE)
4138 self.in_behaviour = clutter.BehaviourOpacity(0, 255, alpha_in)
4139 self.in_behaviour.apply(self.preview_textures[1])
4140
4141
4142=== modified file 'entertainerlib/gui/screens/photographs.py'
4143--- entertainerlib/frontend/gui/screens/photographs.py 2009-07-14 11:06:21 +0000
4144+++ entertainerlib/gui/screens/photographs.py 2010-01-23 03:34:11 +0000
4145@@ -3,13 +3,12 @@
4146
4147 import pango
4148
4149-from entertainerlib.frontend.gui.screens.screen import Screen
4150-from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu
4151-from entertainerlib.frontend.gui.widgets.label import Label
4152-from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
4153-from entertainerlib.frontend.gui.widgets.loading_animation import (
4154- LoadingAnimation)
4155-from entertainerlib.frontend.gui.widgets.texture import Texture
4156+from entertainerlib.gui.screens.screen import Screen
4157+from entertainerlib.gui.widgets.image_menu import ImageMenu
4158+from entertainerlib.gui.widgets.label import Label
4159+from entertainerlib.gui.widgets.list_indicator import ListIndicator
4160+from entertainerlib.gui.widgets.loading_animation import LoadingAnimation
4161+from entertainerlib.gui.widgets.texture import Texture
4162
4163 class Photographs(Screen):
4164 '''Screen displays a grid of selectable photograph thumbnails.'''
4165
4166=== modified file 'entertainerlib/gui/screens/question.py'
4167--- entertainerlib/frontend/gui/screens/question.py 2009-06-27 09:03:33 +0000
4168+++ entertainerlib/gui/screens/question.py 2010-01-23 03:34:11 +0000
4169@@ -1,9 +1,9 @@
4170 # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
4171 '''Question - Screen displays a question label and specified buttons.'''
4172
4173-from entertainerlib.frontend.gui.screens.screen import Screen
4174-from entertainerlib.frontend.gui.widgets.label import Label
4175-from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
4176+from entertainerlib.gui.screens.screen import Screen
4177+from entertainerlib.gui.widgets.label import Label
4178+from entertainerlib.gui.widgets.text_menu import TextMenu
4179
4180 class Question(Screen):
4181 '''Screen is displayed when the application needs to ask a close ended
4182
4183=== modified file 'entertainerlib/gui/screens/rss.py'
4184--- entertainerlib/frontend/gui/screens/rss.py 2009-07-19 19:27:31 +0000
4185+++ entertainerlib/gui/screens/rss.py 2010-01-23 03:34:11 +0000
4186@@ -3,12 +3,12 @@
4187
4188 import pango
4189
4190-from entertainerlib.frontend.gui.screens.screen import Screen
4191-from entertainerlib.frontend.gui.widgets.label import Label
4192-from entertainerlib.frontend.gui.widgets.texture import Texture
4193-from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
4194-from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
4195-from entertainerlib.utils.feed_utils import FeedEntryParser
4196+from entertainerlib.gui.screens.screen import Screen
4197+from entertainerlib.gui.widgets.label import Label
4198+from entertainerlib.gui.widgets.texture import Texture
4199+from entertainerlib.gui.widgets.text_menu import TextMenu
4200+from entertainerlib.gui.widgets.list_indicator import ListIndicator
4201+from entertainerlib.backend.components.feeds.feed_utils import FeedEntryParser
4202
4203 class Rss(Screen):
4204 '''Screen displays RSS-feed titles and number of entries.'''
4205
4206=== modified file 'entertainerlib/gui/screens/screen.py'
4207--- entertainerlib/frontend/gui/screens/screen.py 2009-05-28 20:15:47 +0000
4208+++ entertainerlib/gui/screens/screen.py 2010-01-23 03:34:11 +0000
4209@@ -6,9 +6,9 @@
4210 import clutter
4211
4212 from entertainerlib.exceptions import ScreenException
4213-from entertainerlib.frontend.gui.user_event import UserEvent
4214-from entertainerlib.frontend.gui.widgets.base import Base
4215-from entertainerlib.frontend.gui.widgets.tab_group import TabGroup
4216+from entertainerlib.gui.user_event import UserEvent
4217+from entertainerlib.gui.widgets.base import Base
4218+from entertainerlib.gui.widgets.tab_group import TabGroup
4219
4220 class Screen(Base, clutter.Group):
4221 """
4222@@ -61,9 +61,7 @@
4223 })
4224
4225 rect = clutter.Rectangle()
4226- rect.set_size(
4227- self.config.get_stage_width(),
4228- self.config.get_stage_height())
4229+ rect.set_size(self.config.stage_width, self.config.stage_height)
4230 rect.hide()
4231 self.add(rect)
4232
4233
4234=== modified file 'entertainerlib/gui/screens/tv_episodes.py'
4235--- entertainerlib/frontend/gui/screens/tv_episodes.py 2009-07-21 22:22:53 +0000
4236+++ entertainerlib/gui/screens/tv_episodes.py 2010-01-23 03:34:11 +0000
4237@@ -4,13 +4,12 @@
4238 import gtk
4239 import pango
4240
4241-from entertainerlib.frontend.gui.screens.screen import Screen
4242-from entertainerlib.frontend.gui.widgets.eyecandy_texture import (
4243- EyeCandyTexture)
4244-from entertainerlib.frontend.gui.widgets.label import Label
4245-from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
4246-from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
4247-from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea
4248+from entertainerlib.gui.screens.screen import Screen
4249+from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture
4250+from entertainerlib.gui.widgets.label import Label
4251+from entertainerlib.gui.widgets.list_indicator import ListIndicator
4252+from entertainerlib.gui.widgets.text_menu import TextMenu
4253+from entertainerlib.gui.widgets.scroll_area import ScrollArea
4254
4255 class TvEpisodes(Screen):
4256 '''Screen contains list of all episodes of one specific season.'''
4257@@ -101,6 +100,7 @@
4258 self.menu.selected_userdata.get_title(),
4259 font_weight="bold")
4260 self.title.set_ellipsize(pango.ELLIPSIZE_END)
4261+ self.title.set_line_wrap(False)
4262 self.title.width = 0.4
4263 self.add(self.title)
4264
4265
4266=== modified file 'entertainerlib/gui/screens/tv_series.py'
4267--- entertainerlib/frontend/gui/screens/tv_series.py 2009-07-12 07:57:05 +0000
4268+++ entertainerlib/gui/screens/tv_series.py 2010-01-23 03:34:11 +0000
4269@@ -3,12 +3,11 @@
4270
4271 import gtk
4272
4273-from entertainerlib.frontend.gui.screens.screen import Screen
4274-from entertainerlib.frontend.gui.widgets.eyecandy_texture import (
4275- EyeCandyTexture)
4276-from entertainerlib.frontend.gui.widgets.label import Label
4277-from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
4278-from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
4279+from entertainerlib.gui.screens.screen import Screen
4280+from entertainerlib.gui.widgets.eyecandy_texture import EyeCandyTexture
4281+from entertainerlib.gui.widgets.label import Label
4282+from entertainerlib.gui.widgets.list_indicator import ListIndicator
4283+from entertainerlib.gui.widgets.text_menu import TextMenu
4284
4285 class TvSeries(Screen):
4286 '''Screen that contains all seasons of one TV series.'''
4287
4288=== modified file 'entertainerlib/gui/screens/video.py'
4289--- entertainerlib/frontend/gui/screens/video.py 2009-05-06 03:40:22 +0000
4290+++ entertainerlib/gui/screens/video.py 2010-01-23 03:34:11 +0000
4291@@ -1,11 +1,12 @@
4292 # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
4293 '''Video - Screen allows user to browse video library content'''
4294
4295-from entertainerlib.frontend.gui.screens.screen import Screen
4296-from entertainerlib.frontend.gui.tabs.movies_tab import MoviesTab
4297-from entertainerlib.frontend.gui.tabs.series_tab import SeriesTab
4298-from entertainerlib.frontend.gui.tabs.video_clips_tab import VideoClipsTab
4299-from entertainerlib.frontend.gui.widgets.label import Label
4300+from entertainerlib.gui.screens.screen import Screen
4301+from entertainerlib.gui.tabs.movies_tab import MoviesTab
4302+from entertainerlib.gui.tabs.series_tab import SeriesTab
4303+from entertainerlib.gui.tabs.video_clips_tab import VideoClipsTab
4304+from entertainerlib.gui.widgets.label import Label
4305+
4306
4307 class Video(Screen):
4308 '''Screen contains tabs for different video types in the video library.'''
4309
4310=== modified file 'entertainerlib/gui/screens/video_osd.py'
4311--- entertainerlib/frontend/gui/screens/video_osd.py 2009-05-25 11:07:55 +0000
4312+++ entertainerlib/gui/screens/video_osd.py 2010-01-23 03:34:11 +0000
4313@@ -4,10 +4,11 @@
4314 import gobject
4315 import clutter
4316
4317-from entertainerlib.frontend.gui.screens.screen import Screen
4318-from entertainerlib.frontend.gui.user_event import UserEvent
4319-from entertainerlib.frontend.gui.widgets.progress_bar import ProgressBar
4320-from entertainerlib.frontend.gui.widgets.texture import Texture
4321+from entertainerlib.gui.screens.screen import Screen
4322+from entertainerlib.gui.user_event import UserEvent
4323+from entertainerlib.gui.widgets.progress_bar import ProgressBar
4324+from entertainerlib.gui.widgets.texture import Texture
4325+
4326
4327 class VideoOSD(Screen):
4328 '''Screen is displayed when video is being watched.
4329@@ -54,23 +55,38 @@
4330 '''Create the pause, seek-backward & seek-forward textures.'''
4331 self.pause_texture = Texture(
4332 self.theme.getImage("media-playback-pause"), 0.5, 0.5)
4333- self.pause_texture.set_anchor_point_from_gravity(
4334- clutter.GRAVITY_CENTER)
4335+ self.pause_texture.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER)
4336 self.pause_texture.hide()
4337 self.add(self.pause_texture)
4338
4339- pause_timeline = clutter.Timeline(fps=20, duration=2000)
4340- pause_timeline.set_loop(True)
4341- alpha_pause = clutter.Alpha(pause_timeline, clutter.sine_func)
4342+ pause_in_time = clutter.Timeline(1000)
4343+ in_alpha_pause = clutter.Alpha(pause_in_time, clutter.EASE_IN_OUT_SINE)
4344
4345- self.pause_opacity = clutter.BehaviourOpacity(alpha=alpha_pause,
4346+ self.pause_in_opacity = clutter.BehaviourOpacity(alpha=in_alpha_pause,
4347 opacity_start=100, opacity_end=255)
4348- self.pause_scale = clutter.BehaviourScale(1.0, 1.0, 1.4, 1.4,
4349- alpha_pause)
4350-
4351- self.pause_opacity.apply(self.pause_texture)
4352- self.pause_scale.apply(self.pause_texture)
4353- pause_timeline.start()
4354+ self.pause_in_scale = clutter.BehaviourScale(1.0, 1.0, 1.4, 1.4,
4355+ in_alpha_pause)
4356+
4357+ self.pause_in_opacity.apply(self.pause_texture)
4358+ self.pause_in_scale.apply(self.pause_texture)
4359+
4360+ pause_out_time = clutter.Timeline(1000)
4361+ out_alpha_pause = clutter.Alpha(pause_out_time,
4362+ clutter.EASE_IN_OUT_SINE)
4363+
4364+ self.pause_out_opacity = clutter.BehaviourOpacity(alpha=out_alpha_pause,
4365+ opacity_start=255, opacity_end=100)
4366+ self.pause_out_scale = clutter.BehaviourScale(1.4, 1.4, 1.0, 1.0,
4367+ out_alpha_pause)
4368+
4369+ self.pause_out_opacity.apply(self.pause_texture)
4370+ self.pause_out_scale.apply(self.pause_texture)
4371+
4372+ self.score = clutter.Score()
4373+ self.score.set_loop(True)
4374+ self.score.append(timeline=pause_in_time)
4375+ self.score.append(timeline=pause_out_time, parent=pause_in_time)
4376+ self.score.start()
4377
4378 self.seekbackward_texture = Texture(
4379 self.theme.getImage("media-seek-backward"), 0.1, 0.5)
4380@@ -79,9 +95,9 @@
4381 self.seekbackward_texture.set_opacity(0)
4382 self.add(self.seekbackward_texture)
4383
4384- self.seekbackward_timeline = clutter.Timeline(fps=120, duration=1000)
4385+ self.seekbackward_timeline = clutter.Timeline(1000)
4386 alpha_seekbackward = clutter.Alpha(self.seekbackward_timeline,
4387- clutter.smoothstep_inc_func)
4388+ clutter.EASE_IN_OUT_SINE)
4389
4390 self.seekbackward_opacity = clutter.BehaviourOpacity(
4391 alpha=alpha_seekbackward, opacity_start=255, opacity_end=0)
4392@@ -94,9 +110,9 @@
4393 self.seekforward_texture.set_opacity(0)
4394 self.add(self.seekforward_texture)
4395
4396- self.seekforward_timeline = clutter.Timeline(fps=120, duration=1000)
4397+ self.seekforward_timeline = clutter.Timeline(1000)
4398 alpha_seekforward = clutter.Alpha(self.seekforward_timeline,
4399- clutter.smoothstep_inc_func)
4400+ clutter.EASE_IN_OUT_SINE)
4401
4402 self.seekforward_opacity = clutter.BehaviourOpacity(
4403 alpha=alpha_seekforward, opacity_start=255, opacity_end=0)
4404@@ -130,8 +146,8 @@
4405 self.timeout_key = None # This is used when canceling timeouts
4406 for texture in self.aspect_textures:
4407 texture.position = (
4408- float(self.config.get_stage_width() - texture.get_width()) /
4409- (self.config.get_stage_width() * 2), 0.67)
4410+ float(self.config.stage_width - texture.get_width()) /
4411+ (self.config.stage_width * 2), 0.67)
4412
4413 def _hide_aspect_ratio_logo(self, number):
4414 '''
4415
4416=== modified file 'entertainerlib/gui/screens/weather.py'
4417--- entertainerlib/frontend/gui/screens/weather.py 2009-07-12 19:10:32 +0000
4418+++ entertainerlib/gui/screens/weather.py 2010-01-23 03:34:11 +0000
4419@@ -3,10 +3,11 @@
4420
4421 import clutter
4422
4423-from entertainerlib.frontend.gui.screens.screen import Screen
4424-from entertainerlib.frontend.gui.widgets.label import Label
4425-from entertainerlib.frontend.gui.widgets.texture import Texture
4426-from entertainerlib.utils.weather import Weather
4427+from entertainerlib.gui.screens.screen import Screen
4428+from entertainerlib.gui.widgets.label import Label
4429+from entertainerlib.gui.widgets.texture import Texture
4430+from entertainerlib.weather import Weather
4431+
4432
4433 class WeatherScreen(Screen):
4434 '''Screen to display the user's set weather location.'''
4435@@ -17,7 +18,7 @@
4436 # Screen Title
4437 self.add(Label(0.13, "screentitle", 0, 0.87, _("Weather")))
4438
4439- self.weather = Weather(self.config.get_weather_location())
4440+ self.weather = Weather(self.config.weather_location)
4441
4442 location = Label(0.04167, "text", 0.50, 0.13, self.weather.location,
4443 font_weight="bold")
4444
4445=== renamed file 'entertainerlib/utils/system_tray_icon.py' => 'entertainerlib/gui/system_tray_icon.py'
4446--- entertainerlib/utils/system_tray_icon.py 2009-05-06 02:58:08 +0000
4447+++ entertainerlib/gui/system_tray_icon.py 2010-01-23 03:34:11 +0000
4448@@ -4,19 +4,16 @@
4449 import os
4450
4451 import gtk
4452-import gtk.glade
4453-
4454-from entertainerlib.utils.log_viewer import LogViewer
4455-from entertainerlib.utils.preferences_dialog import PreferencesDialog
4456-from entertainerlib.utils.content_management_dialog import (
4457- ContentManagementDialog)
4458-from entertainerlib.utils.configuration import Configuration
4459+
4460+from entertainerlib.configuration import Configuration
4461+from entertainerlib.dialog import ManagerDialog, LogViewer
4462+
4463
4464 class SystemTrayIcon:
4465 """Implements system tray icon for entertainer."""
4466
4467 FILE_DIR = os.path.dirname(__file__)
4468- GLADE_DIR = os.path.join(FILE_DIR, "glade")
4469+ UI_DIR = os.path.join(FILE_DIR, '..', 'uis')
4470
4471 def __init__(self, quit_callback, toggle_interface_visibility_callback):
4472 '''Create the system tray icon and pop-up menu for it.'''
4473@@ -32,24 +29,24 @@
4474 self.icon_widget = gtk.StatusIcon()
4475 self.icon_widget.set_tooltip(_("Entertainer Server"))
4476
4477- # Load glade files
4478- self.menu_widgets = gtk.glade.XML(
4479- os.path.join(self.GLADE_DIR, "system_tray_icon_menu.glade"))
4480+ # Load UI with gtk.Builder
4481+ uifile = os.path.join(self.UI_DIR, 'system_tray_icon_menu.ui')
4482+ self.menu_widgets = gtk.Builder()
4483+ self.menu_widgets.set_translation_domain('entertainer')
4484+ self.menu_widgets.add_from_file(uifile)
4485
4486 # Bind menu signals
4487- callback_dic = {"on_menuitem_frontend_activate"
4488- : self.on_menuitem_frontend_activate,
4489- "on_menuitem_preferences_activate"
4490- : self.on_menuitem_preferences_activate,
4491- "on_menuitem_content_management_activate"
4492- : self.on_menuitem_content_management_activate,
4493+ callback_dic = {"on_menuitem_client_activate"
4494+ : self.on_menuitem_client_activate,
4495+ "on_menuitem_manager_activate"
4496+ : self.on_menuitem_manager_activate,
4497 "on_menuitem_log_viewer_activate"
4498 : self.on_menuitem_log_viewer_activate,
4499 "on_menuitem_quit_activate"
4500 : self.on_menuitem_quit_activate
4501 }
4502- self.menu_widgets.signal_autoconnect(callback_dic)
4503- self.popup = self.menu_widgets.get_widget("SystemTrayIconMenu")
4504+ self.menu_widgets.connect_signals(callback_dic)
4505+ self.popup = self.menu_widgets.get_object("SystemTrayIconMenu")
4506
4507 # Check if running from a branch to set the tray icon
4508 if (os.path.exists(self.tray_icon_url)):
4509@@ -62,7 +59,7 @@
4510 self.icon_widget.connect('popup-menu', self.open_popup_menu)
4511
4512 def systray_icon_activated(self, widget, data= None):
4513- """Switch visibility of frontend when system tray icon is clicked"""
4514+ """Switch visibility of client when system tray icon is clicked"""
4515 self.toggle_interface_visibility_callback()
4516
4517 def open_popup_menu(self, widget, button, time, data = None):
4518@@ -70,17 +67,13 @@
4519 self.popup.show_all()
4520 self.popup.popup(None, None, None, 3, time)
4521
4522- def on_menuitem_frontend_activate(self, widget):
4523- """Execute frontend here if not running. Show if running"""
4524- self.set_frontend_visible(True)
4525-
4526- def on_menuitem_preferences_activate(self, widget):
4527- """Executes preferences-tool."""
4528- PreferencesDialog(False)
4529-
4530- def on_menuitem_content_management_activate(self, widget):
4531- """Executes content-management-tool"""
4532- ContentManagementDialog(False)
4533+ def on_menuitem_client_activate(self, widget):
4534+ """Execute client here if not running. Show if running"""
4535+ self.set_client_visible(True)
4536+
4537+ def on_menuitem_manager_activate(self, widget):
4538+ '''Executes the manager dialog.'''
4539+ ManagerDialog(False)
4540
4541 def on_menuitem_log_viewer_activate(self, widget):
4542 """Display log viewer dialog"""
4543
4544=== modified file 'entertainerlib/gui/tabs/albums_tab.py'
4545--- entertainerlib/frontend/gui/tabs/albums_tab.py 2009-07-14 11:06:21 +0000
4546+++ entertainerlib/gui/tabs/albums_tab.py 2010-01-23 03:34:11 +0000
4547@@ -4,12 +4,11 @@
4548
4549 import pango
4550
4551-from entertainerlib.frontend.gui.tabs.tab import Tab
4552-from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu
4553-from entertainerlib.frontend.gui.widgets.label import Label
4554-from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
4555-from entertainerlib.frontend.gui.widgets.loading_animation import (
4556- LoadingAnimation)
4557+from entertainerlib.gui.tabs.tab import Tab
4558+from entertainerlib.gui.widgets.image_menu import ImageMenu
4559+from entertainerlib.gui.widgets.label import Label
4560+from entertainerlib.gui.widgets.list_indicator import ListIndicator
4561+from entertainerlib.gui.widgets.loading_animation import LoadingAnimation
4562
4563 class AlbumsTab(Tab):
4564 '''Tab to show album listings'''
4565@@ -55,11 +54,13 @@
4566 # Create album information (displays current menuitem information)
4567 self.album_title = Label(0.045, "title", 0.22, 0.79, "")
4568 self.album_title.set_ellipsize(pango.ELLIPSIZE_END)
4569+ self.album_title.set_line_wrap(False)
4570 self.album_title.width = 0.366
4571 self.add(self.album_title)
4572
4573 self.album_artist = Label(0.037, "subtitle", 0.22, 0.86, "")
4574 self.album_artist.set_ellipsize(pango.ELLIPSIZE_END)
4575+ self.album_artist.set_line_wrap(False)
4576 self.album_artist.width = 0.366
4577 self.add(self.album_artist)
4578
4579
4580=== modified file 'entertainerlib/gui/tabs/artists_tab.py'
4581--- entertainerlib/frontend/gui/tabs/artists_tab.py 2009-07-16 20:31:22 +0000
4582+++ entertainerlib/gui/tabs/artists_tab.py 2010-01-23 03:34:11 +0000
4583@@ -4,12 +4,11 @@
4584
4585 import pango
4586
4587-from entertainerlib.frontend.gui.tabs.tab import Tab
4588-from entertainerlib.frontend.gui.widgets.label import Label
4589-from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
4590-from entertainerlib.frontend.gui.widgets.loading_animation import (
4591- LoadingAnimation)
4592-from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
4593+from entertainerlib.gui.tabs.tab import Tab
4594+from entertainerlib.gui.widgets.label import Label
4595+from entertainerlib.gui.widgets.list_indicator import ListIndicator
4596+from entertainerlib.gui.widgets.loading_animation import LoadingAnimation
4597+from entertainerlib.gui.widgets.text_menu import TextMenu
4598
4599 class ArtistsTab(Tab):
4600 '''Tab for the music screen to show artist listings'''
4601@@ -38,6 +37,7 @@
4602 # Create artist label
4603 self.artist_title = Label(0.0416, "title", 0.22, 0.794, "")
4604 self.artist_title.set_ellipsize(pango.ELLIPSIZE_END)
4605+ self.artist_title.set_line_wrap(False)
4606 self.artist_title.width = 0.366
4607 self.add(self.artist_title)
4608
4609@@ -47,7 +47,7 @@
4610 self.artist_tracks = Label(0.0365, "subtitle", 0.22, 0.911, "")
4611 self.add(self.artist_tracks)
4612
4613- # Create artis menu list indicator
4614+ # Create artist menu list indicator
4615 self.li = ListIndicator(0.77, 0.8, 0.18, 0.045,
4616 ListIndicator.VERTICAL)
4617 self.li.set_maximum(len(artists))
4618
4619=== modified file 'entertainerlib/gui/tabs/lyrics_tab.py'
4620--- entertainerlib/frontend/gui/tabs/lyrics_tab.py 2009-07-22 20:46:29 +0000
4621+++ entertainerlib/gui/tabs/lyrics_tab.py 2010-01-23 03:34:11 +0000
4622@@ -4,11 +4,11 @@
4623 import clutter
4624 import pango
4625
4626-from entertainerlib.frontend.gui.tabs.tab import Tab
4627-from entertainerlib.frontend.gui.widgets.label import Label
4628-from entertainerlib.frontend.gui.widgets.loading_animation import (
4629+from entertainerlib.gui.tabs.tab import Tab
4630+from entertainerlib.gui.widgets.label import Label
4631+from entertainerlib.gui.widgets.loading_animation import (
4632 LoadingAnimation)
4633-from entertainerlib.frontend.gui.widgets.scroll_area import ScrollArea
4634+from entertainerlib.gui.widgets.scroll_area import ScrollArea
4635
4636 class LyricsTab(Tab):
4637 '''Tab for the audio play screen to show lyrics'''
4638
4639=== modified file 'entertainerlib/gui/tabs/movies_tab.py'
4640--- entertainerlib/frontend/gui/tabs/movies_tab.py 2009-07-14 11:06:21 +0000
4641+++ entertainerlib/gui/tabs/movies_tab.py 2010-01-23 03:34:11 +0000
4642@@ -3,12 +3,11 @@
4643
4644 import pango
4645
4646-from entertainerlib.frontend.gui.tabs.tab import Tab
4647-from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu
4648-from entertainerlib.frontend.gui.widgets.label import Label
4649-from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
4650-from entertainerlib.frontend.gui.widgets.loading_animation import (
4651- LoadingAnimation)
4652+from entertainerlib.gui.tabs.tab import Tab
4653+from entertainerlib.gui.widgets.image_menu import ImageMenu
4654+from entertainerlib.gui.widgets.label import Label
4655+from entertainerlib.gui.widgets.list_indicator import ListIndicator
4656+from entertainerlib.gui.widgets.loading_animation import LoadingAnimation
4657
4658 class MoviesTab(Tab):
4659 """
4660@@ -95,11 +94,13 @@
4661 self.movie_title = Label(0.042, "title", 0.2, 0.75, "",
4662 font_weight="bold")
4663 self.movie_title.set_ellipsize(pango.ELLIPSIZE_END)
4664+ self.movie_title.set_line_wrap(False)
4665 self.movie_title.width = 0.5
4666 self.add(self.movie_title)
4667
4668 self.movie_info = Label(0.034, "subtitle", 0.2, 0.8, "")
4669 self.movie_info.set_ellipsize(pango.ELLIPSIZE_END)
4670+ self.movie_info.set_line_wrap(False)
4671 self.movie_info.width = 0.5
4672 self.add(self.movie_info)
4673
4674
4675=== modified file 'entertainerlib/gui/tabs/playing_tab.py'
4676--- entertainerlib/frontend/gui/tabs/playing_tab.py 2009-05-26 17:32:56 +0000
4677+++ entertainerlib/gui/tabs/playing_tab.py 2010-01-23 03:34:11 +0000
4678@@ -3,9 +3,9 @@
4679
4680 import pango
4681
4682-from entertainerlib.frontend.gui.tabs.tab import Tab
4683-from entertainerlib.frontend.gui.widgets.label import Label
4684-from entertainerlib.frontend.gui.widgets.progress_bar import ProgressBar
4685+from entertainerlib.gui.tabs.tab import Tab
4686+from entertainerlib.gui.widgets.label import Label
4687+from entertainerlib.gui.widgets.progress_bar import ProgressBar
4688
4689 class PlayingTab(Tab):
4690 '''Tab for the audio play screen to show currently playing audio.'''
4691
4692=== modified file 'entertainerlib/gui/tabs/series_tab.py'
4693--- entertainerlib/frontend/gui/tabs/series_tab.py 2009-07-14 11:06:21 +0000
4694+++ entertainerlib/gui/tabs/series_tab.py 2010-01-23 03:34:11 +0000
4695@@ -3,12 +3,11 @@
4696
4697 import pango
4698
4699-from entertainerlib.frontend.gui.tabs.tab import Tab
4700-from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu
4701-from entertainerlib.frontend.gui.widgets.label import Label
4702-from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
4703-from entertainerlib.frontend.gui.widgets.loading_animation import (
4704- LoadingAnimation)
4705+from entertainerlib.gui.tabs.tab import Tab
4706+from entertainerlib.gui.widgets.image_menu import ImageMenu
4707+from entertainerlib.gui.widgets.label import Label
4708+from entertainerlib.gui.widgets.list_indicator import ListIndicator
4709+from entertainerlib.gui.widgets.loading_animation import LoadingAnimation
4710
4711 class SeriesTab(Tab):
4712 """
4713@@ -94,6 +93,7 @@
4714 self.series_title = Label(0.042, "title", 0.2, 0.75, "",
4715 font_weight="bold")
4716 self.series_title.set_ellipsize(pango.ELLIPSIZE_END)
4717+ self.series_title.set_line_wrap(False)
4718 self.series_title.width = 0.5
4719 self.add(self.series_title)
4720
4721
4722=== modified file 'entertainerlib/gui/tabs/tab.py'
4723--- entertainerlib/frontend/gui/tabs/tab.py 2009-05-21 16:12:09 +0000
4724+++ entertainerlib/gui/tabs/tab.py 2010-01-23 03:34:11 +0000
4725@@ -4,10 +4,10 @@
4726 import clutter
4727 import gobject
4728
4729-from entertainerlib.frontend.gui.user_event import UserEvent
4730-from entertainerlib.frontend.gui.widgets.base import Base
4731-from entertainerlib.frontend.gui.widgets.label import Label
4732-from entertainerlib.frontend.gui.widgets.texture import Texture
4733+from entertainerlib.gui.user_event import UserEvent
4734+from entertainerlib.gui.widgets.base import Base
4735+from entertainerlib.gui.widgets.label import Label
4736+from entertainerlib.gui.widgets.texture import Texture
4737
4738 class Tab(Base, clutter.Group):
4739 '''
4740@@ -39,9 +39,9 @@
4741 }
4742
4743 # show/hide animation on the Tab
4744- self.timeline = clutter.Timeline(30, 60)
4745+ self.timeline = clutter.Timeline(500)
4746 self.timeline.connect('completed', self._on_timeline_completed)
4747- self.alpha = clutter.Alpha(self.timeline, clutter.smoothstep_inc_func)
4748+ self.alpha = clutter.Alpha(self.timeline, clutter.EASE_IN_OUT_SINE)
4749 self.behaviour = clutter.BehaviourOpacity(0, 255, self.alpha)
4750 self.behaviour.apply(self)
4751
4752
4753=== modified file 'entertainerlib/gui/tabs/tracks_tab.py'
4754--- entertainerlib/frontend/gui/tabs/tracks_tab.py 2009-07-16 20:31:22 +0000
4755+++ entertainerlib/gui/tabs/tracks_tab.py 2010-01-23 03:34:11 +0000
4756@@ -3,12 +3,11 @@
4757
4758 import pango
4759
4760-from entertainerlib.frontend.gui.tabs.tab import Tab
4761-from entertainerlib.frontend.gui.widgets.label import Label
4762-from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
4763-from entertainerlib.frontend.gui.widgets.loading_animation import (
4764- LoadingAnimation)
4765-from entertainerlib.frontend.gui.widgets.text_menu import TextMenu
4766+from entertainerlib.gui.tabs.tab import Tab
4767+from entertainerlib.gui.widgets.label import Label
4768+from entertainerlib.gui.widgets.list_indicator import ListIndicator
4769+from entertainerlib.gui.widgets.loading_animation import LoadingAnimation
4770+from entertainerlib.gui.widgets.text_menu import TextMenu
4771
4772 class TracksTab(Tab):
4773 '''Tab for the artist screen to show track listings'''
4774@@ -35,11 +34,13 @@
4775
4776 self.track_title = Label(0.045, "title", 0.22, 0.79, "")
4777 self.track_title.set_ellipsize(pango.ELLIPSIZE_END)
4778+ self.track_title.set_line_wrap(False)
4779 self.track_title.width = 0.366
4780 self.add(self.track_title)
4781
4782 self.track_number = Label(0.037, "subtitle", 0.22, 0.86, "")
4783 self.track_number.set_ellipsize(pango.ELLIPSIZE_END)
4784+ self.track_number.set_line_wrap(False)
4785 self.track_number.width = 0.366
4786 self.add(self.track_number)
4787
4788
4789=== modified file 'entertainerlib/gui/tabs/video_clips_tab.py'
4790--- entertainerlib/frontend/gui/tabs/video_clips_tab.py 2009-07-14 11:06:21 +0000
4791+++ entertainerlib/gui/tabs/video_clips_tab.py 2010-01-23 03:34:11 +0000
4792@@ -5,12 +5,11 @@
4793
4794 import pango
4795
4796-from entertainerlib.frontend.gui.tabs.tab import Tab
4797-from entertainerlib.frontend.gui.widgets.image_menu import ImageMenu
4798-from entertainerlib.frontend.gui.widgets.label import Label
4799-from entertainerlib.frontend.gui.widgets.list_indicator import ListIndicator
4800-from entertainerlib.frontend.gui.widgets.loading_animation import (
4801- LoadingAnimation)
4802+from entertainerlib.gui.tabs.tab import Tab
4803+from entertainerlib.gui.widgets.image_menu import ImageMenu
4804+from entertainerlib.gui.widgets.label import Label
4805+from entertainerlib.gui.widgets.list_indicator import ListIndicator
4806+from entertainerlib.gui.widgets.loading_animation import LoadingAnimation
4807
4808 class VideoClipsTab(Tab):
4809 """
4810@@ -94,11 +93,13 @@
4811 self.clip_title = Label(0.042, "title", 0.15, 0.77, "",
4812 font_weight="bold")
4813 self.clip_title.set_ellipsize(pango.ELLIPSIZE_END)
4814+ self.clip_title.set_line_wrap(False)
4815 self.clip_title.width = 0.5
4816 self.add(self.clip_title)
4817
4818 self.clip_info = Label(0.034, "subtitle", 0.15, 0.85, "")
4819 self.clip_info.set_ellipsize(pango.ELLIPSIZE_END)
4820+ self.clip_info.set_line_wrap(False)
4821 self.clip_info.width = 0.5
4822 self.add(self.clip_info)
4823
4824
4825=== renamed file 'entertainerlib/utils/theme.py' => 'entertainerlib/gui/theme.py'
4826=== modified file 'entertainerlib/gui/transitions/factory.py'
4827--- entertainerlib/frontend/gui/transitions/factory.py 2009-04-28 22:30:06 +0000
4828+++ entertainerlib/gui/transitions/factory.py 2010-01-23 03:34:11 +0000
4829@@ -2,11 +2,12 @@
4830 '''TransitionFactory - create a transition object based on the configuration
4831 that is in the user's preferences'''
4832
4833-from entertainerlib.frontend.gui.transitions.fade import Fade
4834-from entertainerlib.frontend.gui.transitions.no_effect import NoEffect
4835-from entertainerlib.frontend.gui.transitions.slide import Slide
4836-from entertainerlib.frontend.gui.transitions.zoom_and_fade import ZoomAndFade
4837-from entertainerlib.utils.configuration import Configuration
4838+from entertainerlib.gui.transitions.fade import Fade
4839+from entertainerlib.gui.transitions.no_effect import NoEffect
4840+from entertainerlib.gui.transitions.slide import Slide
4841+from entertainerlib.gui.transitions.zoom_and_fade import ZoomAndFade
4842+from entertainerlib.configuration import Configuration
4843+
4844
4845 class TransitionFactory:
4846 '''Generates a transition object based on the configuration setting'''
4847@@ -31,8 +32,8 @@
4848 'Zoom and fade' : self._generate_zoom_and_fade
4849 }
4850
4851- if self.config.show_effects():
4852- kind = self.config.transition_effect()
4853+ if self.config.show_effects:
4854+ kind = self.config.transition_effect
4855 else:
4856 kind = "No effect"
4857
4858
4859=== modified file 'entertainerlib/gui/transitions/fade.py'
4860--- entertainerlib/frontend/gui/transitions/fade.py 2009-05-06 03:40:22 +0000
4861+++ entertainerlib/gui/transitions/fade.py 2010-01-23 03:34:11 +0000
4862@@ -2,8 +2,8 @@
4863 '''Fade - Transition to fade out old screen and fade in the new screen'''
4864
4865 import clutter
4866+from entertainerlib.gui.transitions.transition import Transition
4867
4868-from entertainerlib.frontend.gui.transitions.transition import Transition
4869
4870 class Fade(Transition):
4871 """
4872
4873=== modified file 'entertainerlib/gui/transitions/no_effect.py'
4874--- entertainerlib/frontend/gui/transitions/no_effect.py 2009-05-06 03:40:22 +0000
4875+++ entertainerlib/gui/transitions/no_effect.py 2010-01-23 03:34:11 +0000
4876@@ -1,7 +1,8 @@
4877 # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
4878 '''NoEffect - Simple change transition from screen 1 to screen 2'''
4879
4880-from entertainerlib.frontend.gui.transitions.transition import Transition
4881+from entertainerlib.gui.transitions.transition import Transition
4882+
4883
4884 class NoEffect(Transition):
4885 """
4886
4887=== modified file 'entertainerlib/gui/transitions/slide.py'
4888--- entertainerlib/frontend/gui/transitions/slide.py 2009-05-06 03:40:22 +0000
4889+++ entertainerlib/gui/transitions/slide.py 2010-01-23 03:34:11 +0000
4890@@ -3,8 +3,8 @@
4891
4892 import clutter
4893
4894-from entertainerlib.frontend.gui.transitions.transition import Transition
4895-from entertainerlib.utils.configuration import Configuration
4896+from entertainerlib.gui.transitions.transition import Transition
4897+from entertainerlib.configuration import Configuration
4898
4899 class Slide(Transition):
4900 '''Move screens on and off the stage through horizontal movements.'''
4901@@ -27,7 +27,7 @@
4902 '''Execute the common sliding logic needed by each direction.
4903
4904 Direction will set the sign of width to determine which way to slide.'''
4905- width = self.config.get_stage_width() * direction
4906+ width = self.config.stage_width * direction
4907
4908 # Initialize to_screen for animation
4909 to_screen.set_position(-width, 0)
4910@@ -35,10 +35,12 @@
4911
4912 # Slide out timeline
4913 if from_screen is not None:
4914- slide_out = clutter.Timeline(20, 60)
4915- alpha_out = clutter.Alpha(slide_out, clutter.smoothstep_inc_func)
4916- self.out_behaviour = clutter.BehaviourPath(alpha_out, ((0, 0),
4917- (width, 0)))
4918+ slide_out = clutter.Timeline(500)
4919+ alpha_out = clutter.Alpha(slide_out, clutter.EASE_IN_OUT_SINE)
4920+ out_path = clutter.Path()
4921+ out_path.add_move_to(0, 0)
4922+ out_path.add_line_to(width, 0)
4923+ self.out_behaviour = clutter.BehaviourPath(alpha_out, out_path)
4924 self.out_behaviour.apply(from_screen)
4925
4926 if remove_screen:
4927@@ -46,10 +48,12 @@
4928 from_screen)
4929
4930 # Slide in timeline
4931- slide_in = clutter.Timeline(20, 60)
4932- alpha_in = clutter.Alpha(slide_in, clutter.smoothstep_inc_func)
4933- self.in_behaviour = clutter.BehaviourPath(alpha_in, ((-width, 0),
4934- (0, 0)))
4935+ slide_in = clutter.Timeline(500)
4936+ alpha_in = clutter.Alpha(slide_in, clutter.EASE_IN_OUT_SINE)
4937+ in_path = clutter.Path()
4938+ in_path.add_move_to(-width, 0)
4939+ in_path.add_line_to(0, 0)
4940+ self.in_behaviour = clutter.BehaviourPath(alpha_in, in_path)
4941 self.in_behaviour.apply(to_screen)
4942
4943 # Start transition animating
4944
4945=== modified file 'entertainerlib/gui/transitions/zoom_and_fade.py'
4946--- entertainerlib/frontend/gui/transitions/zoom_and_fade.py 2009-05-06 03:40:22 +0000
4947+++ entertainerlib/gui/transitions/zoom_and_fade.py 2010-01-23 03:34:11 +0000
4948@@ -2,8 +2,8 @@
4949 '''ZoomAndFade - Transition to zoom and fade between screens'''
4950
4951 import clutter
4952+from entertainerlib.gui.transitions.transition import Transition
4953
4954-from entertainerlib.frontend.gui.transitions.transition import Transition
4955
4956 class ZoomAndFade(Transition):
4957 """
4958
4959=== modified file 'entertainerlib/gui/user_event.py'
4960--- entertainerlib/frontend/gui/user_event.py 2009-05-29 10:33:32 +0000
4961+++ entertainerlib/gui/user_event.py 2010-01-23 03:34:11 +0000
4962@@ -40,7 +40,7 @@
4963 USE_ASPECT_RATIO_2 = 52
4964 USE_ASPECT_RATIO_3 = 53
4965 USE_ASPECT_RATIO_4 = 54
4966- QUIT_FRONTEND = 56
4967+ QUIT = 56
4968 DEFAULT_EVENT = 57
4969
4970 def __init__(self, code):
4971
4972=== modified file 'entertainerlib/gui/user_interface.py'
4973--- entertainerlib/frontend/gui/user_interface.py 2009-06-29 19:41:35 +0000
4974+++ entertainerlib/gui/user_interface.py 2010-01-23 03:34:11 +0000
4975@@ -1,5 +1,5 @@
4976 # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
4977-'''UserInterface - Main window of the Entertainer frontend'''
4978+'''UserInterface - Main window of the Entertainer client'''
4979
4980 # Clutter uses _1 to represent the 1 key and pylint complains about it
4981 # pylint: disable-msg=W0212
4982@@ -12,31 +12,32 @@
4983 import gobject
4984 import gtk
4985
4986-from entertainerlib.frontend.gui.screen_history import ScreenHistory
4987-from entertainerlib.frontend.gui.screens.factory import ScreenFactory
4988-from entertainerlib.frontend.gui.screens.screen import Screen
4989-from entertainerlib.frontend.gui.transitions.factory import TransitionFactory
4990-from entertainerlib.frontend.gui.transitions.transition import Transition
4991-from entertainerlib.frontend.gui.user_event import UserEvent
4992-from entertainerlib.frontend.gui.widgets.menu_overlay import MenuOverlay
4993-from entertainerlib.frontend.media_player import MediaPlayer
4994-from entertainerlib.frontend.gui.widgets.volume_indicator import VolumeIndicator
4995-from entertainerlib.utils.configuration import Configuration
4996-from entertainerlib.utils.logger import Logger
4997+from entertainerlib.gui.widgets.volume_indicator import VolumeIndicator
4998+from entertainerlib.gui.screen_history import ScreenHistory
4999+from entertainerlib.gui.screens.factory import ScreenFactory
5000+from entertainerlib.gui.screens.screen import Screen
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches