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