3 Playlist UI for MusicBox application.
5 Copyright 2004 Kenneth Hayber <ken@hayber.us>
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
22 from __future__
import generators
24 import gtk
, gobject
, os
, sys
, re
27 from rox
import Menu
, saving
, loading
, mime
, filer
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
35 #Who am I and how did I get here?
38 APP_DOMAIN
= 'hayber.us'
41 VIEW_DEFAULT_SIZE
= (760, 400)
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
):
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
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
)
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')
93 Menu
.Action(_("Save"), 'save', '', gtk
.STOCK_SAVE
),
94 Menu
.Action(_("Open location"), 'show_dir', '', gtk
.STOCK_GO_UP
),
96 Menu
.Action(_("Close"), 'close', '', gtk
.STOCK_CLOSE
),
98 self
.menu
.attach(self
,self
)
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())
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()
150 self
.vbox
.pack_start(self
.scroll_window
, True, True, 0)
156 def filter(self
, col
=None):
159 iter = self
.playlist
.get_model().get_iter((self
.curr_index
,))
160 data
= self
.playlist
.get_model().get_value(iter, col
)
162 rox
.info(_("Please select a song first."))
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')
174 """Load the playlist either from a saved xml file, or from source dirs"""
176 if self
.replace_library
:
177 self
.musicbox
.load_songs(self
.library
)
179 self
.musicbox
.add_songs(self
.library
)
181 rox
.report_exception()
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')
193 """Scroll the playlistUI to the currently selected song"""
195 index
= self
.playlist
.get_index()
196 self
.view
.set_cursor((index
,))
197 self
.view
.scroll_to_cell((index
,))
198 self
.curr_index
= index
202 def delete(self
, *dummy
):
204 song
= self
.playlist
.delete(self
.curr_index
)
206 rox
.alert(_("No track selected."))
208 def play(self
, *dummy
):
209 """Play the current song"""
211 self
.playlist
.set(self
.curr_index
)
214 rox
.report_exception()
216 def activate(self
, view
, path
, column
):
217 """Double-click handler, plays the song"""
220 def set_selection(self
, selection
):
221 """Tell the playlist what we currently have selected"""
222 (cursor
, thing
) = self
.view
.get_cursor()
226 self
.curr_index
= cursor
[0]
228 def show_dir(self
, *dummy
):
229 ''' Pops up a filer window. '''
231 song
= self
.playlist
.get(self
.curr_index
)
232 filer
.show_file(song
.filename
)
234 rox
.alert(_("No track selected."))
236 def delete_event(self
, ev
, e1
):
237 """Same as close, but called from the window manager"""
240 def close(self
, button
= None):
241 """Destroy ourselves and all our children"""
244 def button_press(self
, text
, event
):
245 """Popup menu handler"""
246 if event
.button
!= 3:
248 self
.menu
.popup(self
, event
)
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
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"""
269 #strip off the 'file://' part and concatenate them
271 path
.append(rox
.get_local_path(s
))