📄 filediff.py
字号:
def set_buffer_writable(self, buf, yesno): pane = self.textview.index(buf.textview) self.bufferdata[pane].writable = yesno self.recompute_label() def set_buffer_modified(self, buf, yesno): pane = self.textview.index(buf.textview) self.bufferdata[pane].modified = yesno self.recompute_label() def save(self): pane = self._get_focused_pane() if pane >= 0: self.save_file(pane) def save_all(self): for i in range(self.num_panes): if self.bufferdata[i].modified: self.save_file(i) def on_fileentry_activate(self, entry): if self.on_delete_event() != gtk.RESPONSE_CANCEL: files = [ e.get_full_path(0) for e in self.fileentry[:self.num_panes] ] self.set_files(files) return 1 def _get_focused_pane(self): for i in range(self.num_panes): if self.textview[i].is_focus(): return i return -1 def copy_selected(self, direction): assert direction in (-1,1) src_pane = self._get_focused_pane() dst_pane = src_pane + direction assert dst_pane in range(self.num_panes) buffers = [t.get_buffer() for t in self.textview] text = buffers[src_pane].get_text( buffers[src_pane].get_start_iter(), buffers[src_pane].get_end_iter() ) self.on_text_begin_user_action() buffers[dst_pane].set_text( text ) self.on_text_end_user_action() self.scheduler.add_task( lambda : self._sync_vscroll( self.scrolledwindow[src_pane].get_vadjustment() ) and None ) # # refresh and reload # def on_reload_activate(self, *extra): modified = [os.path.basename(b.label) for b in self.bufferdata if b.modified] if len(modified): message = _("Reloading will discard changes in:\n%s\n\nYou cannot undo this operation.") % "\n".join(modified) response = misc.run_dialog( message, parent=self, messagetype=gtk.MESSAGE_WARNING, buttonstype=gtk.BUTTONS_OK_CANCEL) if response != gtk.RESPONSE_OK: return files = [b.filename for b in self.bufferdata[:self.num_panes] ] self.set_files(files) def on_refresh_activate(self, *extra): files = [None for b in self.bufferdata[:self.num_panes] ] self.set_files(files) def queue_draw(self, junk=None): for i in range(self.num_panes-1): self.linkmap[i].queue_draw() self.diffmap0.queue_draw() self.diffmap1.queue_draw() # # scrollbars # def _sync_hscroll(self, adjustment): if not hasattr(self,"_sync_hscroll_lock"): self._sync_hscroll_lock = 0 if not self._sync_hscroll_lock: self._sync_hscroll_lock = 1 adjs = map( lambda x: x.get_hadjustment(), self.scrolledwindow) adjs.remove(adjustment) val = adjustment.get_value() for a in adjs: a.set_value(val) self._sync_hscroll_lock = 0 def _sync_vscroll(self, adjustment): # only allow one scrollbar to be here at a time if not hasattr(self,"_sync_vscroll_lock"): self._sync_vscroll_lock = 0 if (self.keymask & MASK_SHIFT)==0 and not self._sync_vscroll_lock: self._sync_vscroll_lock = 1 syncpoint = 0.5 adjustments = map( lambda x: x.get_vadjustment(), self.scrolledwindow) adjustments = adjustments[:self.num_panes] master = adjustments.index(adjustment) # scrollbar influence 0->1->2 or 0<-1<-2 or 0<-1->2 others = zip( range(self.num_panes), adjustments) del others[master] if master == 2: others.reverse() # the line to search for in the 'master' text master_y = adjustment.value + adjustment.page_size * syncpoint it = self.textview[master].get_line_at_y(master_y)[0] line_y, height = self.textview[master].get_line_yrange(it) line = it.get_line() + ((master_y-line_y)/height) for (i,adj) in others: mbegin,mend, obegin,oend = 0, self._get_line_count(master), 0, self._get_line_count(i) # look for the chunk containing 'line' for c in self.linediffer.pair_changes(master, i, self._get_texts()): c = c[1:] if c[0] >= line: mend = c[0] oend = c[2] break elif c[1] >= line: mbegin,mend = c[0],c[1] obegin,oend = c[2],c[3] break else: mbegin = c[1] obegin = c[3] fraction = (line - mbegin) / ((mend - mbegin) or 1) other_line = (obegin + fraction * (oend - obegin)) it = self.textview[i].get_buffer().get_iter_at_line(other_line) val, height = self.textview[i].get_line_yrange(it) val -= (adj.page_size) * syncpoint val += (other_line-int(other_line)) * height val = misc.clamp(val, 0, adj.upper - adj.page_size) adj.set_value( val ) # scrollbar influence 0->1->2 or 0<-1<-2 or 0<-1->2 if master != 1: line = other_line master = 1 self.on_linkmap_expose_event(self.linkmap0, None) self.on_linkmap_expose_event(self.linkmap1, None) self._sync_vscroll_lock = 0 # # diffmap drawing # def on_diffmap_expose_event(self, area, event): diffmapindex = self.diffmap.index(area) textindex = (0, self.num_panes-1)[diffmapindex] #TODO need height of arrow button on scrollbar - how do we get that? size_of_arrow = 14 hperline = float( self.scrolledwindow[textindex].get_allocation().height - 4*size_of_arrow) / self._get_line_count(textindex) if hperline > self.pixels_per_line: hperline = self.pixels_per_line 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() gctext = area.get_style().text_gc[0] if not hasattr(area, "meldgc"): self._setup_gcs(area) gc = area.meldgc.get_gc for c in self.linediffer.single_changes(textindex, self._get_texts()): assert c[0] != "equal" outline = True if self.prefs.ignore_blank_lines: c1,c2 = self._consume_blank_lines( self._get_texts()[textindex][c[1]:c[2]] ) if (c1 or c2) and (c[1]+c1 == c[2]-c2): outline = False s,e = [int(x) for x in ( math.floor(scaleit(c[1])), math.ceil(scaleit(c[2]+(c[1]==c[2]))) ) ] window.draw_rectangle( gc(c[0]), 1, x0, s, x1, e-s) if outline: window.draw_rectangle( gctext, 0, x0, s, x1, e-s) 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 _get_line_count(self, index): """Return the number of lines in the buffer of textview 'text'""" return self.textview[index].get_buffer().get_line_count() def set_num_panes(self, n): if n != self.num_panes and n in (1,2,3): self.num_panes = n toshow = self.scrolledwindow[:n] + self.fileentry[:n] toshow += self.linkmap[:n-1] + self.diffmap[:n] map( lambda x: x.show(), toshow ) tohide = self.statusimage + self.scrolledwindow[n:] + self.fileentry[n:] tohide += self.linkmap[n-1:] + self.diffmap[n:] map( lambda x: x.hide(), tohide ) for i in range(self.num_panes): if self.bufferdata[i].modified: self.statusimage[i].show() self.queue_draw() self.recompute_label() def _line_to_pixel(self, pane, line ): it = self.textview[pane].get_buffer().get_iter_at_line(line) return self.textview[pane].get_iter_location( it ).y def _pixel_to_line(self, pane, pixel ): return self.textview[pane].get_line_at_y( pixel )[0].get_line() def next_diff(self, direction): adjs = map( lambda x: x.get_vadjustment(), self.scrolledwindow) curline = self._pixel_to_line( 1, int(adjs[1].value + adjs[1].page_size/2) ) c = None if direction == gdk.SCROLL_DOWN: for c in self.linediffer.single_changes(1, self._get_texts()): assert c[0] != "equal" c1,c2 = self._consume_blank_lines( self._get_texts()[1][c[1]:c[2]] ) if c[1]+c1 == c[2]-c2: continue if c[1] > curline + 1: break else: #direction == gdk.SCROLL_UP for chunk in self.linediffer.single_changes(1, self._get_texts()): c1,c2 = self._consume_blank_lines( self._get_texts()[1][chunk[1]:chunk[2]] ) if chunk[1]+c1 == chunk[2]-c2: continue if chunk[2] < curline: c = chunk elif c: break if c: if c[2] - c[1]: # no range, use other side l0,l1 = c[1],c[2] aidx = 1 a = adjs[aidx] else: l0,l1 = c[3],c[4] aidx = c[5] a = adjs[aidx] want = 0.5 * ( self._line_to_pixel(aidx, l0) + self._line_to_pixel(aidx,l1) - a.page_size ) want = misc.clamp(want, 0, a.upper-a.page_size) a.set_value( want ) def _setup_gcs(self, area): 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(self.prefs.color_edited_bg) ) gcx = area.window.new_gc() gcx.set_rgb_fg_color( gdk.color_parse(self.prefs.color_conflict_bg) ) area.meldgc = misc.struct(gc_delete=gcd, gc_insert=gcd, gc_replace=gcc, gc_conflict=gcx) area.meldgc.get_gc = lambda p: getattr(area.meldgc, "gc_"+p) def _consume_blank_lines(self, txt): lo, hi = 0, 0 for l in txt: if len(l)==0: lo += 1 else: break for l in txt[lo:]: if len(l)==0: hi += 1 else: break return lo,hi # # linkmap drawing # def on_linkmap_expose_event(self, area, event): window = area.window # not mapped? if not window: return if not hasattr(area, "meldgc"): self._setup_gcs(area) gctext = area.get_style().bg_gc[gtk.STATE_ACTIVE]
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -