def test_selection_list_store_delete_from_end(): store = SelectionListStore(str) store.append(("one",)) store.append(("two",)) store.append(("three",)) store.select((0,)) store.select((2,)) del store[(2,)] assert store.is_selected((0,)) assert not store.is_selected((1,))
def __init__(self, clipboard, executor): self.clipboard = clipboard self.executor = executor self.model = SelectionListStore(gtk.gdk.Pixbuf, str, gio.FileInfo, bool) self.widget = gtk.VBox() self.uri_entry = gtk.Entry() self.uri_entry.connect('activate', self.on_uri_entry_activate) self.widget.pack_start(self.uri_entry, False, False) self.sw = gtk.ScrolledWindow() self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.widget.pack_start(self.sw) self.view = view = FmdIconView() view.connect('item-activated', self.on_item_activated) icon_cell = gtk.CellRendererPixbuf() view.icon_renderer = icon_cell view.set_attributes(icon_cell, pixbuf=0, sensitive=3) icon_cell.props.follow_state = True icon_cell.props.xpad = 1 text_cell = gtk.CellRendererText() view.text_renderer = text_cell text_cell.props.mode = gtk.CELL_RENDERER_MODE_EDITABLE text_cell.props.editable = True view.set_attributes(text_cell, text=1, sensitive=3) self.sw.add(view) self.isearch = InteractiveSearch(self._search, self._search_widget_created) self.isearch.attach(view) self.icon_cache = {} self.current_folder = None self.history = History() self.show_hidden = False self.monitor = None
class FileList(object): def __init__(self, clipboard, executor): self.clipboard = clipboard self.executor = executor self.model = SelectionListStore(gtk.gdk.Pixbuf, str, gio.FileInfo, bool) self.widget = gtk.VBox() self.uri_entry = gtk.Entry() self.uri_entry.connect('activate', self.on_uri_entry_activate) self.widget.pack_start(self.uri_entry, False, False) self.sw = gtk.ScrolledWindow() self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.widget.pack_start(self.sw) self.view = view = FmdIconView() view.connect('item-activated', self.on_item_activated) icon_cell = gtk.CellRendererPixbuf() view.icon_renderer = icon_cell view.set_attributes(icon_cell, pixbuf=0, sensitive=3) icon_cell.props.follow_state = True icon_cell.props.xpad = 1 text_cell = gtk.CellRendererText() view.text_renderer = text_cell text_cell.props.mode = gtk.CELL_RENDERER_MODE_EDITABLE text_cell.props.editable = True view.set_attributes(text_cell, text=1, sensitive=3) self.sw.add(view) self.isearch = InteractiveSearch(self._search, self._search_widget_created) self.isearch.attach(view) self.icon_cache = {} self.current_folder = None self.history = History() self.show_hidden = False self.monitor = None def _search_widget_created(self, widget): self.feedback.show_widget(widget, 10, 0, False) @property def feedback(self): try: return self._feedback except AttributeError: pass self._feedback = FeedbackHelper(self.view.get_toplevel().feedback, self.view) return self._feedback def _search(self, text, direction, skip): idx = sidx = self.view.get_cursor()[0] if self.view.get_cursor() else 0 if skip: idx = (sidx + direction) % len(self.model) else: sidx = (sidx - 1) % len(self.model) while sidx != idx: r = self.model[idx] if r[1].lower().startswith(text): self.view.set_cursor((idx,)) break idx = (idx + direction) % len(self.model) else: if not skip: self.view.unselect_all() def set_uri(self, uri, add_to_history=True, cursor=None, scroll=None): folder = gio.file_parse_name(uri) file_info = folder.query_info('standard::*') ft = file_info.get_file_type() if ft == gio.FILE_TYPE_DIRECTORY: uri = folder.get_path() or folder.get_uri() elif ft in (gio.FILE_TYPE_MOUNTABLE, gio.FILE_TYPE_SHORTCUT): uri = file_info.get_attribute_as_string(gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI) folder = gio.File(uri=uri) else: raise Exception('Unknown file type %s %s' % (ft, folder.get_uri())) self.uri_entry.set_text(uri) if self.current_folder: self.history.update(self.current_folder.get_uri(), self.view.get_cursor(), self.sw.props.hadjustment.value) self.current_folder = folder self.setup_monitor(folder) self.fill() self.view.grab_focus() if cursor: self.view.set_cursor(cursor) else: self.view.set_cursor((0,)) self.view.refresh(False) if add_to_history: self.history.add(folder.get_uri()) def get_pixbuf(self, info): key = info.get_icon() try: return self.icon_cache[key] except KeyError: pass theme = gtk.icon_theme_get_default() icon_info = theme.lookup_by_gicon(key, 16, 0) if not icon_info: icon_info = theme.lookup_icon('gtk-file', 16, 0) if icon_info: pixbuf = icon_info.load_icon() else: pixbuf = None self.icon_cache[key] = pixbuf return pixbuf def setup_monitor(self, file): if self.monitor: self.monitor.cancel() self.monitor = file.monitor() self.monitor.connect('changed', self.on_file_change) def get_info_for_file_which_will_change_model(self, file): if not self.current_folder.equal(file.get_parent()): return None fi = file.query_info('standard::*') if not self.show_hidden and fi.get_is_hidden(): return None return fi def add_file(self, file): for r in self.model: if r[2].get_name() == file.get_basename(): return fi = self.get_info_for_file_which_will_change_model(file) self.model.append((self.get_pixbuf(fi), fi.get_display_name(), fi, True)) self.view.refresh() def remove_file(self, file): if not self.current_folder.equal(file.get_parent()): return None for r in self.model: if r[2].get_name() == file.get_basename(): del self.model[r.path] break self.view.refresh() def on_file_change(self, monitor, file, other_file, event_type): if event_type == gio.FILE_MONITOR_EVENT_CREATED: self.add_file(file) elif event_type == gio.FILE_MONITOR_EVENT_DELETED: self.remove_file(file) elif event_type == gio.FILE_MONITOR_EVENT_MOVED: self.remove_file(file) self.add_file(other_file) def fill(self): self.view.set_model(None) enumerator = self.current_folder.enumerate_children('standard::*') infos = [] while True: fi = enumerator.next_file() if not fi: break if not self.show_hidden and fi.get_attribute_boolean('standard::is-hidden'): continue content_type = fi.get_attribute_as_string('standard::content-type') sk = 0 if content_type == 'inode/directory' else 1 name = fi.get_attribute_as_string('standard::display-name') infos.append((sk, name.lower(), name, fi)) enumerator.close() self.model.clear() for _, _, name, info in sorted(infos): self.model.append((self.get_pixbuf(info), name, info, True)) self.view.set_model(self.model) def on_uri_entry_activate(self, entry): self.set_uri(entry.get_text()) def set_uri_from_file_info(self, file_info, cfile): ft = file_info.get_file_type() if ft == gio.FILE_TYPE_DIRECTORY: self.set_uri(cfile.get_path() or cfile.get_uri()) elif ft in (gio.FILE_TYPE_MOUNTABLE, gio.FILE_TYPE_SHORTCUT): uri = file_info.get_attribute_as_string(gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI) self.set_uri(uri) elif ft == gio.FILE_TYPE_REGULAR: app_info = cfile.query_default_handler() if app_info: os.chdir(self.current_folder.get_path()) app_info.launch([cfile]) else: print ft, cfile.get_uri() def process_archive(self, cfile, fi): from subprocess import Popen, PIPE ct = Popen(["/usr/bin/env", "file", "-b", "--mime-type", cfile.get_path()], stdout=PIPE).communicate()[0].strip() if ct == 'application/x-rar': cmd = ['unrar', 'x', '-y'] elif ct == 'application/zip': cmd = ['unzip', '-o'] elif ct == 'application/x-7z-compressed': cmd = ['7z', 'x', '-y'] else: print ct return False folder = gio.file_parse_name('/tmp/fmd-archive-cache/' + fi.get_display_name()) if not folder.query_exists(): folder.make_directory_with_parents() cmd.append(self.current_folder.get_child(fi.get_name()).get_path()) pid, _, _, _ = glib.spawn_async(cmd, working_directory=folder.get_path(), flags=glib.SPAWN_SEARCH_PATH) self.set_uri(folder.get_uri()) return True def on_item_activated(self, view, path): row = self.model[path] fi = row[2] cfile = self.current_folder.get_child(fi.get_name()) ft = fi.get_file_type() if ft == gio.FILE_TYPE_REGULAR: if not self.process_archive(cfile, fi): app_info = gio.app_info_get_default_for_type(fi.get_content_type(), False) if app_info: os.chdir(self.current_folder.get_path()) app_info.launch([cfile]) else: self.set_uri(cfile.get_uri()) def navigate_parent(self): parent = self.current_folder.get_parent() if parent: uri = parent.get_path() cursor, scroll = self.history.get(uri) self.set_uri(uri, True, cursor, scroll) def navigate_back(self): path, cursor, scroll = self.history.back() if path: self.set_uri(path, False, cursor, scroll) def navigate_forward(self): path, cursor, scroll = self.history.forward() if path: self.set_uri(path, False, cursor, scroll) def activate_location(self): self.uri_entry.grab_focus() def show_hidden(self, is_set): if is_set: self.show_hidden = not self.show_hidden self.fill() self.view.refresh() else: return self.show_hidden def get_cursor_for_name(self, name): for row in self.model: if row[1] == name: return row.path return None def get_filelist_from_selection(self): result = [] for path in self.model.selection: result.append(self.current_folder.get_child(self.model[path][2].get_name())) return result def cut(self): filelist = self.get_filelist_from_selection() self.clipboard.cut(filelist) for r in self.model: r[3] = r.path not in self.model.selection self.view.queue_draw() self.feedback.show('Cut') def copy(self): filelist = self.get_filelist_from_selection() self.clipboard.copy(filelist) for r in self.model: r[3] = True self.view.refresh(False) self.feedback.show('Copied') def paste(self): def on_paste(is_cut, filelist): filelist = [gio.File(uri=r) for r in filelist] if is_cut: self.executor.move(filelist, self.current_folder) for r in self.model: r[3] = True self.view.refresh(False) else: self.executor.copy(filelist, self.current_folder) self.clipboard.paste(on_paste) def delete(self): files = self.get_filelist_from_selection() try: for f in files: f.trash() except gio.Error, e: self.feedback.show(str(e), 'error') else: