1
# -*- coding: utf-8 -*-
2
# Copyright 2004-2005 Joe Wreschnig, Michael Urman, Iñigo Serna
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License version 2 as
6
# published by the Free Software Foundation
8
# $Id: filesel.py 4033 2007-04-28 17:14:57Z piman $
21
from qltk.getstring import GetStringDialog
22
from qltk.views import AllTreeView, RCMTreeView, MultiDragTreeView
24
def search_func(model, column, key, iter, handledirs):
25
check = model.get_value(iter, 0)
28
elif not handledirs or os.sep not in key:
29
check = os.path.basename(check) or os.sep
30
return key not in check.lower() and key not in check
32
class DirectoryTree(RCMTreeView, MultiDragTreeView):
33
def cell_data(column, cell, model, iter):
34
value = model[iter][0]
36
cell.set_property('text', util.fsdecode(
37
os.path.basename(value) or value))
38
cell_data = staticmethod(cell_data)
40
def __init__(self, initial=None, folders=[const.HOME, "/"]):
41
super(DirectoryTree, self).__init__(gtk.TreeStore(str))
42
column = gtk.TreeViewColumn(_("Folders"))
43
column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
44
render = gtk.CellRendererPixbuf()
45
render.set_property('stock_id', gtk.STOCK_DIRECTORY)
46
column.pack_start(render, expand=False)
47
render = gtk.CellRendererText()
48
column.pack_start(render)
49
column.set_cell_data_func(render, self.cell_data)
51
column.set_attributes(render, text=0)
52
self.append_column(column)
53
self.set_search_equal_func(search_func, True)
55
# Read in the GTK bookmarks list; gjc says this is the right way
56
try: f = file(os.path.join(const.HOME, ".gtk-bookmarks"))
57
except EnvironmentError: pass
60
for line in f.readlines():
61
folders.append(urlparse.urlsplit(line.rstrip())[2])
63
def is_folder(filename):
64
return filename is None or os.path.isdir(filename)
65
folders = filter(is_folder, folders)
66
if folders[-1] is None:
70
niter = self.get_model().append(None, [path])
72
self.get_model().append(niter, ["dummy"])
73
self.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
75
'test-expand-row', DirectoryTree.__expanded, self.get_model())
76
self.set_row_separator_func(lambda model, iter: model[iter][0] is None)
78
if initial: self.go_to(initial)
81
m = qltk.MenuItem(_("_New Folder..."), gtk.STOCK_NEW)
82
m.connect('activate', self.__mkdir)
84
m = gtk.ImageMenuItem(gtk.STOCK_DELETE)
85
m.connect('activate', self.__rmdir)
87
m = gtk.ImageMenuItem(gtk.STOCK_REFRESH)
88
m.connect('activate', self.__refresh)
90
m = qltk.MenuItem(_("_Select All Subfolders"), gtk.STOCK_DIRECTORY)
91
m.connect('activate', self.__expand)
94
self.connect_object('popup-menu', self.__popup_menu, menu)
96
def go_to(self, initial):
98
head, tail = os.path.split(initial)
99
while os.path.join(head, tail) != const.HOME and tail != '':
102
joined = os.path.join(head, t)
103
return (not t.startswith(".") and
104
os.access(joined, os.X_OK) and
105
os.path.isdir(joined))
106
try: dirs = filter(isvisibledir, sorted(os.listdir(head)))
107
except OSError: break
108
try: path.insert(0, dirs.index(tail))
109
except ValueError: break
110
head, tail = os.path.split(head)
112
if initial.startswith(const.HOME):
114
else: path.insert(0, 1)
115
for i in range(len(path)):
116
self.expand_row(tuple(path[:i+1]), False)
117
self.get_selection().select_path(tuple(path))
118
self.scroll_to_cell(tuple(path))
120
def __popup_menu(self, menu):
121
try: model, (path,) = self.get_selection().get_selected_rows()
122
except ValueError: return True
123
directory = model[path][0]
124
delete = menu.get_children()[1]
125
try: delete.set_sensitive(len(os.listdir(directory)) == 0)
127
if err.errno == 2: model.remove(model.get_iter(path))
129
selection = self.get_selection()
130
selection.unselect_all()
131
selection.select_path(path)
132
return self.popup_menu(menu, 0, gtk.get_current_event_time())
134
def __mkdir(self, button):
135
model, rows = self.get_selection().get_selected_rows()
136
if len(rows) != 1: return
139
directory = model[row][0]
140
dir = GetStringDialog(
141
None, _("New Folder"), _("Enter a name for the new folder:")).run()
144
dir = util.fsencode(dir.decode('utf-8'))
145
fullpath = os.path.realpath(os.path.join(directory, dir))
146
try: os.makedirs(fullpath)
147
except EnvironmentError, err:
148
error = "<b>%s</b>: %s" % (err.filename, err.strerror)
150
None, _("Unable to create folder"), error).run()
152
self.emit('test-expand-row', model.get_iter(row), row)
153
self.expand_row(row, False)
155
def __rmdir(self, button):
156
model, rows = self.get_selection().get_selected_rows()
157
if len(rows) != 1: return
158
directory = model[rows[0]][0]
159
try: os.rmdir(directory)
160
except EnvironmentError, err:
161
error = "<b>%s</b>: %s" % (err.filename, err.strerror)
163
None, _("Unable to delete folder"), error).run()
166
expanded = self.row_expanded(prow)
167
self.emit('test-expand-row', model.get_iter(prow), prow)
168
if expanded: self.expand_row(prow, False)
170
def __expand(self, button):
171
selection = self.get_selection()
172
model, rows = selection.get_selected_rows()
174
it = model.get_iter(row)
175
self.expand_row(row, False)
176
last = self.__select_children(it, model, selection)
177
selection.select_range(row, last)
179
def __select_children(self, iter, model, selection):
180
nchildren = model.iter_n_children(iter)
181
last = model.get_path(iter)
182
for i in xrange(nchildren):
183
child = model.iter_nth_child(iter, i)
184
self.expand_row(model.get_path(child), False)
185
last = self.__select_children(child, model, selection)
188
def __refresh(self, button):
189
model, rows = self.get_selection().get_selected_rows()
191
if self.row_expanded(row):
192
self.emit('test-expand-row', model.get_iter(row), row)
193
self.expand_row(row, False)
195
def __expanded(self, iter, path, model):
198
window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
203
while model.iter_has_child(iter):
204
model.remove(model.iter_children(iter))
205
folder = model[iter][0]
206
for path in util.listdir(folder):
208
for filename in util.listdir(path):
209
if os.path.isdir(filename):
210
niter = model.append(iter, [path])
211
model.append(niter, ["dummy"])
214
model.append(iter, [path])
217
if not model.iter_has_child(iter):
221
window.set_cursor(None)
223
class FileSelector(gtk.VPaned):
224
def cell_data(column, cell, model, iter):
225
value = model[iter][0]
226
if value is not None:
227
cell.set_property('text', util.fsdecode(os.path.basename(value)))
228
cell_data = staticmethod(cell_data)
230
__gsignals__ = { 'changed': (gobject.SIGNAL_RUN_LAST,
231
gobject.TYPE_NONE, (gtk.TreeSelection,))
234
def __init__(self, initial=None, filter=formats.filter,
235
folders=[const.HOME, "/"]):
236
super(FileSelector, self).__init__()
237
self.__filter = filter
239
dirlist = DirectoryTree(initial, folders=folders)
240
filelist = AllTreeView(gtk.ListStore(str))
241
column = gtk.TreeViewColumn(_("Songs"))
242
column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
243
render = gtk.CellRendererPixbuf()
244
render.set_property('stock_id', gtk.STOCK_FILE)
245
column.pack_start(render, expand=False)
246
render = gtk.CellRendererText()
247
column.pack_start(render)
248
column.set_cell_data_func(render, self.cell_data)
249
column.set_attributes(render, text=0)
250
filelist.append_column(column)
251
filelist.set_rules_hint(True)
252
filelist.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
253
filelist.set_search_equal_func(search_func, False)
255
self.__sig = filelist.get_selection().connect(
256
'changed', self.__changed)
258
dirlist.get_selection().connect(
259
'changed', self.__fill, filelist)
260
dirlist.get_selection().emit('changed')
261
def select_all_files(view, path, col, fileselection):
262
view.expand_row(path, False)
263
fileselection.select_all()
264
dirlist.connect('row-activated', select_all_files,
265
filelist.get_selection())
267
sw = gtk.ScrolledWindow()
269
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
270
sw.set_shadow_type(gtk.SHADOW_IN)
271
self.pack1(sw, resize=True)
273
sw = gtk.ScrolledWindow()
275
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
276
sw.set_shadow_type(gtk.SHADOW_IN)
277
self.pack2(sw, resize=True)
279
def rescan(self, *args):
280
self.get_child1().child.get_selection().emit('changed')
282
def __changed(self, selection):
283
self.emit('changed', selection)
285
def __fill(self, selection, filelist):
286
fselect = filelist.get_selection()
287
fselect.handler_block(self.__sig)
288
fmodel, frows = fselect.get_selected_rows()
289
selected = [fmodel[row][0] for row in frows]
290
fmodel = filelist.get_model()
292
dmodel, rows = selection.get_selected_rows()
293
dirs = [dmodel[row][0] for row in rows]
296
for file in filter(self.__filter, sorted(os.listdir(dir))):
297
filename = os.path.join(dir, file)
298
if os.access(filename, os.R_OK):
299
fmodel.append([filename])
304
if row[0] in selected:
305
fselect.select_path(row.path)
306
fselect.handler_unblock(self.__sig)
307
fselect.emit('changed')