Consistent __create_view with other panels
[mussorgsky] / src / edit_panel.py
1 #!/usr/bin/env python2.5
2 import hildon
3 import gtk, gobject
4 from mutagen_backend import MutagenBackend
5 from player_backend import MediaPlayer
6 import album_art_spec
7 from album_art import MussorgskyAlbumArt
8 import os
9
10 # Fields in the tuple!
11 FILE_URI = 0
12 ARTIST_KEY = 2
13 TITLE_KEY = 3
14 ALBUM_KEY = 4
15 MIME_KEY = 5
16
17 class MussorgskyEditPanel (hildon.StackableWindow):
18
19     def __init__ (self, songs_list=None, albums_list=None, artists_list=None):
20         hildon.StackableWindow.__init__ (self)
21         self.set_border_width (12)
22         self.song_counter = 0
23         self.album_callback_id = -1
24         self.album_change_handler = -1
25         self.artist_change_handler = -1
26         self.writer = MutagenBackend ()
27         self.player = MediaPlayer ()
28         self.album_art_retriever = MussorgskyAlbumArt ()
29         self.albums_list = [a [0] for a in albums_list]
30         self.artists_list = [a [0] for a in artists_list]
31         self.__create_view ()
32         if (songs_list):
33             self.set_songs_list (songs_list)
34         self.update_title ()
35         self.banner = None
36
37
38     def update_title (self):
39         self.set_title ("Edit (%d/%d)" % (self.song_counter+1, len (self.songs_list)))
40
41         
42     def set_songs_list (self, songs_list):
43         if (songs_list and len (songs_list) > 0):
44             self.songs_list = songs_list
45             self.set_data_in_view (songs_list [0])
46             self.song_counter = 0
47
48     def press_back_cb (self, widget):
49         if (self.player.is_playing ()):
50             self.player.stop ()
51
52         if (self.banner and self.banner.get_property("visible")):
53             self.banner.destroy ()
54
55         if (self.album_callback_id != -1):
56             gobject.source_remove (self.album_callback_id)
57             self.album_callback_id = -1
58
59         if self.__is_view_dirty ():
60             print "Modified data. Save!"
61             self.save_metadata ()
62             
63         if (self.song_counter > 0):
64             self.song_counter -= 1
65             self.set_data_in_view (self.songs_list [self.song_counter])
66             self.update_title ()
67         else:
68             self.destroy ()
69
70     def press_next_cb (self, widget):
71         if (self.player.is_playing ()):
72             self.player.stop ()
73
74         if (self.banner and self.banner.get_property("visible")):
75             self.banner.destroy ()
76
77         if (self.album_callback_id != -1):
78             gobject.source_remove (self.album_callback_id)
79             self.album_callback_id = -1
80
81         if self.__is_view_dirty ():
82             print "Modified data. Save!"
83             self.save_metadata ()
84
85         if (self.song_counter < len (self.songs_list) -1):
86             self.song_counter += 1
87             self.set_data_in_view (self.songs_list [self.song_counter])
88             self.update_title ()
89         else:
90             self.destroy ()
91
92     def save_metadata (self):
93         # Save the data in the online model to show the appropiate data
94         # in the UI while tracker process the update.
95         song = self.songs_list [self.song_counter]
96
97         new_song = (song[FILE_URI], song[1],
98                     self.artist_button.get_value (),
99                     self.title_entry.get_text (),
100                     self.album_button.get_value (),
101                     song[MIME_KEY])
102         self.songs_list [self.song_counter] = new_song
103         try:
104             self.writer.save_metadata_on_file (new_song[FILE_URI],
105                                                new_song[MIME_KEY],
106                                                self.artist_button.get_value (),
107                                                self.title_entry.get_text (),
108                                                self.album_button.get_value ())
109         except IOError, e:
110             # This error in case of tracker returning unexistent files.
111             # Uhm.... for instance after removing a memory card we are editing!
112             dialog = gtk.MessageDialog (self,
113                                         gtk.DIALOG_DESTROY_WITH_PARENT,
114                                         gtk.MESSAGE_ERROR,
115                                         gtk.BUTTONS_CLOSE,
116                                         "%s" % str(e));
117             dialog.run ()
118
119         
120
121     def __is_view_dirty (self):
122         """
123         True if the data has been modified in the widgets
124         """
125         song = self.songs_list [self.song_counter]
126
127         return not (self.filename_data.get_text() == song[FILE_URI] and
128                     self.artist_button.get_value () == song[ARTIST_KEY] and
129                     self.title_entry.get_text () == song[TITLE_KEY] and
130                     self.album_button.get_value () == song[ALBUM_KEY] )
131         
132
133     def __create_view (self):
134         view_vbox = gtk.VBox (homogeneous=False, spacing = 12)
135
136         filename_row = gtk.HBox ()
137         filename_label = gtk.Label ("Filename:")
138         filename_row.pack_start (filename_label, expand=False, padding=12);
139         self.filename_data = gtk.Label ("")
140         filename_row.pack_start (self.filename_data, expand=True)
141
142         play_button = hildon.Button (hildon.BUTTON_STYLE_NORMAL, hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
143         img = gtk.image_new_from_stock (gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_BUTTON)
144         play_button.set_image (img)
145         play_button.connect ("clicked", self.clicked_play)
146         filename_row.pack_start (play_button, expand=False, fill=False)
147         view_vbox.pack_start (filename_row, expand=False);
148
149         central_panel = gtk.HBox (spacing=12)
150
151         table = gtk.Table (3, 2, False)
152         table.set_col_spacings (12)
153         table.set_row_spacings (12)
154
155         central_panel.pack_start (table, fill=True)
156         view_vbox.pack_start (central_panel, expand=True, fill=True)
157
158         # Title row
159         label_title = gtk.Label ("Title:")
160         table.attach (label_title, 0, 1, 0, 1, 0)
161         self.title_entry = gtk.Entry()
162         table.attach (self.title_entry, 1, 2, 0, 1)
163
164         # Artist row
165         artist_selector = hildon.TouchSelectorEntry (text=True)
166         for a in self.artists_list:
167             artist_selector.append_text (a)
168         self.artist_button = hildon.PickerButton (hildon.BUTTON_STYLE_NORMAL,
169                                                   hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
170         self.artist_button.set_title ("Artist: ")
171         self.artist_button.set_selector (artist_selector)
172         table.attach (self.artist_button, 0, 2, 1, 2)
173
174
175         # Album row
176         album_selector = hildon.TouchSelectorEntry (text=True)
177         for a in self.albums_list:
178             album_selector.append_text (a)
179         self.album_button = hildon.PickerButton (hildon.BUTTON_STYLE_NORMAL,
180                                                  hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
181         self.album_button.set_title ("Album: ")
182         self.album_button.set_selector (album_selector)
183         table.attach (self.album_button, 0, 2, 2, 3) 
184         
185
186         # Album art space
187         album_art_button = gtk.Button ()
188         self.album_art = gtk.Image ()
189         self.album_art.set_size_request (124, 124)
190         album_art_button.add (self.album_art)
191         album_art_button.connect ("clicked", self.clicked_album_art)
192         central_panel.pack_start (album_art_button, expand=False, fill=False)
193         
194         # Buttons row
195         button_box = gtk.HButtonBox ()
196         button_box.set_layout (gtk.BUTTONBOX_END)
197
198         back_button = hildon.Button (hildon.BUTTON_STYLE_NORMAL, hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
199         img = gtk.image_new_from_stock (gtk.STOCK_GO_BACK, gtk.ICON_SIZE_BUTTON)
200         back_button.set_image (img)
201         back_button.connect ("clicked", self.press_back_cb)
202         button_box.pack_start (back_button, expand=True, fill=True, padding=6)
203         
204         next_button = hildon.Button (hildon.BUTTON_STYLE_NORMAL, hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
205         img = gtk.image_new_from_stock (gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_BUTTON)
206         next_button.set_image (img)
207         next_button.connect ("clicked", self.press_next_cb)
208         button_box.pack_start (next_button, expand=True, fill=True, padding=6)
209         
210         view_vbox.pack_start (button_box, expand=False, fill=True, padding=6)
211         
212         self.add (view_vbox)
213
214         #menu = hildon.AppMenu ()
215         #go_to = hildon.Button (hildon.BUTTON_STYLE_NORMAL,
216         #                       hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
217         #go_to.set_title ("Go to")
218         #go_to.connect ("clicked", self.go_to_cb)
219         #menu.append (go_to)
220         #menu.show_all ()
221         #self.set_app_menu (menu)
222         
223
224     def go_to_cb (self, widget):
225         pass
226
227     def set_data_in_view (self, song):
228         """
229         Place in the screen the song information.
230         Song is a tuple like (filename, 'Music', title, artist, album, mime)
231         """
232         assert len (song) == 6
233         
234         self.filename_data.set_text (song[FILE_URI])
235         self.title_entry.set_text (song[TITLE_KEY])
236         
237
238         # Disconnect the value-change signal to avoid extra album art retrievals
239         if (self.album_button.handler_is_connected (self.album_change_handler)):
240             self.album_button.disconnect (self.album_change_handler)
241             
242         if (self.artist_button.handler_is_connected (self.artist_change_handler)):
243             self.artist_button.disconnect (self.artist_change_handler)
244
245         # Set values in the picker buttons
246         try:
247             self.artist_button.set_active (self.artists_list.index(song[ARTIST_KEY]))
248         except ValueError:
249             print "'%s' not in artist list!?" % (song[ARTIST_KEY])
250             self.artist_button.set_value ("")
251             
252         try:
253             self.album_button.set_active (self.albums_list.index (song[ALBUM_KEY]))
254         except ValueError:
255             print "'%s' is not in the album list!?" % (song[ALBUM_KEY])
256             self.album_button.set_value ("")
257
258         # Reconnect the signals!
259         self.album_change_handler = self.album_button.connect ("value-changed",
260                                                                self.album_selection_cb)
261
262         self.artist_change_handler = self.artist_button.connect ("value-changed",
263                                                                  self.artist_selection_cb)
264
265         # Set the album art given the current data
266         has_album = False
267         if (song[ALBUM_KEY]):
268             thumb = album_art_spec.getCoverArtThumbFileName (song[ALBUM_KEY])
269             print "%s -> %s" % (song[ALBUM_KEY], thumb)
270             if (os.path.exists (thumb)):
271                 self.album_art.set_from_file (thumb)
272                 has_album = True
273             else:
274                 self.album_callback_id = gobject.idle_add (self.retrieve_album_art,
275                                                            song[ARTIST_KEY], song[ALBUM_KEY])
276                 
277         if (not has_album):
278             self.album_art.set_from_stock (gtk.STOCK_CDROM, gtk.ICON_SIZE_DIALOG)
279
280         if (not song[MIME_KEY] in self.writer.get_supported_mimes ()):
281             print "show notification"
282             self.banner = hildon.Banner ()
283             self.banner.set_text ("Unsupported format (%s)" % song[MIME_KEY])
284             self.banner.show_all ()
285
286     def retrieve_album_art (self, artist, album):
287         print "trying to get the album art"
288         (img, thumb) = self.album_art_retriever.get_album_art (artist, album)
289         if (thumb):
290             self.album_art.set_from_file (thumb)
291         else:
292             print "Unable to retrieve album art"
293
294         return False
295         
296     def clicked_play (self, widget):
297         if (self.player.is_playing ()):
298             self.player.stop ()
299         else:
300             song = self.songs_list [self.song_counter]
301             self.player.play ("file://" + song[FILE_URI])
302
303     def clicked_album_art (self, widget):
304         print "implement me, please"
305
306     def album_selection_cb (self, widget):
307         """
308         On album change, add the album the local list of albums and the selector
309         if it doesn't exist already. So we show the new entry in the selector next time.
310         """
311         song = self.songs_list [self.song_counter]
312         if (not widget.get_value () in self.albums_list):
313             print "Inserting ", widget.get_value ()
314             widget.get_selector ().prepend_text (widget.get_value ())
315             self.albums_list.insert (0, widget.get_value ())
316         self.retrieve_album_art (song[ARTIST_KEY], widget.get_value ())
317
318     def artist_selection_cb (self, widget):
319         """
320         On artist change, add the artist the local list of artists and the selector
321         if it doesn't exist already. So we show the new entry in the selector next time
322         """
323         song = self.songs_list [self.song_counter]
324         if (not widget.get_value () in self.artists_list):
325             print "Inserting artist", widget.get_value ()
326             widget.get_selector ().prepend_text (widget.get_value ())
327             self.artists_list.insert (0, widget.get_value ())
328     
329 # Testing porpuses
330 if __name__ == "__main__":
331
332     TEST_DATA = [("/home/user/Music/dylan.mp3", "Music", "Bob Dylan", "Subterranean homesick blues", "Bring it all back home", "audio/mpeg"),
333                  ("/home/user/mufix/a.mp3", "Music", "", "title", "Album 2", "a/b"),
334                  ("/media/mmc1/Attachments/b.m4a", "Music", "", "b", "Album 9", "audio/mpeg"),
335                  ("/home/user/mufix/3.mp2", "Music", "", "titlex", "Album 3", "audio/mpeg")]
336     #TEST_DATA = []
337     ALBUMS = [["Album %d" % i] for i in range (0, 10)] + [["Bring it all back home"]]
338     ARTISTS = [["Artist %d" % i] for i in range (0, 10)] + [["Bob Dylan"]]
339     window = MussorgskyEditPanel (TEST_DATA, ALBUMS, ARTISTS)
340     window.connect ("destroy", gtk.main_quit)
341     window.show_all ()
342     gtk.main ()