Пример #1
0
    def go_to(self, path_to_go):
        # FIXME: be stricter here..
        # assert util.is_fsnative(path_to_go)

        # FIXME: what about non-normalized paths?

        path_to_go = util.fsnative(path_to_go)
        model = self.get_model()

        # Find the top level row which has the largest common
        # path with the path we want to go to
        roots = dict([(p, i) for (i, p) in model.iterrows(None)])
        head, tail = path_to_go, util.fsnative("")
        to_find = []
        while head and head not in roots:
            new_head, tail = os.path.split(head)
            # this can happen for invalid paths on Windows
            if head == new_head:
                break
            head = new_head
            to_find.append(tail)
        if head not in roots:
            return
        start_iter = roots[head]

        # expand until we find the right directory or the last valid one
        # and select/scroll to it
        def search(view, model, iter_, to_find):
            tree_path = model.get_path(iter_)

            # we are where we want, select and scroll
            if not to_find:
                view.set_cursor(tree_path)
                view.scroll_to_cell(tree_path)
                return

            # expand the row
            view.expand_row(tree_path, False)

            next_ = to_find.pop(-1)
            for sub_iter, path in model.iterrows(iter_):
                if os.path.basename(path) == next_:
                    search(view, model, sub_iter, to_find)
                    break
            else:
                # we haven't found the right sub folder, select the parent
                # and stop
                search(view, model, iter_, [])

        search(self, model, start_iter, to_find)
Пример #2
0
 def test_long_filename(s):
     if os.name == "nt":
         a = s.AudioFile({"title": "x" * 300, "~filename": u"C:\\f.mp3"})
         path = FileFromPattern(u'C:\\foobar\\ä<title>\\<title>').format(a)
         s.failUnlessEqual(len(util.fsnative(path)),
                           3 + 6 + 1 + 255 + 1 + 255)
         path = FileFromPattern(u'äüö<title><title>').format(a)
         s.failUnlessEqual(len(util.fsnative(path)), 255)
     else:
         a = s.AudioFile({"title": "x" * 300, "~filename": "/f.mp3"})
         path = FileFromPattern(u'/foobar/ä<title>/<title>').format(a)
         s.failUnlessEqual(len(util.fsnative(path)),
                           1 + 6 + 1 + 255 + 1 + 255)
         path = FileFromPattern(u'äüö<title><title>').format(a)
         s.failUnlessEqual(len(util.fsnative(path)), 255)
Пример #3
0
 def __find_songs(self, selection):
     model, rows = selection.get_selected_rows()
     dirs = [model[row][0] for row in rows]
     songs = []
     to_add = []
     for dir in dirs:
         try:
             for file in filter(formats.filter,
                                sorted(os.listdir(util.fsnative(dir)))):
                 raw_path = os.path.join(dir, file)
                 fn = normalize_path(raw_path, canonicalise=True)
                 if fn in self.__glibrary:
                     songs.append(self.__glibrary[fn])
                 elif fn not in self.__library:
                     song = formats.MusicFile(fn)
                     if song:
                         to_add.append(song)
                         songs.append(song)
                         yield songs
                 if fn in self.__library:
                     song = self.__library[fn]
                     if not song.valid():
                         self.__library.reload(song)
                     if song in self.__library:
                         songs.append(song)
         except OSError:
             pass
     self.__library.add(to_add)
     yield songs
    def _post(self, value, song):
        if value:
            fn = song.get("~filename", ".")
            ext = fn[fn.rfind("."):].lower()
            val_ext = value[-len(ext):].lower()
            if not ext == val_ext:
                value += ext.lower()
            if os.name == "nt":
                value = strip_win32_incompat_from_path(value)

            value = expanduser(value)

            # Limit each path section to 255 (bytes on linux, chars on win).
            # http://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits
            path, ext = os.path.splitext(value)
            path = map(util.fsnative, path.split(os.sep))
            limit = [255] * len(path)
            limit[-1] -= len(util.fsnative(ext))
            elip = lambda (p, l): (len(p) > l and p[:l - 2] + "..") or p
            path = os.sep.join(map(elip, zip(path, limit)))
            value = fsdecode(path) + ext

            if os.sep in value and not os.path.isabs(value):
                raise ValueError("Pattern is not rooted")
        return value
Пример #5
0
 def filename(self):
     """a local filename equivalent to the URI"""
     if self.scheme != "file":
         raise ValueError("only the file scheme supports filenames")
     elif self.netloc:
         raise ValueError("only local files have filenames")
     else:
         return util.fsnative(url2pathname(self.path))
Пример #6
0
    def __rename(self, library):
        model = self.view.get_model()
        win = WritingWindow(self, len(model))
        win.show()
        was_changed = set()
        skip_all = False
        self.view.freeze_child_notify()

        # FIXME: encoding/decoding mess
        rows = [(row[0], row[1], row[2].decode('utf-8')) for row in model]
        for song, oldname, newname in rows:
            try:
                newname = util.fsnative(newname)
                library.rename(song, newname, changed=was_changed)
            except StandardError:
                util.print_exc()
                if skip_all:
                    continue
                RESPONSE_SKIP_ALL = 1
                buttons = (_("Ignore _All Errors"), RESPONSE_SKIP_ALL,
                           Gtk.STOCK_STOP, Gtk.ResponseType.CANCEL,
                           _("_Continue"), Gtk.ResponseType.OK)
                msg = qltk.Message(
                    Gtk.MessageType.ERROR,
                    win,
                    _("Unable to rename file"),
                    _("Renaming <b>%s</b> to <b>%s</b> failed. "
                      "Possibly the target file already exists, "
                      "or you do not have permission to make the "
                      "new file or remove the old one.") % (util.escape(
                          fsdecode(oldname)), util.escape(fsdecode(newname))),
                    buttons=Gtk.ButtonsType.NONE)
                msg.add_buttons(*buttons)
                msg.set_default_response(Gtk.ResponseType.OK)
                resp = msg.run()
                skip_all |= (resp == RESPONSE_SKIP_ALL)
                # Preserve old behavior: shift-click is Ignore All
                mods = Gdk.Display.get_default().get_pointer()[3]
                skip_all |= mods & Gdk.ModifierType.SHIFT_MASK
                library.reload(song, changed=was_changed)
                if resp != Gtk.ResponseType.OK and resp != RESPONSE_SKIP_ALL:
                    break
            if win.step():
                break

        self.view.thaw_child_notify()
        win.destroy()
        library.changed(was_changed)
        self.save.set_sensitive(False)
Пример #7
0
def scan_library(library, force):
    """Start the global library re-scan

    If `force` is True, reload all existing valid items.
    """

    paths = get_scan_dirs()
    exclude = util.split_scan_dirs(config.get("library", "exclude"))
    exclude = [util.fsnative(e) for e in exclude]
    copool.add(library.rebuild,
               paths,
               force,
               exclude,
               cofuncid="library",
               funcid="library")
    def __init__(self, dir, name, library=None):
        super(Playlist, self).__init__()
        self.__instances.append(self)

        if isinstance(name, unicode) and os.name != "nt":
            name = name.encode('utf-8')

        self.name = name
        self.dir = dir
        self.library = library
        self._list = HashedList()
        basename = self.quote(name)
        try:
            for line in file(os.path.join(self.dir, basename), "r"):
                line = util.fsnative(line.rstrip())
                if line in library:
                    self._list.append(library[line])
                elif library and library.masked(line):
                    self._list.append(line)
        except IOError:
            if self.name:
                self.write()
    def __changed(self, selector, selection, label):
        model, rows = selection.get_selected_rows()
        files = []

        if len(rows) < 2:
            count = len(model or [])
        else:
            count = len(rows)
        label.set_text(ngettext("%d song", "%d songs", count) % count)

        for row in rows:
            filename = util.fsnative(model[row][0])
            if not os.path.exists(filename):
                pass
            elif filename in self.__library:
                file = self.__library[filename]
                if file("~#mtime") + 1. < mtime(filename):
                    try:
                        file.reload()
                    except StandardError:
                        pass
                files.append(file)
            else:
                files.append(formats.MusicFile(filename))
        files = filter(None, files)
        if len(files) == 0:
            self.set_title("Ex Falso")
        elif len(files) == 1:
            self.set_title("%s - Ex Falso" % files[0].comma("title"))
        else:
            self.set_title("%s - Ex Falso" % (ngettext(
                "%(title)s and %(count)d more", "%(title)s and %(count)d more",
                len(files) - 1) % ({
                    'title': files[0].comma("title"),
                    'count': len(files) - 1
                })))
        self.__library.add(files)
        self.emit('changed', files)
Пример #10
0
 def scan(self, paths, exclude=[], cofuncid=None):
     added = []
     exclude = [expanduser(path) for path in exclude if path]
     for fullpath in paths:
         print_d("Scanning %r." % fullpath, self)
         desc = _("Scanning %s") % (unexpand(fsdecode(fullpath)))
         with Task(_("Library"), desc) as task:
             if cofuncid:
                 task.copool(cofuncid)
             fullpath = expanduser(fullpath)
             if filter(fullpath.startswith, exclude):
                 continue
             for path, dnames, fnames in os.walk(util.fsnative(fullpath)):
                 for filename in fnames:
                     fullfilename = os.path.join(path, filename)
                     if filter(fullfilename.startswith, exclude):
                         continue
                     if fullfilename not in self._contents:
                         fullfilename = os.path.realpath(fullfilename)
                         # skip unknown file extensions
                         if not formats.filter(fullfilename):
                             continue
                         if filter(fullfilename.startswith, exclude):
                             continue
                         if fullfilename not in self._contents:
                             item = self.add_filename(fullfilename, False)
                             if item is not None:
                                 added.append(item)
                                 if len(added) > 20:
                                     self.add(added)
                                     added = []
                                     task.pulse()
                                     yield True
                 if added:
                     self.add(added)
                     added = []
                     task.pulse()
                     yield True
Пример #11
0
    def __mkdir(self, button):
        model, paths = self.get_selection().get_selected_rows()
        if len(paths) != 1:
            return

        path = paths[0]
        directory = model[path][0]

        dir_ = GetStringDialog(None, _("New Folder"),
                               _("Enter a name for the new folder:")).run()

        if not dir_:
            return

        dir_ = util.fsnative(dir_.decode('utf-8'))
        fullpath = os.path.realpath(os.path.join(directory, dir_))

        try:
            os.makedirs(fullpath)
        except EnvironmentError, err:
            error = "<b>%s</b>: %s" % (err.filename, err.strerror)
            qltk.ErrorMessage(None, _("Unable to create folder"), error).run()
            return
Пример #12
0
    def __init__(self, initial=None, filter=filesel_filter, folders=None):
        """
        initial -- a path to a file which should be shown initially
        filter -- a function which filters paths shown in the file list
        folders -- list of shown folders in the directory tree
        """

        super(FileSelector, self).__init__()
        self.__filter = filter

        if initial is not None:
            initial = util.fsnative(initial)

        if initial and os.path.isfile(initial):
            initial = os.path.dirname(initial)
        dirlist = DirectoryTree(initial, folders=folders)

        model = ObjectStore()
        filelist = AllTreeView(model)

        column = TreeViewColumn(_("Songs"))
        column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        render = Gtk.CellRendererPixbuf()
        render.set_property('stock_id', Gtk.STOCK_FILE)
        render.props.xpad = 3
        column.pack_start(render, False)
        render = Gtk.CellRendererText()
        column.pack_start(render, True)

        def cell_data(column, cell, model, iter_, userdata):
            value = model.get_value(iter_)
            cell.set_property('text', fsdecode(os.path.basename(value)))

        column.set_cell_data_func(render, cell_data)

        filelist.append_column(column)
        filelist.set_rules_hint(True)
        filelist.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)
        filelist.set_search_equal_func(search_func, False)
        # Allow to drag and drop files from outside
        filelist.enable_model_drag_dest([], Gdk.DragAction.COPY)
        filelist.drag_dest_add_uri_targets()
        filelist.connect('drag-data-received', self.__file_dropped)

        self.__sig = filelist.get_selection().connect('changed',
                                                      self.__changed)

        dirlist.get_selection().connect('changed',
                                        self.__dir_selection_changed, filelist)
        dirlist.get_selection().emit('changed')

        def select_all_files(view, path, col, fileselection):
            view.expand_row(path, False)
            fileselection.select_all()

        dirlist.connect('row-activated', select_all_files,
                        filelist.get_selection())

        sw = ScrolledWindow()
        sw.add(dirlist)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        sw.set_shadow_type(Gtk.ShadowType.IN)
        self.pack1(sw, resize=True)

        sw = ScrolledWindow()
        sw.add(filelist)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        sw.set_shadow_type(Gtk.ShadowType.IN)
        self.pack2(sw, resize=True)
Пример #13
0
    def __init__(self, initial=None, folders=None):
        """
        initial -- the path to select/scroll to
        folders -- a list of paths to show in the tree view, None
                   will result in a separator.
        """

        model = ObjectTreeStore()
        super(DirectoryTree, self).__init__(model=model)

        if initial is not None:
            initial = util.fsnative(initial)

        column = TreeViewColumn(_("Folders"))
        column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
        render = Gtk.CellRendererPixbuf()
        render.set_property('stock_id', Gtk.STOCK_DIRECTORY)
        column.pack_start(render, False)
        render = Gtk.CellRendererText()
        column.pack_start(render, True)

        def cell_data(column, cell, model, iter_, userdata):
            value = model.get_value(iter_)
            if value is not None:
                text = fsdecode(os.path.basename(value) or value)
                cell.set_property('text', text)

        column.set_cell_data_func(render, cell_data)

        self.append_column(column)
        self.set_search_equal_func(search_func, True)

        if folders is None:
            folders = []

        for path in folders:
            niter = model.append(None, [path])
            if path is not None:
                model.append(niter, ["dummy"])

        self.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)
        self.connect('test-expand-row', DirectoryTree.__expanded, model)

        self.set_row_separator_func(
            lambda model, iter_, data: model.get_value(iter_) is None, None)

        if initial:
            self.go_to(initial)

        menu = Gtk.Menu()
        m = qltk.MenuItem(_("_New Folder..."), Gtk.STOCK_NEW)
        m.connect('activate', self.__mkdir)
        menu.append(m)
        m = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_DELETE, None)
        m.connect('activate', self.__rmdir)
        menu.append(m)
        m = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_REFRESH, None)
        m.connect('activate', self.__refresh)
        menu.append(m)
        m = qltk.MenuItem(_("_Select All Subfolders"), Gtk.STOCK_DIRECTORY)
        m.connect('activate', self.__expand)
        menu.append(m)
        menu.show_all()
        self.connect_object('popup-menu', self.__popup_menu, menu)
Пример #14
0
def get_scan_dirs():
    dirs = util.split_scan_dirs(config.get("settings", "scan"))
    return [util.fsnative(d) for d in dirs if d]