📄 filediff.py
字号:
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" if c[1] > curline + 1: break else: #direction == gdk.SCROLL_UP for chunk in self.linediffer.single_changes(1, self._get_texts()): 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] alloc = area.get_allocation() (wtotal,htotal) = alloc.width, alloc.height window.begin_paint_rect( (0,0,wtotal,htotal) ) window.clear() # gain function for smoothing #TODO cache these values bias = lambda x,g: math.pow(x, math.log(g) / math.log(0.5)) def gain(t,g): if t<0.5: return bias(2*t,1-g)/2.0 else: return (2-bias(2-2*t,1-g))/2.0 f = lambda x: gain( x, 0.85) if self.keymask & MASK_SHIFT: pix0 = self.pixbuf_delete pix1 = self.pixbuf_delete elif self.keymask & MASK_CTRL: pix0 = self.pixbuf_copy0 pix1 = self.pixbuf_copy1 else: # self.keymask == 0: pix0 = self.pixbuf_apply0 pix1 = self.pixbuf_apply1 draw_style = self.prefs.draw_style gc = area.meldgc.get_gc which = self.linkmap.index(area) pix_start = [None] * self.num_panes pix_start[which ] = self.textview[which ].get_visible_rect().y pix_start[which+1] = self.textview[which+1].get_visible_rect().y def bounds(idx): return [self._pixel_to_line(idx, pix_start[idx]), self._pixel_to_line(idx, pix_start[idx]+htotal)] visible = [None] + bounds(which) + bounds(which+1) for c in self.linediffer.pair_changes(which, which+1, self._get_texts()): if self.prefs.ignore_blank_lines: c1,c2 = self._consume_blank_lines( self._get_texts()[which ][c[1]:c[2]] ) c3,c4 = self._consume_blank_lines( self._get_texts()[which+1][c[3]:c[4]] ) c = c[0], c[1]+c1,c[2]-c2, c[3]+c3,c[4]-c4 if c[1]==c[2] and c[3]==c[4]: continue assert c[0] != "equal" if c[2] < visible[1] and c[4] < visible[3]: # find first visible chunk continue elif c[1] > visible[2] and c[3] > visible[4]: # we've gone past last visible break f0,f1 = [self._line_to_pixel(which, l) - pix_start[which ] for l in c[1:3] ] t0,t1 = [self._line_to_pixel(which+1, l) - pix_start[which+1] for l in c[3:5] ] if f0==f1: f0 -= 2; f1 += 2 if t0==t1: t0 -= 2; t1 += 2 if draw_style > 0: n = (1, 9)[draw_style-1] points0 = [] points1 = [] for t in map(lambda x: float(x)/n, range(n+1)): points0.append( (int( t*wtotal), int((1-f(t))*f0 + f(t)*t0 )) ) points1.append( (int((1-t)*wtotal), int(f(t)*f1 + (1-f(t))*t1 )) ) points = points0 + points1 + [points0[0]] window.draw_polygon( gc(c[0]), 1, points) window.draw_lines(gctext, points0) window.draw_lines(gctext, points1) else: w = wtotal p = self.pixbuf_apply0.get_width() window.draw_polygon(gctext, 0, (( -1, f0), ( p, f0), ( p,f1), ( -1,f1)) ) window.draw_polygon(gctext, 0, ((w+1, t0), (w-p, t0), (w-p,t1), (w+1,t1)) ) points0 = (0,f0), (0,t0) window.draw_line( gctext, p, (f0+f1)/2, w-p, (t0+t1)/2 ) x = wtotal-self.pixbuf_apply0.get_width() if c[0]=="insert": window.draw_pixbuf( gctext, pix1, 0,0, x, points0[-1][1], -1,-1, 0,0,0) elif c[0] == "delete": window.draw_pixbuf( gctext, pix0, 0,0, 0, points0[ 0][1], -1,-1, 0,0,0) else: #replace window.draw_pixbuf( gctext, pix0, 0,0, 0, points0[ 0][1], -1,-1, 0,0,0) window.draw_pixbuf( gctext, pix1, 0,0, x, points0[-1][1], -1,-1, 0,0,0) # allow for scrollbar at end of textview mid = 0.5 * self.textview0.get_allocation().height window.draw_line(gctext, int(.25*wtotal), int(mid), int(.75*wtotal), int(mid) ) window.end_paint() def on_linkmap_scroll_event(self, area, event): self.next_diff(event.direction) def on_linkmap_button_press_event(self, area, event): if event.button == 1: self.focus_before_click = None for t in self.textview: if t.is_focus(): self.focus_before_click = t break area.grab_focus() self.mouse_chunk = None alloc = area.get_allocation() (wtotal,htotal) = alloc.width, alloc.height pix_width = self.pixbuf_apply0.get_width() pix_height = self.pixbuf_apply0.get_height() if self.keymask == MASK_CTRL: # hack pix_height *= 2 which = self.linkmap.index(area) # quick reject are we near the gutter? if event.x < pix_width: side = 0 rect_x = 0 elif event.x > wtotal - pix_width: side = 1 rect_x = wtotal - pix_width else: return 1 src = which + side dst = which + 1 - side adj = self.scrolledwindow[src].get_vadjustment() func = lambda c: self._line_to_pixel(src, c[1]) - adj.value for c in self.linediffer.pair_changes(src, dst, self._get_texts()): if self.prefs.ignore_blank_lines: c1,c2 = self._consume_blank_lines( self._get_texts()[src][c[1]:c[2]] ) c3,c4 = self._consume_blank_lines( self._get_texts()[dst][c[3]:c[4]] ) c = c[0], c[1]+c1,c[2]-c2, c[3]+c3,c[4]-c4 if c[1]==c[2] and c[3]==c[4]: continue if c[0] == "insert": continue h = func(c) if h < 0: # find first visible chunk continue elif h > htotal: # we've gone past last visible break elif h < event.y and event.y < h + pix_height: self.mouse_chunk = ( (src,dst), (rect_x, h, pix_width, pix_height), c) break #print self.mouse_chunk return 1 elif event.button == 2: self.linkmap_drag_coord = event.x return 0 def on_linkmap_motion_notify_event(self, area, event): return #dx = event.x - self.linkmap_drag_coord #self.linkmap_drag_coord = event.x #w,h = self.scrolledwindow0.size_request() #w,h = size[2] - size[0], size[3] - size[1] #self.scrolledwindow0.set_size_request(w+dx,h) #print w+dx #textview0.get_allocation( #print misc.all(event) def on_linkmap_button_release_event(self, area, event): if event.button == 1: if self.focus_before_click: self.focus_before_click.grab_focus() self.focus_before_click = None if self.mouse_chunk: (src,dst), rect, chunk = self.mouse_chunk # check we're still in button inrect = lambda p, r: ((r[0] < p.x) and (p.x < r[0]+r[2]) and (r[1] < p.y) and (p.y < r[1]+r[3])) if inrect(event, rect): # gtk tries to jump back to where the cursor was unless we move the cursor self.textview[src].place_cursor_onscreen() self.textview[dst].place_cursor_onscreen() chunk = chunk[1:] self.mouse_chunk = None if self.keymask & MASK_SHIFT: # delete b = self.textview[src].get_buffer() b.delete(b.get_iter_at_line(chunk[0]), b.get_iter_at_line(chunk[1])) elif self.keymask & MASK_CTRL: # copy up or down b0 = self.textview[src].get_buffer() t0 = b0.get_text( b0.get_iter_at_line(chunk[0]), b0.get_iter_at_line(chunk[1]), 0) b1 = self.textview[dst].get_buffer() if event.y - rect[1] < 0.5 * rect[3]: # copy up b1.insert_with_tags_by_name(b1.get_iter_at_line(chunk[2]), t0, "edited line") else: # copy down b1.insert_with_tags_by_name(b1.get_iter_at_line(chunk[3]), t0, "edited line") else: # replace b0 = self.textview[src].get_buffer() t0 = b0.get_text( b0.get_iter_at_line(chunk[0]), b0.get_iter_at_line(chunk[1]), 0) b1 = self.textview[dst].get_buffer() self.on_text_begin_user_action() b1.delete(b1.get_iter_at_line(chunk[2]), b1.get_iter_at_line(chunk[3])) b1.insert_with_tags_by_name(b1.get_iter_at_line(chunk[2]), t0, "edited line") self.on_text_end_user_action() return 1 return 0 def on_linkmap_drag_begin(self, *args): print argsif gobject.pygtk_version < (2,8,0): gobject.type_register(FileDiff)################################################################################## Local Functions#################################################################################class MeldBufferData(object): __slots__ = ("modified", "writable", "filename", "encoding", "newlines") def __init__(self, filename=None): self.modified = 0 self.writable = 1 self.filename = filename self.encoding = None self.newlines = None################################################################################## BufferInsertionAction#################################################################################class BufferInsertionAction(object): """A helper to undo/redo text insertion into a text buffer""" def __init__(self, buffer, offset, text): self.buffer = buffer self.offset = offset self.text = text def undo(self): b = self.buffer b.delete( b.get_iter_at_offset( self.offset), b.get_iter_at_offset(self.offset + len(self.text)) ) def redo(self): b = self.buffer b.insert( b.get_iter_at_offset( self.offset), self.text)################################################################################## BufferDeletionAction#################################################################################class BufferDeletionAction(object): """A helper to undo/redo text deletion from a text buffer""" def __init__(self, buffer, offset, text): self.buffer = buffer self.offset = offset self.text = text def undo(self): b = self.buffer b.insert( b.get_iter_at_offset( self.offset), self.text) def redo(self): b = self.buffer b.delete( b.get_iter_at_offset( self.offset), b.get_iter_at_offset(self.offset + len(self.text)) )################################################################################## BufferModifiedAction#################################################################################class BufferModifiedAction(object): """A helper set modified flag on a text buffer""" def __init__(self, buffer, app): self.buffer, self.app = buffer, app self.app.set_buffer_modified(self.buffer, 1) def undo(self): self.app.set_buffer_modified(self.buffer, 0) def redo(self): self.app.set_buffer_modified(self.buffer, 1)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -