📄 filediff.py
字号:
x = self.keylookup.get(event.keyval, 0) if self.keymask & ~x != self.keymask: self.keymask &= ~x for l in self.linkmap[:self.num_panes-1]: a = l.get_allocation() w = self.pixbuf_copy0.get_width() l.queue_draw_area(0, 0, w, a[3]) l.queue_draw_area(a[2]-w, 0, w, a[3]) def is_modified(self): state = [b.modified for b in self.bufferdata] return 1 in state def _get_filename(self, i): return self.bufferdata[i].filename or "<unnamed>" def on_delete_event(self, appquit=0): modified = [b.modified for b in self.bufferdata] if 1 in modified: dialog = gnomeglade.Component( paths.share_dir("glade2/filediff.glade"), "closedialog") dialog.widget.set_transient_for(self.widget.get_toplevel()) buttons = [] for i in range(self.num_panes): b = gtk.CheckButton( self._get_filename(i) ) b.set_use_underline(False) buttons.append(b) dialog.box.pack_start(b, 1, 1) if not modified[i]: b.set_sensitive(0) else: b.set_active(1) dialog.widget.show_all() if not appquit: dialog.button_quit.hide() response = dialog.widget.run() try_save = [ b.get_active() for b in buttons] dialog.widget.destroy() if response==gtk.RESPONSE_OK: for i in range(self.num_panes): if try_save[i]: if self.save_file(i) != melddoc.RESULT_OK: return gtk.RESPONSE_CANCEL elif response==gtk.RESPONSE_CLOSE: return gtk.RESPONSE_CLOSE else: return gtk.RESPONSE_CANCEL return gtk.RESPONSE_OK # # text buffer undo/redo # def on_text_begin_user_action(self, *buffer): self.undosequence.begin_group() def on_text_end_user_action(self, *buffer): self.undosequence.end_group() def on_text_insert_text(self, buffer, it, text, textlen): if not self.undosequence_busy: self.undosequence.begin_group() pane = self.textview.index( buffer.textview ) if self.bufferdata[pane].modified != 1: self.undosequence.add_action( BufferModifiedAction(buffer, self) ) self.undosequence.add_action( BufferInsertionAction(buffer, it.get_offset(), text) ) self.undosequence.end_group() def on_text_delete_range(self, buffer, it0, it1): text = buffer.get_text(it0, it1, 0) pane = self.textview.index(buffer.textview) assert self.deleted_lines_pending == -1 self.deleted_lines_pending = text.count("\n") if not self.undosequence_busy: self.undosequence.begin_group() if self.bufferdata[pane].modified != 1: self.undosequence.add_action( BufferModifiedAction(buffer, self) ) self.undosequence.add_action( BufferDeletionAction(buffer, it0.get_offset(), text) ) self.undosequence.end_group() # # # def _get_focused_textview(self): for i in range(self.num_panes): t = self.textview[i] if t.is_focus(): return t return None def on_find_activate(self, *args): self.keymask = 0 self.queue_draw() if self.find_dialog: self.find_dialog.widget.present() else: class FindDialog(gnomeglade.Component): def __init__(self, app): self.parent = app self.pane = -1 gladefile = paths.share_dir("glade2/filediff.glade") gnomeglade.Component.__init__(self, gladefile, "finddialog") self.widget.set_transient_for(app.widget.get_toplevel()) self.widget.show_all() def on_destroy(self, *args): self.parent.find_dialog = None self.widget.destroy() def on_entry_search_for_activate(self, *args): self.parent._find_text( self.entry_search_for.get_chars(0,-1), self.check_case.get_active(), self.check_word.get_active(), self.check_wrap.get_active(), self.check_regex.get_active() ) return 1 self.find_dialog = FindDialog(self) def on_find_next_activate(self, *args): if self.last_search: s = self.last_search self._find_text(s.text, s.case, s.word, s.wrap, s.regex) else: self.on_find_activate() def on_copy_activate(self, *extra): t = self._get_focused_textview() if t: t.emit("copy-clipboard") #XXX .get_buffer().copy_clipboard() def on_cut_activate(self, *extra): t = self._get_focused_textview() if t: t.emit("cut-clipboard") #XXX get_buffer().cut_clipboard() def on_paste_activate(self, *extra): t = self._get_focused_textview() if t: t.emit("paste-clipboard") #XXX t.get_buffer().paste_clipboard(None, 1) def on_textview_button_press_event(self, textview, event): if event.button == 3: textview.grab_focus() pane = self.textview.index(textview) self.popup_menu.popup_in_pane( pane ) return 1 return 0 def on_textview_toggle_overwrite(self, view): self.textview_overwrite = not self.textview_overwrite for v,h in zip(self.textview, self.textview_overwrite_handlers): v.disconnect(h) if v != view: v.emit("toggle-overwrite") self.textview_overwrite_handlers = [ t.connect("toggle-overwrite", self.on_textview_toggle_overwrite) for t in self.textview ] self._update_cursor_status(view.get_buffer()) # # find/replace buffer # def _find_text(self, tofind_utf8, match_case=0, entire_word=0, wrap=1, regex=0): self.last_search = misc.struct(text=tofind_utf8, case=match_case, word=entire_word, wrap=wrap, regex=regex) view = self._get_focused_textview() or self.textview0 buf = view.get_buffer() insert = buf.get_iter_at_mark( buf.get_insert() ) tofind = tofind_utf8.decode("utf-8") # tofind is utf-8 encoded text = buf.get_text(*buf.get_bounds() ).decode("utf-8") # as is buffer if not regex: tofind = re.escape(tofind) if entire_word: tofind = r'\b' + tofind + r'\b' try: pattern = re.compile( tofind, (match_case and re.M or (re.M|re.I)) ) except re.error, e: misc.run_dialog( _("Regular expression error\n'%s'") % e, self, messagetype=gtk.MESSAGE_ERROR) else: match = pattern.search(text, insert.get_offset()+1) if match == None and wrap: match = pattern.search(text, 0) if match: it = buf.get_iter_at_offset( match.start() ) buf.place_cursor( it ) it.forward_chars( match.end() - match.start() ) buf.move_mark( buf.get_selection_bound(), it ) view.scroll_to_mark( buf.get_insert(), 0) elif regex: misc.run_dialog( _("The regular expression '%s' was not found.") % tofind_utf8, self, messagetype=gtk.MESSAGE_INFO) else: misc.run_dialog( _("The text '%s' was not found.") % tofind_utf8, self, messagetype=gtk.MESSAGE_INFO) # # text buffer loading/saving # def recompute_label(self): filenames = [] for i in range(self.num_panes): filenames.append( self._get_filename(i) ) shortnames = misc.shorten_names(*filenames) for i in range(self.num_panes): stock = None if self.bufferdata[i].modified == 1: shortnames[i] += "*" if self.bufferdata[i].writable == 1: stock = gtk.STOCK_SAVE else: stock = gtk.STOCK_SAVE_AS elif self.bufferdata[i].writable == 0: stock = gtk.STOCK_NO if stock: self.statusimage[i].show() self.statusimage[i].set_from_stock(stock, gtk.ICON_SIZE_SMALL_TOOLBAR) else: self.statusimage[i].hide() self.label_text = " : ".join(shortnames) self.label_changed() def set_files(self, files): """Set num panes to len(files) and load each file given. If an element is None, the text of a pane is left as is. """ for i,f in enumerate(files): if f: b = self.textview[i].get_buffer() b.delete( b.get_start_iter(), b.get_end_iter() ) absfile = os.path.abspath(f) self.fileentry[i].set_filename(absfile) self.bufferdata[i] = MeldBufferData(absfile) self.recompute_label() self.scheduler.add_task( self._set_files_internal(files).next ) def _set_files_internal(self, files): yield _("[%s] Set num panes") % self.label_text self.set_num_panes( len(files) ) self._disconnect_buffer_handlers() self.linediffer.diffs = [[],[]] self.queue_draw() buffers = [t.get_buffer() for t in self.textview][:self.num_panes] try_codecs = self.prefs.text_codecs.split() yield _("[%s] Opening files") % self.label_text panetext = ["\n"] * self.num_panes tasks = [] for i,f in enumerate(files): if f: try: task = misc.struct(filename = f, file = codecs.open(f, "rU", try_codecs[0]), buf = buffers[i], codec = try_codecs[:], text = [], pane = i) tasks.append(task) except (IOError, LookupError), e: buffers[i].set_text("\n") misc.run_dialog( "%s\n\n%s\n%s" % ( _("Could not read from '%s'") % f, _("The error was:"), str(e)), parent = self) else: panetext[i] = buffers[i].get_text( buffers[i].get_start_iter(), buffers[i].get_end_iter() ) yield _("[%s] Reading files") % self.label_text while len(tasks): for t in tasks[:]: try: nextbit = t.file.read(4096) if nextbit.find("\x00") != -1: misc.run_dialog( "%s\n\n%s" % ( _("Could not read from '%s'") % t.filename, _("It contains ascii nulls.\nPerhaps it is a binary file.") ), parent = self ) t.buf.delete( t.buf.get_start_iter(), t.buf.get_end_iter() ) tasks.remove(t) except ValueError, err: t.codec.pop(0) if len(t.codec): t.file = codecs.open(t.filename, "rU", t.codec[0]) t.buf.delete( t.buf.get_start_iter(), t.buf.get_end_iter() ) t.text = [] else: print "codec error fallback", err t.buf.delete( t.buf.get_start_iter(), t.buf.get_end_iter() ) misc.run_dialog( "%s\n\n%s" % ( _("Could not read from '%s'") % t.filename, _("I tried encodings %s.") % try_codecs ), parent = self) tasks.remove(t) except IOError, ioerr: misc.run_dialog( "%s\n\n%s\n%s" % ( _("Could not read from '%s'") % t.filename, _("The error was:"), str(ioerr)), parent = self) tasks.remove(t) else: if len(nextbit): t.buf.insert( t.buf.get_end_iter(), nextbit ) t.text.append(nextbit) else: self.set_buffer_writable(t.buf, os.access(t.filename, os.W_OK)) self.bufferdata[t.pane].encoding = t.codec[0] if hasattr(t.file, "newlines"): self.bufferdata[t.pane].newlines = t.file.newlines tasks.remove(t) panetext[t.pane] = "".join(t.text) if len(panetext[t.pane]) and \ panetext[t.pane][-1] != "\n" and \ self.prefs.supply_newline: t.buf.insert( t.buf.get_end_iter(), "\n") panetext[t.pane] += "\n" yield 1 self.undosequence.clear() yield _("[%s] Computing differences") % self.label_text panetext = [self._filter_text(p) for p in panetext] lines = map(lambda x: x.split("\n"), panetext) step = self.linediffer.set_sequences_iter(*lines) while step.next() == None: yield 1 self.queue_draw() lenseq = [len(d) for d in self.linediffer.diffs] self.scheduler.add_task( self._update_highlighting( (0,lenseq[0]), (0,lenseq[1]) ).next ) self._connect_buffer_handlers() if sourceview_available: for i in range(len(files)): if files[i]: set_highlighting_enabled( self.textview[i].get_buffer(), files[i], self.prefs.use_syntax_highlighting ) yield 0 def _update_highlighting(self, range0, range1): buffers = [t.get_buffer() for t in self.textview] for b in buffers: taglist = ["delete line", "conflict line", "replace line", "inline line"] table = b.get_tag_table() for tagname in taglist: tag = table.lookup(tagname) b.remove_tag(tag, b.get_start_iter(), b.get_end_iter() ) for chunk in self.linediffer.all_changes(self._get_texts()): for i,c in enumerate(chunk): if c and c[0] == "replace": bufs = buffers[1], buffers[i*2] #tags = [b.get_tag_table().lookup("replace line") for b in bufs] starts = [b.get_iter_at_line(l) for b,l in zip(bufs, (c[1],c[3])) ] text1 = "\n".join( self._get_texts(raw=1)[1 ][c[1]:c[2]] ).encode("utf16") text1 = struct.unpack("%iH"%(len(text1)/2), text1)[1:] textn = "\n".join( self._get_texts(raw=1)[i*2][c[3]:c[4]] ).encode("utf16") textn = struct.unpack("%iH"%(len(textn)/2), textn)[1:] matcher = difflib.SequenceMatcher(None, text1, textn) #print "<<<\n%s\n---\n%s\n>>>" % (text1, textn) tags = [b.get_tag_table().lookup("inline line") for b in bufs] back = (0,0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -