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