Theme changes, fix for unicode conversion errors, misc
[rox-musicbox.git] / playlistui.py
bloba05f1ad613a3add1a3de044826ec57fdcfa3fc82
1 """
2 playlistui.py
3 Playlist UI for MusicBox application.
5 Copyright 2004 Kenneth Hayber <ken@hayber.us>
6 All rights reserved.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License.
12 This program is distributed in the hope that it will be useful
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 """
22 from __future__ import generators
24 import gtk, gobject, os, sys, re
26 import rox
27 from rox import Menu, saving, loading, mime, filer
29 import playlist
30 from playlist import COL_FILE, COL_TITLE, COL_TRACK, COL_ALBUM, COL_ARTIST
31 from playlist import COL_GENRE, COL_LENGTH, COL_TYPE, COL_ICON
33 import plugins
35 #Who am I and how did I get here?
36 APP_NAME = "MusicBox"
37 APP_DIR = rox.app_dir
38 APP_DOMAIN = 'hayber.us'
40 #View options
41 VIEW_DEFAULT_SIZE = (760, 400)
44 COLUMNS = [
45 # (_("Filename"), COL_FILE, str, 100),
46 (_("Artist"), COL_ARTIST, str, 200),
47 (_("Album"), COL_ALBUM, str, 200),
48 (_("Title"), COL_TITLE, str, 200),
49 (_("Track"), COL_TRACK, int, 50),
50 (_("Genre"), COL_GENRE, str, 80),
51 # (_("Length"), COL_LENGTH, int, 60),
52 # (_("Type"), COL_TYPE, str, 60),
56 class PlaylistUI(rox.Window, loading.XDSLoader):
57 """the playlist UI for MusicBox"""
59 def __init__(self, the_playlist, musicbox):
60 """Constructor"""
61 rox.Window.__init__(self)
62 loading.XDSLoader.__init__(self, plugins.TYPE_LIST)
64 self.playlist = the_playlist #this is a reference to the main playlist
65 self.library = []
66 self.replace_library = True
67 self.musicbox = musicbox
69 self.set_title(APP_NAME+' - '+_("Playlist"))
70 self.set_role("PlayList")
71 self.set_default_size(VIEW_DEFAULT_SIZE[0], VIEW_DEFAULT_SIZE[1])
73 #capture wm delete event
74 self.connect('delete_event', self.delete_event)
76 # Menu
77 self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
78 self.connect('button-press-event', self.button_press)
79 self.connect('popup-menu', self.menukey_press)
81 self.menu = Menu.Menu('main', [
82 Menu.Action(_("Play"), 'play'),
83 Menu.Action(_("Delete"), 'delete'),
84 # Menu.Action(_("Sync"), 'sync'),
85 Menu.SubMenu(_('Filter'), [
86 Menu.Action(_("All songs"), 'filter'),
87 Menu.Action(_("This Artist"), 'filter', None, None, (COL_ARTIST,)),
88 Menu.Action(_("This Album"), 'filter', None, None, (COL_ALBUM,)),
89 Menu.Action(_("This Genre"), 'filter', None, None, (COL_GENRE,)),
90 # Menu.Action(_("New Filter..."), 'filter_new')
91 ]),
92 Menu.Separator(),
93 Menu.Action(_("Save"), 'save', '', gtk.STOCK_SAVE),
94 Menu.Action(_("Open location"), 'show_dir', '', gtk.STOCK_GO_UP),
95 Menu.Separator(),
96 Menu.Action(_("Close"), 'close', '', gtk.STOCK_CLOSE),
98 self.menu.attach(self,self)
100 # Playlist
101 swin = gtk.ScrolledWindow()
102 self.scroll_window = swin
104 swin.set_border_width(0)
105 swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
107 view = gtk.TreeView(self.playlist.get_model())
108 self.view = view
109 swin.add(view)
110 view.set_rules_hint(True)
111 self.view.set_reorderable(True)
112 self.view.set_search_column(COL_TITLE)
114 #enable for drag from playlist to other apps (doesn't work yet)
115 # self.view.drag_source_set(gtk.gdk.BUTTON_PRESS_MASK, [('text/uri-list', 0, 0)], gtk.gdk.ACTION_COPY)
116 # self.view.connect('drag_data_get', self.drag_data_get)
118 self.view.add_events(gtk.gdk.BUTTON_PRESS_MASK)
119 self.view.connect('button-press-event', self.button_press)
121 #icon showing the current song...
122 cell = gtk.CellRendererPixbuf()
123 column = gtk.TreeViewColumn('', cell, stock_id=COL_ICON)
124 view.append_column(column)
125 column.set_resizable(False)
126 column.set_reorderable(False)
128 for n in range(len(COLUMNS)):
129 cell = gtk.CellRendererText()
130 column = gtk.TreeViewColumn(COLUMNS[n][0], cell, text = COLUMNS[n][1])
131 view.append_column(column)
132 column.set_sort_column_id(COLUMNS[n][1])
133 column.set_resizable(True)
134 column.set_reorderable(True)
135 column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
136 column.set_fixed_width(COLUMNS[n][3])
137 column.connect('clicked', self.col_activate)
139 view.connect('row-activated', self.activate)
140 self.selection = view.get_selection()
141 self.handler = self.selection.connect('changed', self.set_selection)
142 self.view.set_search_column(COL_ARTIST)
144 #TODO: Multiple Selections
145 #self.selection.set_mode(gtk.SELECTION_MULTIPLE)
147 # Create layout, pack and show widgets
148 self.vbox = gtk.VBox()
149 self.add(self.vbox)
150 self.vbox.pack_start(self.scroll_window, True, True, 0)
151 self.vbox.show_all()
153 self.show()
154 self.sync()
156 def filter(self, col=None):
157 if col:
158 try:
159 iter = self.playlist.get_model().get_iter((self.curr_index,))
160 data = self.playlist.get_model().get_value(iter, col)
161 except:
162 rox.info(_("Please select a song first."))
163 return
164 else:
165 data = None
166 self.playlist.set_filter(col, data)
168 def drag_data_get(self, widget, context, selection, targetType, eventTime):
169 print >>sys.stderr, selection.target, selection.format, selection.data
170 if selection.target == 'text/uri-list':
171 selection.set(selection.target, 8, 'test.fil\n')
173 def load(self):
174 """Load the playlist either from a saved xml file, or from source dirs"""
175 try:
176 if self.replace_library:
177 self.musicbox.load_songs(self.library)
178 else:
179 self.musicbox.add_songs(self.library)
180 except:
181 rox.report_exception()
183 def save(self):
184 """Save the current list"""
185 # box = saving.SaveBox(self.playlist, rox.choices.save(APP_NAME, 'Library.xml'), 'text/xml')
186 # box = saving.SaveBox(self.playlist, rox.choices.save(APP_NAME, 'MyMusic.music'), 'application/x-music-playlist')
187 file = 'MyMusic.music'
188 path = os.path.join(rox.basedir.save_config_path(APP_DOMAIN, APP_NAME), file)
189 box = saving.SaveBox(self.playlist, path, 'application/x-music-playlist')
190 box.show()
192 def sync(self):
193 """Scroll the playlistUI to the currently selected song"""
194 try:
195 index = self.playlist.get_index()
196 self.view.set_cursor((index,))
197 self.view.scroll_to_cell((index,))
198 self.curr_index = index
199 except:
200 pass
202 def delete(self, *dummy):
203 try:
204 song = self.playlist.delete(self.curr_index)
205 except:
206 rox.alert(_("No track selected."))
208 def play(self, *dummy):
209 """Play the current song"""
210 try:
211 self.playlist.set(self.curr_index)
212 self.musicbox.play()
213 except:
214 rox.report_exception()
216 def activate(self, view, path, column):
217 """Double-click handler, plays the song"""
218 self.play()
220 def set_selection(self, selection):
221 """Tell the playlist what we currently have selected"""
222 (cursor, thing) = self.view.get_cursor()
223 if cursor == None:
224 self.curr_index = 0
225 else:
226 self.curr_index = cursor[0]
228 def show_dir(self, *dummy):
229 ''' Pops up a filer window. '''
230 try:
231 song = self.playlist.get(self.curr_index)
232 filer.show_file(song.filename)
233 except:
234 rox.alert(_("No track selected."))
236 def delete_event(self, ev, e1):
237 """Same as close, but called from the window manager"""
238 self.close()
240 def close(self, button = None):
241 """Destroy ourselves and all our children"""
242 self.destroy()
244 def button_press(self, text, event):
245 """Popup menu handler"""
246 if event.button != 3:
247 return 0
248 self.menu.popup(self, event)
249 return 1
251 def menukey_press(self, widget):
252 self.menu.popup(self, None)
254 def col_activate(self, column):
255 """Set the selected column as the search <Ctrl-S> column"""
256 self.view.set_search_column(column.get_sort_column_id())
258 def xds_drag_drop(self, widget, context, data, info, time):
259 """Check if the Shift key is pressed or not when Dropping files"""
260 if context.actions & gtk.gdk.ACTION_COPY:
261 self.replace_library = False
262 else:
263 self.replace_library = True
264 return loading.XDSLoader.xds_drag_drop(self, widget, context, data, info, time)
266 def xds_load_uris(self, uris):
267 """Accept files and folders dropped on us as new Library"""
268 path = []
269 #strip off the 'file://' part and concatenate them
270 for s in uris:
271 path.append(rox.get_local_path(s))
272 self.library = path
273 self.load()