📄 dirdiff.py
字号:
def set_locations(self, locations): self.set_num_panes(len(locations)) locations = [os.path.abspath(l or ".") for l in locations] self.model.clear() for pane, loc in enumerate(locations): self.fileentry[pane].set_filename(loc) child = self.model.add_entries(None, locations) self._update_item_state(child) self.recompute_label() self.scheduler.remove_all_tasks() self.recursively_update( (0,) ) def recursively_update( self, path ): """Recursively update from tree path 'path'. """ it = self.model.get_iter( path ) child = self.model.iter_children( it ) while child: self.model.remove(child) child = self.model.iter_children( it ) self._update_item_state(it) self.scheduler.add_task( self._search_recursively_iter( path ).next ) def _search_recursively_iter(self, rootpath): self.filter_hide_current.set_sensitive(False) yield _("[%s] Scanning %s") % (self.label_text, "") prefixlen = 1 + len( self.model.value_path( self.model.get_iter(rootpath), 0 ) ) symlinks_followed = {} # only follow symlinks once todo = [ rootpath ] while len(todo): todo.sort() # depth first path = todo.pop(0) it = self.model.get_iter( path ) roots = self.model.value_paths( it ) yield _("[%s] Scanning %s") % (self.label_text, roots[0][prefixlen:]) differences = [0] if not self.button_ignore_case.get_active(): class accum(object): def __init__(self, parent, roots): self.items = [] self.n = parent.num_panes def add(self, pane, items): self.items.extend(items) def get(self): self.items.sort() def repeat(s, n): for i in xrange(n): yield s return [ tuple(repeat(i,self.n)) for i in uniq(self.items) ] else: canonicalize = lambda x : x.lower() class accum(object): def __init__(self, parent, roots): self.items = {} # map canonical names to realnames self.bad = [] self.parent = parent self.roots = roots self.default = [None] * self.parent.num_panes def add(self, pane, items): for i in items: ci = canonicalize(i) try: assert self.items[ ci ][pane] == None except KeyError: self.items[ ci ] = self.default[:] self.items[ ci ][pane] = i except AssertionError: self.bad.append( _("'%s' hidden by '%s'") % ( os.path.join(self.roots[pane], i), self.items[ ci ][pane]) ) else: self.items[ ci ][pane] = i def get(self): if len(self.bad): misc.run_dialog(_("You are running a case insensitive comparison on" " a case sensitive filesystem. Some files are not visible:\n%s") % "\n".join( self.bad ), self.parent ) keys = self.items.keys() keys.sort() def fixup(key, tuples): # replace None with a usable label def first_nonempty(seq): for s in seq: if s: return s return tuple([ t or first_nonempty(tuples) or k for t in tuples ]) return [ fixup(k, self.items[k]) for k in keys ] accumdirs = accum(self, roots) accumfiles = accum(self, roots) for pane, root in enumerate(roots): if os.path.isdir( root ): try: entries = os.listdir( root ) except OSError, err: self.model.add_error( it, err.strerror, pane ) differences = [1] else: for f in self.name_filters: entries = filter(f.filter, entries) files = [] dirs = [] for e in entries: s = os.lstat( join(root,e) ) if stat.S_ISLNK(s.st_mode): if not self.prefs.ignore_symlinks: key = (s.st_dev, s.st_ino) if symlinks_followed.get( key, 0 ) == 0: symlinks_followed[key] = 1 try: s = os.stat( join(root,e) ) except OSError, err: print "ignoring dangling symlink", e pass else: if stat.S_ISREG(s.st_mode): files.append(e) elif stat.S_ISDIR(s.st_mode): dirs.append(e) elif stat.S_ISREG(s.st_mode): files.append(e) elif stat.S_ISDIR(s.st_mode): dirs.append(e) accumfiles.add( pane, files ) accumdirs.add( pane, dirs ) alldirs = accumdirs.get() allfiles = self._filter_on_state( roots, accumfiles.get() ) # then directories and files if len(alldirs) + len(allfiles) != 0: def add_entry(names): child = self.model.add_entries( it, [join(r,n) for r,n in zip(roots, names) ] ) differences[0] |= self._update_item_state(child) return child map(lambda x : todo.append( self.model.get_path(add_entry(x))), alldirs ) map(add_entry, allfiles) else: # directory is empty, add a placeholder self.model.add_empty(it) if differences[0]: start = path[:] while len(start) and not self.treeview[0].row_expanded(start): start = start[:-1] level = len(start) while level < len(path): level += 1 self.treeview[0].expand_row( path[:level], 0) yield _("[%s] Done") % self.label_text self.filter_hide_current.set_sensitive(True) def launch_comparison(self, it, pane, force=1): """Launch comparison at 'it'. If it is a file we launch a diff. If it is a folder we recursively open diffs for each non equal file. """ paths = filter(os.path.exists, self.model.value_paths(it)) self.emit("create-diff", paths) def launch_comparisons_on_selected(self): """Launch comparisons on all selected elements. """ pane = self._get_focused_pane() if pane != None: selected = self._get_selected_paths(pane) get_iter = self.model.get_iter for s in selected: self.launch_comparison( get_iter(s), pane ) def copy_selected(self, direction): assert direction in (-1,1) src_pane = self._get_focused_pane() if src_pane != None: dst_pane = src_pane + direction assert dst_pane >= 0 and dst_pane < self.num_panes paths = self._get_selected_paths(src_pane) paths.reverse() model = self.model for path in paths: #filter(lambda x: x.name!=None, sel): it = model.get_iter(path) name = model.value_path(it, src_pane) if name == None: continue src = model.value_path(it, src_pane) dst = model.value_path(it, dst_pane) try: if os.path.isfile(src): dstdir = os.path.dirname( dst ) if not os.path.exists( dstdir ): os.makedirs( dstdir ) shutil.copy2( src, dstdir ) self.file_created( path, dst_pane) elif os.path.isdir(src): if os.path.exists(dst): if misc.run_dialog( _("'%s' exists.\nOverwrite?") % os.path.basename(dst), parent = self, buttonstype = gtk.BUTTONS_OK_CANCEL) != gtk.RESPONSE_OK: continue misc.copytree(src, dst) self.recursively_update( path ) except (OSError,IOError), e: misc.run_dialog(_("Error copying '%s' to '%s'\n\n%s.") % (src, dst,e), self) def delete_selected(self): """Delete all selected files/folders recursively. """ # reverse so paths dont get changed pane = self._get_focused_pane() if pane != None: paths = self._get_selected_paths(pane) paths.reverse() for path in paths: it = self.model.get_iter(path) name = self.model.value_path(it, pane) try: if os.path.isfile(name): os.remove(name) self.file_deleted( path, pane) elif os.path.isdir(name): if misc.run_dialog(_("'%s' is a directory.\nRemove recusively?") % os.path.basename(name), parent = self, buttonstype=gtk.BUTTONS_OK_CANCEL) == gtk.RESPONSE_OK: shutil.rmtree(name) self.recursively_update( path ) self.file_deleted( path, pane) except OSError, e: misc.run_dialog(_("Error removing %s\n\n%s.") % (name,e), parent = self) def on_treeview_cursor_changed(self, *args): pane = self._get_focused_pane() if pane == None: return paths = self._get_selected_paths(pane) if len(paths) > 0: def rwx(mode): return "".join( [ ((mode& (1<<i)) and "xwr"[i%3] or "-") for i in range(8,-1,-1) ] ) def nice(deltat): times = ( (60, lambda n: ngettext("%i second","%i seconds",n)), (60, lambda n: ngettext("%i minute","%i minutes",n)), (24, lambda n: ngettext("%i hour","%i hours",n)), ( 7, lambda n: ngettext("%i day","%i days",n)), ( 4, lambda n: ngettext("%i week","%i weeks",n)), (12, lambda n: ngettext("%i month","%i months",n)), (100,lambda n: ngettext("%i year","%i years",n)) ) for units, msg in times: if abs(int(deltat)) < 5 * units: return msg(int(deltat)) % int(deltat) deltat /= units fname = self.model.value_path( self.model.get_iter(paths[0]), pane ) try: stat = os.stat(fname) except OSError: self.emit("status-changed", "" ) else: self.emit("status-changed", "%s : %s" % (rwx(stat.st_mode), nice(time.time() - stat.st_mtime) ) ) def on_switch_event(self): if self.treeview_focussed: self.scheduler.add_task( self.treeview_focussed.grab_focus ) self.scheduler.add_task( self.on_treeview_cursor_changed ) def on_treeview_key_press_event(self, view, event): pane = self.treeview.index(view) tree = None if gtk.keysyms.Right == event.keyval: if pane+1 < self.num_panes: tree = self.treeview[pane+1] elif gtk.keysyms.Left == event.keyval: if pane-1 >= 0: tree = self.treeview[pane-1] if tree != None: paths = self._get_selected_paths(pane) view.get_selection().unselect_all() tree.grab_focus() tree.get_selection().unselect_all() if len(paths): tree.set_cursor(paths[0]) for p in paths: tree.get_selection().select_path(p) tree.emit("cursor-changed") return event.keyval in (gtk.keysyms.Left, gtk.keysyms.Right) #handled def on_treeview_row_activated(self, view, path, column): it = self.model.get_iter(path) files = [] for i in range(self.num_panes): fname = self.model.value_path( it, i ) if os.path.exists(fname): files.append(fname) else: files.append(None) if files.count(None) != self.num_panes: # Its possible to have file 'foo' in one pane and dir 'foo' in another. # We want to do the right thing depending on the one clicked. clicked_pane = [ t.get_column(0) for t in self.treeview ].index(column) while files[clicked_pane] == None: # clicked on missing entry? clicked_pane = (clicked_pane+1) % self.num_panes if os.path.isfile( files[clicked_pane] ): self.emit("create-diff", filter(os.path.isfile, _not_none(files) )) else: if view.row_expanded(path): view.collapse_row(path) else: view.expand_row(path,0) def on_treeview_row_expanded(self, view, it, path): self._do_to_others(view, self.treeview, "expand_row", (path,0) ) self._update_diffmaps() def on_treeview_row_collapsed(self, view, me, path): self._do_to_others(view, self.treeview, "collapse_row", (path,) ) self._update_diffmaps() def on_treeview_focus_in_event(self, tree, event): self.treeview_focussed = tree pane = self.treeview.index(tree) if pane > 0: self.button_copy_left.set_sensitive(1) if pane+1 < self.num_panes: self.button_copy_right.set_sensitive(1) self.button_delete.set_sensitive(1) def on_treeview_focus_out_event(self, tree, event): self.button_copy_left.set_sensitive(0) self.button_copy_right.set_sensitive(0) self.button_delete.set_sensitive(0) # # Toolbar handlers # def on_button_diff_clicked(self, button): self.launch_comparisons_on_selected() def on_button_copy_left_clicked(self, button): self.copy_selected(-1) def on_button_copy_right_clicked(self, button): self.copy_selected(1) def on_button_delete_clicked(self, button): self.delete_selected() def on_button_edit_clicked(self, button): pane = self._get_focused_pane() if pane != None: m = self.model
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -