📄 vcview.py
字号:
### Copyright (C) 2002-2006 Stephen Kennedy <stevek@gnome.org>### This program is free software; you can redistribute it and/or modify### it under the terms of the GNU General Public License as published by### the Free Software Foundation; either version 2 of the License, or### (at your option) any later version.### This program is distributed in the hope that it will be useful,### but WITHOUT ANY WARRANTY; without even the implied warranty of### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the### GNU General Public License for more details.### You should have received a copy of the GNU General Public License### along with this program; if not, write to the Free Software### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USAfrom __future__ import generatorsimport tempfileimport shutilimport gtkimport osimport reimport treeimport miscimport gnomegladeimport melddocimport pathsimport vc################################################################################## Local Functions#################################################################################def _expand_to_root( treeview, path ): """Expand rows from path up to root""" start = path[:] while len(start) and not treeview.row_expanded(start): start = start[:-1] level = len(start) while level < len(path): level += 1 treeview.expand_row( path[:level], 0)def _commonprefix(files): if len(files) != 1: workdir = misc.commonprefix(files) else: workdir = os.path.dirname(files[0]) return workdir################################################################################## CommitDialog#################################################################################class CommitDialog(gnomeglade.Component): def __init__(self, parent): gnomeglade.Component.__init__(self, paths.share_dir("glade2/vcview.glade"), "commitdialog") self.parent = parent self.widget.set_transient_for( parent.widget.get_toplevel() ) selected = parent._get_selected_files() topdir = _commonprefix(selected) selected = [ s[len(topdir):] for s in selected ] self.changedfiles.set_text( ("(in %s) "%topdir) + " ".join(selected) ) self.widget.show_all() def run(self): self.previousentry.list.select_item(0) self.textview.grab_focus() buf = self.textview.get_buffer() buf.place_cursor( buf.get_start_iter() ) buf.move_mark( buf.get_selection_bound(), buf.get_end_iter() ) response = self.widget.run() msg = buf.get_text(buf.get_start_iter(), buf.get_end_iter(), 0) if response == gtk.RESPONSE_OK: self.parent._command_on_selected( self.parent.vc.commit_command(msg) ) if len(msg.strip()): self.previousentry.prepend_history(1, msg) self.widget.destroy() def on_previousentry_activate(self, gentry): buf = self.textview.get_buffer() buf.set_text( gentry.gtk_entry().get_text() )COL_LOCATION, COL_STATUS, COL_REVISION, COL_TAG, COL_OPTIONS, COL_END = range(tree.COL_END, tree.COL_END+6)class VcTreeStore(tree.DiffTreeStore): def __init__(self): types = [type("")] * COL_END types[tree.COL_ICON] = type(tree.pixbuf_file) gtk.TreeStore.__init__(self, *types) self.ntree = 1 self._setup_default_styles() self.textstyle[tree.STATE_MISSING] = '<span foreground="#000088" strikethrough="true" weight="bold">%s</span>'################################################################################## DirDiffMenu#################################################################################class VcMenu(gnomeglade.Component): def __init__(self, app, event): gladefile = paths.share_dir("glade2/vcview.glade") gnomeglade.Component.__init__(self, gladefile, "menu_popup") self.parent = app self.widget.popup( None, None, None, 3, event.time ) def on_diff_activate(self, menuitem): self.parent.on_button_diff_clicked( menuitem ) def on_edit_activate(self, menuitem): self.parent._edit_files( self.parent._get_selected_files() ) def on_update_activate(self, menuitem): self.parent.on_button_update_clicked( menuitem ) def on_commit_activate(self, menuitem): self.parent.on_button_commit_clicked( menuitem ) def on_add_activate(self, menuitem): self.parent.on_button_add_clicked( menuitem ) def on_add_binary_activate(self, menuitem): self.parent.on_button_add_binary_clicked( menuitem ) def on_remove_activate(self, menuitem): self.parent.on_button_remove_clicked( menuitem ) def on_revert_activate(self, menuitem): self.parent.on_button_revert_clicked( menuitem ) def on_remove_locally_activate(self, menuitem): self.parent.on_button_delete_clicked( menuitem )################################################################################# filters################################################################################entry_modified = lambda x: (x.state >= tree.STATE_NEW) or (x.isdir and (x.state > tree.STATE_NONE))entry_normal = lambda x: (x.state == tree.STATE_NORMAL)entry_nonvc = lambda x: (x.state == tree.STATE_NONE) or (x.isdir and (x.state > tree.STATE_IGNORED))entry_ignored = lambda x: (x.state == tree.STATE_IGNORED) or x.isdir################################################################################## VcView#################################################################################class VcView(melddoc.MeldDoc, gnomeglade.Component): def __init__(self, prefs): melddoc.MeldDoc.__init__(self, prefs) gnomeglade.Component.__init__(self, paths.share_dir("glade2/vcview.glade"), "vcview") self.toolbar.set_style( self.prefs.get_toolbar_style() ) self.tempdirs = [] self.model = VcTreeStore() self.treeview.set_model(self.model) self.treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE) self.treeview.set_headers_visible(1) column = gtk.TreeViewColumn( _("Name") ) renpix = gtk.CellRendererPixbuf() rentext = gtk.CellRendererText() column.pack_start(renpix, expand=0) column.pack_start(rentext, expand=1) column.set_attributes(renpix, pixbuf=self.model.column_index(tree.COL_ICON, 0)) column.set_attributes(rentext, markup=self.model.column_index(tree.COL_TEXT, 0)) self.treeview.append_column(column) def addCol(name, num): column = gtk.TreeViewColumn(name) rentext = gtk.CellRendererText() column.pack_start(rentext, expand=0) column.set_attributes(rentext, markup=self.model.column_index(num, 0)) self.treeview.append_column(column) return column self.treeview_column_location = addCol( _("Location"), COL_LOCATION) addCol(_("Status"), COL_STATUS) addCol(_("Rev"), COL_REVISION) addCol(_("Tag"), COL_TAG) addCol(_("Options"), COL_OPTIONS) class ConsoleStream(object): def __init__(this, textview): this.textview = textview b = textview.get_buffer() this.mark = b.create_mark("END", b.get_end_iter(), 0) def write(this, s): if s: b = this.textview.get_buffer() b.insert(b.get_end_iter(), s) this.textview.scroll_mark_onscreen( this.mark ) self.consolestream = ConsoleStream(self.consoleview) self.location = None self.treeview_column_location.set_visible( self.button_flatten.get_active() ) size = self.fileentry.size_request()[1] self.button_jump.set_size_request(size, size) self.button_jump.hide() if not self.prefs.vc_console_visible: self.on_console_view_toggle(self.console_hide_box) def set_location(self, location): self.model.clear() self.location = location = os.path.abspath(location or ".") self.fileentry.gtk_entry().set_text(location) self.vc = vc.Vc(location) it = self.model.add_entries( None, [location] ) self.treeview.get_selection().select_iter(it) self.model.set_state(it, 0, tree.STATE_NORMAL, isdir=1) self.recompute_label() self.scheduler.remove_all_tasks() self.scheduler.add_task( self._search_recursively_iter(self.model.get_iter_root()).next ) def recompute_label(self): self.label_text = os.path.basename(self.location) self.label_changed() def _search_recursively_iter(self, iterstart): yield _("[%s] Scanning %s") % (self.label_text,"") rootpath = self.model.get_path( iterstart ) rootname = self.model.value_path( self.model.get_iter(rootpath), 0 ) prefixlen = 1 + len( self.model.value_path( self.model.get_iter_root(), 0 ) ) todo = [ (rootpath, rootname) ] filters = [] if self.button_modified.get_active(): filters.append( entry_modified ) if self.button_normal.get_active(): filters.append( entry_normal ) if self.button_nonvc.get_active(): filters.append( entry_nonvc ) if self.button_ignored.get_active(): filters.append( entry_ignored ) def showable(entry): for f in filters: if f(entry): return 1 recursive = self.button_flatten.get_active() self.vc.cache_inventory(rootname) while len(todo): todo.sort() # depth first path, name = todo.pop(0) if path: it = self.model.get_iter( path ) root = self.model.value_path( it, 0 ) else: it = self.model.get_iter_root() root = name yield _("[%s] Scanning %s") % (self.label_text, root[prefixlen:]) #import time; time.sleep(1.0) entries = filter(showable, self.vc.listdir(root)) differences = 0 for e in entries: differences |= (e.state != tree.STATE_NORMAL) if e.isdir and recursive: todo.append( (None, e.path) ) continue child = self.model.add_entries(it, [e.path]) self._update_item_state( child, e, root[prefixlen:] ) if e.isdir: todo.append( (self.model.get_path(child), None) ) if not recursive: # expand parents if len(entries) == 0: self.model.add_empty(it, _("(Empty)")) if differences or len(path)==1: _expand_to_root( self.treeview, path ) else: # just the root self.treeview.expand_row( (0,), 0) self.vc.uncache_inventory() def on_preference_changed(self, key, value): if key == "toolbar_style": self.toolbar.set_style( self.prefs.get_toolbar_style() ) def on_fileentry_activate(self, fileentry): path = fileentry.get_full_path(0) self.set_location(path) def on_quit_event(self): self.scheduler.remove_all_tasks() for f in self.tempdirs: if os.path.exists(f): shutil.rmtree(f, ignore_errors=1) def on_delete_event(self, appquit=0): self.on_quit_event() return gtk.RESPONSE_OK def on_row_activated(self, treeview, path, tvc): it = self.model.get_iter(path) if self.model.iter_has_child(it): if self.treeview.row_expanded(path): self.treeview.collapse_row(path) else: self.treeview.expand_row(path,0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -