📄 dirdiff.py
字号:
files = [ m.value_path( m.get_iter(p), pane ) for p in self._get_selected_paths(pane) ] self._edit_files( [f for f in files if os.path.isfile(f)] ) def on_button_ignore_case_toggled(self, button): self.refresh() def _update_state_filter(self, state, active): assert state in (tree.STATE_NEW, tree.STATE_MODIFIED, tree.STATE_NORMAL) try: self.state_filters.remove( state ) except ValueError: pass if active: self.state_filters.append( state ) self.refresh() def on_filter_state_normal_toggled(self, button): self._update_state_filter( tree.STATE_NORMAL, button.get_active() ) def on_filter_state_new_toggled(self, button): self._update_state_filter( tree.STATE_NEW, button.get_active() ) def on_filter_state_modified_toggled(self, button): self._update_state_filter( tree.STATE_MODIFIED, button.get_active() ) def _update_name_filter(self, button, idx): for i in range(len(self.name_filters)): if self.name_filters[i] == self.name_filters_available[idx]: self.name_filters.pop(i) break if button.get_active(): self.name_filters.append( self.name_filters_available[idx] ) self.refresh() def on_filter_hide_current_clicked(self, button): pane = self._get_focused_pane() if pane != None: paths = self._get_selected_paths(pane) paths.reverse() for p in paths: self.model.remove( self.model.get_iter(p) ) # # Selection # def _get_selected_paths(self, pane): assert pane != None selected_paths = [] self.treeview[pane].get_selection().selected_foreach( lambda store, path, it: selected_paths.append( path ) ) return selected_paths # # Filtering # def _filter_on_state(self, roots, fileslist): """Get state of 'files' for filtering purposes. Returns STATE_NORMAL, STATE_NEW or STATE_MODIFIED roots - array of root directories fileslist - array of filename tuples of length len(roots) """ assert len(roots) == self.model.ntree ret = [] for files in fileslist: curfiles = [ os.path.join( r, f ) for r,f in zip(roots,files) ] is_present = [ os.path.exists( f ) for f in curfiles ] all_present = 0 not in is_present if all_present: if _files_same( curfiles, self.regexes ): state = tree.STATE_NORMAL else: state = tree.STATE_MODIFIED else: state = tree.STATE_NEW if state in self.state_filters: ret.append( files ) return ret def _update_item_state(self, it): """Update the state of the item at 'it' """ files = self.model.value_paths(it) def mtime(f): try: return os.stat(f).st_mtime except OSError: return 0 # find the newest file, checking also that they differ mod_times = [ mtime(f) for f in files[:self.num_panes] ] newest_index = mod_times.index( max(mod_times) ) if mod_times.count( max(mod_times) ) == len(mod_times): newest_index = -1 # all same all_present = 0 not in mod_times if all_present: all_same = _files_same( files, self.regexes ) all_present_same = all_same else: lof = [] for j in range(len(mod_times)): if mod_times[j]: lof.append( files[j] ) all_same = 0 all_present_same = _files_same( lof, self.regexes ) different = 1 for j in range(self.model.ntree): if mod_times[j]: isdir = os.path.isdir( files[j] ) if all_same == 1: self.model.set_state(it, j, tree.STATE_NORMAL, isdir) different = 0 elif all_same == 2: self.model.set_state(it, j, tree.STATE_NOCHANGE, isdir) different = 0 elif all_present_same: self.model.set_state(it, j, tree.STATE_NEW, isdir) else: self.model.set_state(it, j, tree.STATE_MODIFIED, isdir) self.model.set_value(it, self.model.column_index(COL_EMBLEM, j), j == newest_index and pixbuf_newer or None) else: self.model.set_state(it, j, tree.STATE_MISSING) return different def on_treeview_button_press_event(self, treeview, event): # unselect other panes for t in filter(lambda x:x!=treeview, self.treeview[:self.num_panes]): t.get_selection().unselect_all() if event.button == 3: try: path, col, cellx, celly = treeview.get_path_at_pos( int(event.x), int(event.y) ) except TypeError: pass # clicked outside tree else: treeview.grab_focus() selected = self._get_selected_paths( self.treeview.index(treeview) ) if len(selected) <= 1 and event.state == 0: treeview.set_cursor( path, col, 0) self.popup_menu.popup_in_pane( self.treeview.index(treeview) ) return event.state==0 return 0 def set_num_panes(self, n): if n != self.num_panes and n in (1,2,3): self.model = DirDiffTreeStore(n) for i in range(n): self.treeview[i].set_model(self.model) toshow = self.scrolledwindow[:n] + self.fileentry[:n] toshow += self.linkmap[:n-1] + self.diffmap[:n] map( lambda x: x.show(), toshow ) tohide = self.scrolledwindow[n:] + self.fileentry[n:] tohide += self.linkmap[n-1:] + self.diffmap[n:] map( lambda x: x.hide(), tohide ) if self.num_panes != 0: # not first time through self.num_panes = n self.on_fileentry_activate(None) else: self.num_panes = n def refresh(self): root = self.model.get_iter_root() if root: roots = self.model.value_paths(root) self.set_locations( roots ) def recompute_label(self): root = self.model.get_iter_root() filenames = self.model.value_paths(root) shortnames = misc.shorten_names(*filenames) self.label_text = " : ".join(shortnames) self.label_changed() def _update_diffmaps(self): self.diffmap[0].queue_draw() self.diffmap[1].queue_draw() def on_diffmap_expose_event(self, area, event): diffmapindex = self.diffmap.index(area) treeindex = (0, self.num_panes-1)[diffmapindex] treeview = self.treeview[treeindex] def traverse_states(root): todo = [root] model = self.model while len(todo): it = todo.pop(0) #print model.value_path(it, treeindex), model.get_state(it, treeindex) yield model.get_state(it, treeindex) path = model.get_path(it) if treeview.row_expanded(path): children = [] child = model.iter_children(it) while child: children.append(child) child = model.iter_next(child) todo = children + todo yield None # end marker chunks = [] laststate = None lastlines = 0 numlines = -1 for state in traverse_states( self.model.get_iter_root() ): if state != laststate: chunks.append( (lastlines, laststate) ) laststate = state lastlines = 1 else: lastlines += 1 numlines += 1 if not hasattr(area, "meldgc"): assert area.window gcd = area.window.new_gc() gcd.set_rgb_fg_color( gdk.color_parse(self.prefs.color_delete_bg) ) gcc = area.window.new_gc() gcc.set_rgb_fg_color( gdk.color_parse(self.prefs.color_replace_bg) ) gce = area.window.new_gc() gce.set_rgb_fg_color( gdk.color_parse("yellow") ) gcm = area.window.new_gc() gcm.set_rgb_fg_color( gdk.color_parse("white") ) gcb = area.window.new_gc() gcb.set_rgb_fg_color( gdk.color_parse("black") ) area.meldgc = [None, # ignore None, # none None, # normal None, # nochange gce, # error None, # empty gcd, # new gcc, # modified gcc, # conflict gcc, # removed gcm, # missing gcb ] # border assert len(area.meldgc) - 1 == tree.STATE_MAX #TODO need gutter of scrollbar - how do we get that? size_of_arrow = 14 hperline = float( area.get_allocation().height - 3*size_of_arrow) / numlines scaleit = lambda x,s=hperline,o=size_of_arrow: x*s+o x0 = 4 x1 = area.get_allocation().width - 2*x0 window = area.window window.clear() start = 0 for c in chunks[1:]: end = start + c[0] s,e = [int(x) for x in (math.floor(scaleit(start)), math.ceil(scaleit(end))) ] gc = area.meldgc[ int(c[1]) ] if gc: window.draw_rectangle( gc, 1, x0, s, x1, e-s) window.draw_rectangle( area.meldgc[-1], 0, x0, s, x1, e-s) start = end def on_diffmap_button_press_event(self, area, event): #TODO need gutter of scrollbar - how do we get that? if event.button == 1: size_of_arrow = 14 diffmapindex = self.diffmap.index(area) index = (0, self.num_panes-1)[diffmapindex] height = area.get_allocation().height fraction = (event.y - size_of_arrow) / (height - 3.75*size_of_arrow) adj = self.scrolledwindow[index].get_vadjustment() val = fraction * adj.upper - adj.page_size/2 upper = adj.upper - adj.page_size adj.set_value( max( min(upper, val), 0) ) return 1 return 0 def on_file_changed(self, changed_filename): """When a file has changed, try to find it in our tree and update its status if necessary """ model = self.model changed_paths = [] # search each panes tree for changed_filename for pane in range(self.num_panes): it = model.get_iter_root() current = model.value_path(it, pane).split(os.sep) changed = changed_filename.split(os.sep) # early exit. does filename begin with root? try: if changed[:len(current)] != current: continue except IndexError: continue changed = changed[len(current):] # search the tree component at a time for component in changed: child = model.iter_children( it ) while child: leading, name = os.path.split( model.value_path(child, pane) ) if component == name : # found it it = child break else: child = self.model.iter_next( child ) # next if not it: break # save if found and unique if it: path = model.get_path(it) if path not in changed_paths: changed_paths.append(path) # do the update for path in changed_paths: self._update_item_state( model.get_iter(path) ) def next_diff(self, direction): if self.treeview_focussed: pane = self.treeview.index( self.treeview_focussed ) else: pane = 0 start_iter = self.model.get_iter( (self._get_selected_paths(pane) or [(0,)])[-1] ) def goto_iter(it): curpath = self.model.get_path(it) for i in range(len(curpath)-1): self.treeview[pane].expand_row( curpath[:i+1], 0) self.treeview[pane].set_cursor(curpath) search = {gdk.SCROLL_UP : self.model.inorder_search_up}.get(direction, self.model.inorder_search_down) for it in search( start_iter ): state = int(self.model.get_state( it, pane )) if state not in (tree.STATE_NORMAL, tree.STATE_EMPTY): goto_iter(it) return def on_reload_activate(self, *extra): self.on_fileentry_activate(None)if gobject.pygtk_version < (2,8,0): gobject.type_register(DirDiff)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -