def show_files_win32(path, files): """Takes a path to a directory and a list of filenames in that directory to display. Returns True on success. """ assert os.name == "nt" import pywintypes from win32com.shell import shell assert is_fsnative(path) assert all(is_fsnative(f) for f in files) normalized_files = map(normalize_path, files) try: folder_pidl = shell.SHILCreateFromPath(path, 0)[0] desktop = shell.SHGetDesktopFolder() shell_folder = desktop.BindToObject( folder_pidl, None, shell.IID_IShellFolder) items = [] for item in shell_folder: name = desktop.GetDisplayNameOf(item, 0) if normalize_path(name) in normalized_files: items.append(item) shell.SHOpenFolderAndSelectItems(folder_pidl, items, 0) except pywintypes.com_error: return False else: return True
def test_uri_to_path(self): if os.name != "nt": path = uri_to_path("file:///home/piman/cr%21azy") self.assertTrue(is_fsnative(path)) self.assertEqual(path, fsnative(u"/home/piman/cr!azy")) else: path = uri_to_path("file:///C:/foo") self.assertTrue(is_fsnative(path)) self.assertEqual(path, fsnative(u"C:\\foo"))
def test_lyric_filename(self): song = AudioFile() song["~filename"] = fsnative(u"filename") self.assertTrue(is_fsnative(song.lyric_filename)) song["title"] = u"Title" song["artist"] = u"Artist" self.assertTrue(is_fsnative(song.lyric_filename)) song["lyricist"] = u"Lyricist" self.assertTrue(is_fsnative(song.lyric_filename))
def test_uri2fsn(self): if os.name != "nt": path = uri2fsn("file:///home/piman/cr%21azy") self.assertTrue(is_fsnative(path)) self.assertEqual(path, fsnative(u"/home/piman/cr!azy")) else: path = uri2fsn("file:///C:/foo") self.assertTrue(is_fsnative(path)) self.assertEqual(path, fsnative(u"C:\\foo"))
def test_conv(self): empty = fsnative(u"") v = self.c.filter(empty, fsnative(u"foobar baz")) self.failUnlessEqual(v, fsnative(u"foobar baz")) self.failUnless(is_fsnative(v)) v = self.c.filter(empty, fsnative(u"Foobar.BAZ")) self.failUnlessEqual(v, fsnative(u"foobar.baz")) self.failUnless(is_fsnative(v))
def test_main(self): v = fsnative(u"foo") self.assertTrue(is_fsnative(v)) v2 = glib2fsnative(fsnative2glib(v)) self.assertTrue(is_fsnative(v2)) self.assertEqual(v, v2) v3 = bytes2fsnative(fsnative2bytes(v)) self.assertTrue(is_fsnative(v3)) self.assertEqual(v, v3)
def test_cover_path(self): song = AudioFile({"musicbrainz_albumid": u"foobar"}) song2 = AudioFile() # missing Soup if "lastfm-cover" in self.plugins: cls = self.plugins["lastfm-cover"].cls self.assertTrue(is_fsnative(cls(song).cover_path)) self.assertTrue(is_fsnative(cls(song2).cover_path)) # missing Soup if "musicbrainz-cover" in self.plugins: cls = self.plugins["musicbrainz-cover"].cls self.assertTrue(is_fsnative(cls(song).cover_path)) self.assertTrue(is_fsnative(cls(song2).cover_path))
def test_conv(self): empty = fsnative(u"") in_ = fsnative(u"foo \u00c1 \u1234") out = fsnative(u"foo _ _") v = self.c.filter(empty, in_) self.failUnlessEqual(v, out) self.failUnless(is_fsnative(v))
def test_conv(self): empty = fsnative(u"") test = fsnative(u"\u00c1 test") out = fsnative(u"A test") v = self.c.filter(empty, test) self.failUnlessEqual(v, out) self.failUnless(is_fsnative(v))
def get_thumbnail_from_file(fileobj, boundary): """Like get_thumbnail() but works with files that can't be reopened. This is needed on Windows where NamedTemporaryFile can't be reopened. Returns Pixbuf or None. Thread-safe. """ assert fileobj try: path = fileobj.name assert is_fsnative(path), path return get_thumbnail(path, boundary) except GLib.GError: try: loader = GdkPixbuf.PixbufLoader() loader.set_size(*boundary) loader.write(fileobj.read()) loader.close() fileobj.seek(0, 0) # can return None in case of partial data return loader.get_pixbuf() except (GLib.GError, EnvironmentError): pass
def get_cache_info(path, boundary): """For an image at `path` return (cache_path, thumb_size) cache_path points to a potential cache file thumb size is either 128 or 256 """ assert is_fsnative(path) width, height = boundary if width <= ThumbSize.NORMAL and height <= ThumbSize.NORMAL: size_name = "normal" thumb_size = ThumbSize.NORMAL else: size_name = "large" thumb_size = ThumbSize.LARGE thumb_folder = get_thumbnail_folder() cache_dir = os.path.join(thumb_folder, size_name) uri = "file://" + pathname2url(path) thumb_name = hashlib.md5(uri).hexdigest() + ".png" thumb_path = os.path.join(cache_dir, thumb_name) return (thumb_path, thumb_size)
def test_long_filename(s): if os.name == "nt": a = s.AudioFile({"title": "x" * 300, "~filename": u"C:\\f.mp3"}) path = s._create(u'C:\\foobar\\ä<title>\\<title>').format(a) assert is_fsnative(path) s.failUnlessEqual(len(path), 3 + 6 + 1 + 255 + 1 + 255) path = s._create(u'äüö<title><title>').format(a) assert is_fsnative(path) s.failUnlessEqual(len(path), 255) else: a = s.AudioFile({"title": "x" * 300, "~filename": "/f.mp3"}) path = s._create(u'/foobar/ä<title>/<title>').format(a) assert is_fsnative(path) s.failUnlessEqual(len(path), 1 + 6 + 1 + 255 + 1 + 255) path = s._create(u'äüö<title><title>').format(a) assert is_fsnative(path) s.failUnlessEqual(len(path), 255)
def test_long_filename(s): if os.name == "nt": a = AudioFile({"title": "x" * 300, "~filename": u"C:\\f.mp3"}) path = s._create(u'C:\\foobar\\ä<title>\\<title>').format(a) assert is_fsnative(path) s.failUnlessEqual(len(path), 3 + 6 + 1 + 255 + 1 + 255) path = s._create(u'äüö<title><title>').format(a) assert is_fsnative(path) s.failUnlessEqual(len(path), 255) else: a = AudioFile({"title": "x" * 300, "~filename": "/f.mp3"}) path = s._create(u'/foobar/ä<title>/<title>').format(a) assert is_fsnative(path) s.failUnlessEqual(len(path), 1 + 6 + 1 + 255 + 1 + 255) path = s._create(u'äüö<title><title>').format(a) assert is_fsnative(path) s.failUnlessEqual(len(path), 255)
def show_files_win32(path, files): """Takes a path to a directory and a list of filenames in that directory to display. Returns True on success. """ assert os.name == "nt" assert is_fsnative(path) assert all(is_fsnative(f) for f in files) from quodlibet.util.windows import open_folder_and_select_items try: open_folder_and_select_items(path, files) except WindowsError: return False return True
def sanitize(self, filename=None): """Fill in metadata defaults. Find ~mountpoint, ~#mtime, ~#filesize and ~#added. Check for null bytes in tags. Does not raise. """ # Replace nulls with newlines, trimming zero-length segments for key, val in listitems(self): if isinstance(val, string_types) and '\0' in val: self[key] = '\n'.join(filter(lambda s: s, val.split('\0'))) # Remove unnecessary defaults if key in NUMERIC_ZERO_DEFAULT and val == 0: del self[key] if filename: self["~filename"] = filename elif "~filename" not in self: raise ValueError("Unknown filename!") assert is_fsnative(self["~filename"]) if self.is_file: self["~filename"] = normalize_path(self["~filename"], canonicalise=True) # Find mount point (terminating at "/" if necessary) head = self["~filename"] while "~mountpoint" not in self: head, tail = os.path.split(head) # Prevent infinite loop without a fully-qualified filename # (the unit tests use these). head = head or "/" if os.path.ismount(head): self["~mountpoint"] = head else: self["~mountpoint"] = fsnative(u"/") # Fill in necessary values. self.setdefault("~#added", int(time.time())) # For efficiency, do a single stat here. See Issue 504 try: stat = os.stat(self['~filename']) self["~#mtime"] = stat.st_mtime self["~#filesize"] = stat.st_size # Issue 342. This is a horrible approximation (due to headers) but # on FLACs, the most common case, this should be close enough if "~#bitrate" not in self: try: # kbps = bytes * 8 / seconds / 1000 self["~#bitrate"] = int(stat.st_size / (self["~#length"] * (1000 / 8))) except (KeyError, ZeroDivisionError): pass except OSError: self["~#mtime"] = 0
def sanitize(self, filename=None): """Fill in metadata defaults. Find ~mountpoint, ~#mtime, ~#filesize and ~#added. Check for null bytes in tags. Does not raise. """ # Replace nulls with newlines, trimming zero-length segments for key, val in self.items(): if isinstance(val, string_types) and '\0' in val: self[key] = '\n'.join(filter(lambda s: s, val.split('\0'))) # Remove unnecessary defaults if key in INTERN_NUM_DEFAULT and val == 0: del self[key] if filename: self["~filename"] = filename elif "~filename" not in self: raise ValueError("Unknown filename!") assert is_fsnative(self["~filename"]) if self.is_file: self["~filename"] = normalize_path( self["~filename"], canonicalise=True) # Find mount point (terminating at "/" if necessary) head = self["~filename"] while "~mountpoint" not in self: head, tail = os.path.split(head) # Prevent infinite loop without a fully-qualified filename # (the unit tests use these). head = head or "/" if os.path.ismount(head): self["~mountpoint"] = head else: self["~mountpoint"] = fsnative(u"/") # Fill in necessary values. self.setdefault("~#added", int(time.time())) # For efficiency, do a single stat here. See Issue 504 try: stat = os.stat(self['~filename']) self["~#mtime"] = stat.st_mtime self["~#filesize"] = stat.st_size # Issue 342. This is a horrible approximation (due to headers) but # on FLACs, the most common case, this should be close enough if "~#bitrate" not in self: try: # kbps = bytes * 8 / seconds / 1000 self["~#bitrate"] = int(stat.st_size / (self["~#length"] * (1000 / 8))) except (KeyError, ZeroDivisionError): pass except OSError: self["~#mtime"] = 0
def test_roundtrip(self): if os.name == "nt": paths = [u"C:\\öäü.txt"] else: paths = [u"/öäü.txt", u"//foo/bar", u"///foo/bar"] for source in paths: path = uri2fsn(fsn2uri(fsnative(source))) self.assertTrue(is_fsnative(path)) self.assertEqual(path, fsnative(source))
def test_windows_path(self): if os.name != "nt": return win_path = u"C:\\SomeDir\xe4" uri = URI.frompath(win_path) self.assertEqual(uri, "file:///C:/SomeDir%C3%A4") self.assertTrue(uri.is_filename) self.assertTrue(is_fsnative(uri.filename)) self.assertEqual(uri.filename, win_path)
def test_roundtrip(self): if os.name == "nt": paths = [u"C:\\öäü.txt"] else: paths = [u"/öäü.txt", u"//foo/bar", u"///foo/bar"] for source in paths: path = uri_to_path(uri_from_path(fsnative(source))) self.assertTrue(is_fsnative(path)) self.assertEqual(path, fsnative(source))
def test_ends_with_dots_or_spaces(self): empty = fsnative(u"") v = self.c.filter(empty, fsnative(u"foo. . ")) self.failUnlessEqual(v, fsnative(u"foo. ._")) self.assertTrue(is_fsnative(v)) if os.name == "nt": self.failUnlessEqual(self.c.filter(empty, u"foo. \\bar ."), u"foo._\\bar _") else: self.failUnlessEqual(self.c.filter(empty, u"foo. /bar ."), "foo._/bar _")
def __getitem__(self, key): # we used to save them with the wrong type value = super(RemoteFile, self).__getitem__(key) if key in ("~filename", "~mountpoint") and not is_fsnative(value): if os.name == "nt": value = unicode(value) else: value = value.encode("utf-8") return value
def test_main(self): try: 1 / 0 except: result = extract_tb(sys.exc_info()[2]) self.assertTrue(isinstance(result, list)) for fn, l, fu, text in result: self.assertTrue(is_fsnative(fn)) self.assertTrue(isinstance(l, int)) self.assertTrue(isinstance(fu, text_type)) self.assertTrue(isinstance(text, text_type))
def write(self, filename): """Write config to filename. Can raise EnvironmentError """ assert is_fsnative(filename) mkdir(os.path.dirname(filename)) with atomic_save(filename, ".tmp", "wb") as fileobj: self._config.write(fileobj)
def test_ends_with_dots_or_spaces(self): empty = fsnative(u"") v = self.c.filter(empty, fsnative(u'foo. . ')) self.failUnlessEqual(v, fsnative(u"foo. ._")) self.assertTrue(is_fsnative(v)) if os.name == "nt": self.failUnlessEqual( self.c.filter(empty, u'foo. \\bar .'), u"foo._\\bar _") else: self.failUnlessEqual( self.c.filter(empty, u'foo. /bar .'), "foo._/bar _")
def new(cls, dir_, base=_("New Playlist"), library=None): assert is_fsnative(dir_) if not (dir_ and os.path.realpath(dir_)): raise ValueError("Invalid playlist directory %r" % (dir_,)) for i in range(1000): try: name = "%s %d" % (base, i) if i else base return FileBackedPlaylist(dir_, name, library, validate=True) except ValueError: pass raise ValueError("Couldn't create playlist of name '%s'" % base)
def new(cls, dir_, base=_("New Playlist"), library=None): assert is_fsnative(dir_) if not (dir_ and os.path.realpath(dir_)): raise ValueError("Invalid playlist directory %r" % (dir_, )) for i in range(1000): try: name = "%s %d" % (base, i) if i else base return FileBackedPlaylist(dir_, name, library, validate=True) except ValueError: pass raise ValueError("Couldn't create playlist of name '%s'" % base)
def test_main(self): if os.name == "nt": path = u'C:\\foobar\\ä%s\\%s' % ("x" * 300, "x" * 300) path = limit_path(path) self.failUnlessEqual(len(path), 3 + 6 + 1 + 255 + 1 + 255) else: path = '/foobar/ä%s/%s' % ("x" * 300, "x" * 300) path = limit_path(path) self.failUnlessEqual(len(path), 1 + 6 + 1 + 255 + 1 + 255) path = fsnative(u"foo%s.ext" % (u"x" * 300)) new = limit_path(path, ellipsis=False) self.assertTrue(is_fsnative(new)) self.assertEqual(len(new), 255) self.assertTrue(new.endswith(fsnative(u"xx.ext"))) new = limit_path(path) self.assertTrue(is_fsnative(new)) self.assertEqual(len(new), 255) self.assertTrue(new.endswith(fsnative(u"...ext"))) self.assertTrue(is_fsnative(limit_path(fsnative()))) self.assertEqual(limit_path(fsnative()), fsnative())
def fromsongs(cls, dir_, songs, library=None): assert is_fsnative(dir_) if len(songs) == 1: title = songs[0].comma("title") else: title = ngettext( "%(title)s and %(count)d more", "%(title)s and %(count)d more", len(songs) - 1) % ({ 'title': songs[0].comma("title"), 'count': len(songs) - 1 }) playlist = cls.new(dir_, title, library) playlist.extend(songs) return playlist
def set_file(self, fileobj): if fileobj is None: path = None else: path = fileobj.name assert is_fsnative(path) # XXX: Don't reload if the file path is the same. # Could prevent updates if fileobj.name isn't defined if self._path == path: return self._file = fileobj self._path = path self._dirty = True self.queue_resize()
def fromsongs(cls, dir_, songs, library=None): assert is_fsnative(dir_) if len(songs) == 1: title = songs[0].comma("title") else: title = ngettext( "%(title)s and %(count)d more", "%(title)s and %(count)d more", len(songs) - 1) % ( {'title': songs[0].comma("title"), 'count': len(songs) - 1}) playlist = cls.new(dir_, title, library) playlist.extend(songs) return playlist
def go_to(self, path_to_go): assert is_fsnative(path_to_go) # FIXME: what about non-normalized paths? 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, fsnative(u"") 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)
def new(cls, dir_, base=_("New Playlist"), library=None): assert is_fsnative(dir_) if not (dir_ and os.path.realpath(dir_)): raise ValueError("Invalid playlist directory %r" % (dir_,)) p = Playlist(dir_, "", library) i = 0 try: p.rename(base) except ValueError: while not p.name: i += 1 try: p.rename("%s %d" % (base, i)) except ValueError: pass return p
def new(cls, dir_, base=_("New Playlist"), library=None): assert is_fsnative(dir_) if not (dir_ and os.path.realpath(dir_)): raise ValueError("Invalid playlist directory %r" % (dir_, )) p = Playlist(dir_, "", library) i = 0 try: p.rename(base) except ValueError: while not p.name: i += 1 try: p.rename("%s %d" % (base, i)) except ValueError: pass return p
def match_path(self, path): assert is_fsnative(path) tail = os.path.splitdrive(path)[-1] # only match on the last n pieces of a filename, dictated by pattern # this means no pattern may effectively cross a /, despite .* doing so sep = os.path.sep matchon = sep + sep.join(tail.split(sep)[-self.slashes:]) # work on unicode matchon = fsdecode(matchon, note=False) match = self.pattern.search(matchon) # dicts for all! if match is None: return {} else: return match.groupdict()
def __setitem__(self, key, value): if not self.__dict__: # unpickle case.. we can't fail dict.__setitem__(self, key, value) return if key.startswith("~#"): assert isinstance(value, number_types) elif key in FILESYSTEM_TAGS: assert is_fsnative(value) else: value = text_type(value) dict.__setitem__(self, key, value) pop = self.__dict__.pop pop("album_key", None) pop("sort_key", None)
def write(self, filename): """Write config to filename. Can raise EnvironmentError """ assert is_fsnative(filename) mkdir(os.path.dirname(filename)) # temporary set the new version for saving if self._version is not None: self.add_section("__config__") self.set("__config__", "version", self._version) try: with atomic_save(filename, ".tmp", "wb") as fileobj: self._config.write(fileobj) finally: if self._loaded_version is not None: self.set("__config__", "version", self._loaded_version)
def atomic_save(filename, suffix, mode): """Try to replace the content of a file in the safest way possible. * filename+suffix will be created during the process. * On UNIX this operation is atomic, on Windows it is not. with atomic_save("config.cfg", ".tmp", "wb") as f: f.write(data) Can raise. """ assert is_fsnative(filename) temp_filename = filename + suffix fileobj = open(temp_filename, "wb") try: if fcntl is not None: fcntl.flock(fileobj.fileno(), fcntl.LOCK_EX) yield fileobj fileobj.flush() os.fsync(fileobj.fileno()) # No atomic rename on windows if os.name == "nt": fileobj.close() try: os.remove(filename) except EnvironmentError: pass os.rename(temp_filename, filename) finally: if fcntl is not None: fcntl.flock(fileobj.fileno(), fcntl.LOCK_UN) fileobj.close()
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__( orientation=Gtk.Orientation.VERTICAL) self.__filter = filter if initial is not None: assert is_fsnative(initial) if initial and os.path.isfile(initial): initial = os.path.dirname(initial) dirlist = DirectoryTree(initial, folders=folders) model = ObjectStore() filelist = AllTreeView(model=model) column = TreeViewColumn(title=_("Songs")) column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) render = Gtk.CellRendererPixbuf() render.props.xpad = 3 def cell_icon(column, cell, model, iter_, userdata): value = model.get_value(iter_) if is_image(value): cell.set_property('icon-name', Icons.IMAGE_X_GENERIC) else: cell.set_property('icon-name', Icons.AUDIO_X_GENERIC) column.set_cell_data_func(render, cell_icon) column.pack_start(render, False) render = Gtk.CellRendererText() if filelist.supports_hints(): render.set_property('ellipsize', Pango.EllipsizeMode.END) 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) filelist.set_search_column(0) 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)
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: assert is_fsnative(initial) column = TreeViewColumn(title=_("Folders")) column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) render = Gtk.CellRendererPixbuf() render.set_property('icon-name', Icons.FOLDER) render.props.xpad = 3 column.pack_start(render, False) render = Gtk.CellRendererText() if self.supports_hints(): render.set_property('ellipsize', Pango.EllipsizeMode.END) 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) self.set_search_column(0) if folders is None: folders = [] for path in folders: niter = model.append(None, [path]) if path is not None: assert is_fsnative(path) 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(_(u"_New Folder…"), Icons.DOCUMENT_NEW) m.connect('activate', self.__mkdir) menu.append(m) m = qltk.MenuItem(_("_Delete"), Icons.EDIT_DELETE) m.connect('activate', self.__rmdir) menu.append(m) m = qltk.MenuItem(_("_Refresh"), Icons.VIEW_REFRESH) m.connect('activate', self.__refresh) menu.append(m) m = qltk.MenuItem(_("_Select All Subfolders"), Icons.FOLDER) m.connect('activate', self.__expand) menu.append(m) menu.show_all() connect_obj(self, 'popup-menu', self.__popup_menu, menu) # Allow to drag and drop files from outside targets = [ ("text/uri-list", 0, 42) ] targets = [Gtk.TargetEntry.new(*t) for t in targets] self.drag_dest_set(Gtk.DestDefaults.ALL, targets, Gdk.DragAction.COPY) self.connect('drag-data-received', self.__drag_data_received)
import os from gi.repository import Gtk from quodlibet import formats, qltk from quodlibet.qltk.wlw import WaitLoadWindow from quodlibet.qltk.getstring import GetStringDialog from quodlibet.util import escape from quodlibet.util.collection import Playlist from quodlibet.util.path import mkdir, fsdecode, is_fsnative from quodlibet import const # Directory for playlist files from quodlibet.util.uri import URI PLAYLISTS = os.path.join(const.USERDIR, "playlists") assert is_fsnative(PLAYLISTS) if not os.path.isdir(PLAYLISTS): mkdir(PLAYLISTS) class ConfirmRemovePlaylistDialog(qltk.Message): def __init__(self, parent, playlist): title = (_("Are you sure you want to delete the playlist '%s'?") % escape(playlist.name)) description = (_("All information about the selected playlist " "will be deleted and can not be restored.")) super(ConfirmRemovePlaylistDialog, self).__init__(Gtk.MessageType.WARNING, parent, title, description, Gtk.ButtonsType.NONE)