~ubuntu-branches/ubuntu/karmic/quodlibet/karmic

« back to all changes in this revision

Viewing changes to qltk/filesel.py

  • Committer: Bazaar Package Importer
  • Author(s): Luca Falavigna
  • Date: 2009-01-30 23:55:34 UTC
  • mfrom: (1.1.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20090130235534-l4e72ulw0vqfo17w
Tags: 2.0-1ubuntu1
* Merge from Debian experimental (LP: #276856), remaining Ubuntu changes:
  + debian/patches/40-use-music-profile.patch:
    - Use the "Music and Movies" pipeline per default.
* Refresh the above patch for new upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
# Copyright 2004-2005 Joe Wreschnig, Michael Urman, Iñigo Serna
3
 
#
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
7
 
#
8
 
# $Id: filesel.py 4033 2007-04-28 17:14:57Z piman $
9
 
 
10
 
import os
11
 
import urlparse
12
 
 
13
 
import gobject
14
 
import gtk
15
 
 
16
 
import const
17
 
import formats
18
 
import qltk
19
 
import util
20
 
 
21
 
from qltk.getstring import GetStringDialog
22
 
from qltk.views import AllTreeView, RCMTreeView, MultiDragTreeView
23
 
 
24
 
def search_func(model, column, key, iter, handledirs):
25
 
    check = model.get_value(iter, 0)
26
 
    if check is None:
27
 
        return True
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
31
 
 
32
 
class DirectoryTree(RCMTreeView, MultiDragTreeView):
33
 
    def cell_data(column, cell, model, iter):
34
 
        value = model[iter][0]
35
 
        if value is not None:
36
 
            cell.set_property('text', util.fsdecode(
37
 
                os.path.basename(value) or value))
38
 
    cell_data = staticmethod(cell_data)
39
 
 
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)
50
 
 
51
 
        column.set_attributes(render, text=0)
52
 
        self.append_column(column)
53
 
        self.set_search_equal_func(search_func, True)
54
 
 
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
58
 
        else:
59
 
            folders.append(None)
60
 
            for line in f.readlines():
61
 
                folders.append(urlparse.urlsplit(line.rstrip())[2])
62
 
 
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:
67
 
            folders.pop()
68
 
 
69
 
        for path in folders:
70
 
            niter = self.get_model().append(None, [path])
71
 
            if path is not None:
72
 
                self.get_model().append(niter, ["dummy"])
73
 
        self.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
74
 
        self.connect(
75
 
            'test-expand-row', DirectoryTree.__expanded, self.get_model())
76
 
        self.set_row_separator_func(lambda model, iter: model[iter][0] is None)
77
 
 
78
 
        if initial: self.go_to(initial)
79
 
 
80
 
        menu = gtk.Menu()
81
 
        m = qltk.MenuItem(_("_New Folder..."), gtk.STOCK_NEW)
82
 
        m.connect('activate', self.__mkdir)
83
 
        menu.append(m)
84
 
        m = gtk.ImageMenuItem(gtk.STOCK_DELETE)
85
 
        m.connect('activate', self.__rmdir)
86
 
        menu.append(m)
87
 
        m = gtk.ImageMenuItem(gtk.STOCK_REFRESH)
88
 
        m.connect('activate', self.__refresh)
89
 
        menu.append(m)
90
 
        m = qltk.MenuItem(_("_Select All Subfolders"), gtk.STOCK_DIRECTORY)
91
 
        m.connect('activate', self.__expand)
92
 
        menu.append(m)
93
 
        menu.show_all()
94
 
        self.connect_object('popup-menu', self.__popup_menu, menu)
95
 
 
96
 
    def go_to(self, initial):
97
 
        path = []
98
 
        head, tail = os.path.split(initial)
99
 
        while os.path.join(head, tail) != const.HOME and tail != '':
100
 
            if tail:
101
 
                def isvisibledir(t):
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)
111
 
 
112
 
        if initial.startswith(const.HOME):
113
 
            path.insert(0, 0)
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))
119
 
 
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)
126
 
        except OSError, err:
127
 
            if err.errno == 2: model.remove(model.get_iter(path))
128
 
        else:
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())
133
 
 
134
 
    def __mkdir(self, button):
135
 
        model, rows = self.get_selection().get_selected_rows()
136
 
        if len(rows) != 1: return
137
 
 
138
 
        row = rows[0]
139
 
        directory = model[row][0]
140
 
        dir = GetStringDialog(
141
 
            None, _("New Folder"), _("Enter a name for the new folder:")).run()
142
 
 
143
 
        if dir:
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)
149
 
                qltk.ErrorMessage(
150
 
                    None, _("Unable to create folder"), error).run()
151
 
            else:
152
 
                self.emit('test-expand-row', model.get_iter(row), row)
153
 
                self.expand_row(row, False)
154
 
 
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)
162
 
            qltk.ErrorMessage(
163
 
                None, _("Unable to delete folder"), error).run()
164
 
        else:
165
 
            prow = rows[0][:-1]
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)
169
 
 
170
 
    def __expand(self, button):
171
 
        selection = self.get_selection()
172
 
        model, rows = selection.get_selected_rows()
173
 
        for row in 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)
178
 
 
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)
186
 
        return last
187
 
 
188
 
    def __refresh(self, button):
189
 
        model, rows = self.get_selection().get_selected_rows()
190
 
        for row in rows:
191
 
            if self.row_expanded(row):
192
 
                self.emit('test-expand-row', model.get_iter(row), row)
193
 
                self.expand_row(row, False)
194
 
 
195
 
    def __expanded(self, iter, path, model):
196
 
        window = self.window
197
 
        if window:
198
 
            window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
199
 
            gtk.main_iteration()
200
 
        try:
201
 
            if model is None:
202
 
                return
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):
207
 
                try:
208
 
                    for filename in util.listdir(path):
209
 
                        if os.path.isdir(filename):
210
 
                            niter = model.append(iter, [path])
211
 
                            model.append(niter, ["dummy"])
212
 
                            break
213
 
                    else:
214
 
                        model.append(iter, [path])
215
 
                except OSError:
216
 
                    pass
217
 
            if not model.iter_has_child(iter):
218
 
                return True
219
 
        finally:
220
 
            if window:
221
 
                window.set_cursor(None)
222
 
 
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)
229
 
 
230
 
    __gsignals__ = { 'changed': (gobject.SIGNAL_RUN_LAST,
231
 
                                 gobject.TYPE_NONE, (gtk.TreeSelection,))
232
 
                     }
233
 
 
234
 
    def __init__(self, initial=None, filter=formats.filter,
235
 
                 folders=[const.HOME, "/"]):
236
 
        super(FileSelector, self).__init__()
237
 
        self.__filter = filter
238
 
 
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)
254
 
 
255
 
        self.__sig = filelist.get_selection().connect(
256
 
            'changed', self.__changed)
257
 
 
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())
266
 
 
267
 
        sw = gtk.ScrolledWindow()
268
 
        sw.add(dirlist)
269
 
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
270
 
        sw.set_shadow_type(gtk.SHADOW_IN)
271
 
        self.pack1(sw, resize=True)
272
 
 
273
 
        sw = gtk.ScrolledWindow()
274
 
        sw.add(filelist)
275
 
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
276
 
        sw.set_shadow_type(gtk.SHADOW_IN)
277
 
        self.pack2(sw, resize=True)
278
 
 
279
 
    def rescan(self, *args):
280
 
        self.get_child1().child.get_selection().emit('changed')
281
 
 
282
 
    def __changed(self, selection):
283
 
        self.emit('changed', selection)
284
 
 
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()
291
 
        fmodel.clear()
292
 
        dmodel, rows = selection.get_selected_rows()
293
 
        dirs = [dmodel[row][0] for row in rows]
294
 
        for dir in dirs:
295
 
            try:
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])
300
 
            except OSError:
301
 
                pass
302
 
 
303
 
        for row in fmodel:
304
 
            if row[0] in selected:
305
 
                fselect.select_path(row.path)
306
 
        fselect.handler_unblock(self.__sig)
307
 
        fselect.emit('changed')