Changeset 7a6e8c8784f8…
Parent 1dd1d6a1cc5b…
by
Changes to 3 files · Browse files at 7a6e8c8784f8 Showing diff from parent 1dd1d6a1cc5b Diff from another changeset...
|
|
@@ -91,7 +91,7 @@ self.mode = 'commit'
self.nextbranch = None
self.closebranch = False
- self._last_commit_id = None
+ self.last_commit_id = None
self.qnew = False
self.notify_func = None
@@ -127,18 +127,18 @@ for entry in self.filemodel :
if entry[FM_STATUS] in 'MAR':
entry[FM_CHECKED] = True
- self._update_check_count()
+ self.update_check_count()
self.opts['check'] = False
def save_settings(self):
settings = GStatus.save_settings(self)
- settings['commit-vpane'] = self._vpaned.get_position()
+ settings['commit-vpane'] = self.vpaned.get_position()
return settings
def load_settings(self, settings):
- self.connect('delete-event', self._delete)
+ self.connect('delete-event', self.delete)
GStatus.load_settings(self, settings)
self._setting_vpos = -1
try:
@@ -150,12 +150,12 @@ def get_tbbuttons(self):
tbbuttons = GStatus.get_tbbuttons(self)
tbbuttons.insert(2, gtk.SeparatorToolItem())
- self._undo_button = self.make_toolbutton(gtk.STOCK_UNDO, _('_Undo'),
- self._undo_clicked, tip=_('undo recent commit'))
- self._commit_button = self.make_toolbutton(gtk.STOCK_OK, _('_Commit'),
- self._commit_clicked, tip=_('commit'))
- tbbuttons.insert(2, self._undo_button)
- tbbuttons.insert(2, self._commit_button)
+ self.undo_button = self.make_toolbutton(gtk.STOCK_UNDO, _('_Undo'),
+ self.undo_clicked, tip=_('undo recent commit'))
+ self.commit_button = self.make_toolbutton(gtk.STOCK_OK, _('_Commit'),
+ self.commit_clicked, tip=_('commit'))
+ tbbuttons.insert(2, self.undo_button)
+ tbbuttons.insert(2, self.commit_button)
return tbbuttons
@@ -173,12 +173,12 @@ buf.set_text(model[index][1])
buf.set_modified(False)
- def _first_msg_popdown(self, combo, shown):
+ def first_msg_popdown(self, combo, shown):
combo.disconnect(self.popupid)
self.popupid = None
- self._update_recent_messages()
+ self.update_recent_messages()
- def _update_recent_messages(self, msg=None):
+ def update_recent_messages(self, msg=None):
if msg is not None:
self._mru_messages.add(msg)
self.settings.write()
@@ -216,7 +216,7 @@ mbox.pack_start(label, False, False, 2)
self.qnew_name = gtk.Entry()
self.qnew_name.set_width_chars(20)
- self.qnew_name.connect('changed', self._qnew_changed)
+ self.qnew_name.connect('changed', self.qnew_changed)
mbox.pack_start(self.qnew_name, False, False, 2)
else:
self.qnew_name = None
@@ -229,7 +229,7 @@ liststore.append([_('Recent Commit Messages...'), ''])
self.msg_cbbox.set_active(0)
self.popupid = self.msg_cbbox.connect('notify::popup-shown',
- self._first_msg_popdown)
+ self.first_msg_popdown)
self.msg_cbbox.connect('changed', self.changed_cb)
mbox.pack_start(self.msg_cbbox)
vbox.pack_start(mbox, False, False)
@@ -243,21 +243,21 @@ vbox.pack_start(frame)
self.text = gtk.TextView()
- self.text.connect('populate-popup', self._msg_add_to_popup)
+ self.text.connect('populate-popup', self.msg_add_to_popup)
self.text.modify_font(pango.FontDescription(self.fontcomment))
scroller.add(self.text)
- self._vpaned = gtk.VPaned()
- self._vpaned.add1(vbox)
- self._vpaned.add2(status_body)
+ self.vpaned = gtk.VPaned()
+ self.vpaned.add1(vbox)
+ self.vpaned.add2(status_body)
gobject.idle_add(self.realize_settings)
- return self._vpaned
+ return self.vpaned
def realize_settings(self):
- self._vpaned.set_position(self._setting_vpos)
+ self.vpaned.set_position(self._setting_vpos)
def thgaccept(self, window):
- self._commit_clicked(None)
+ self.commit_clicked(None)
def get_menu_info(self):
"""Returns menu info in this order: merge, addrem, unknown,
@@ -265,18 +265,18 @@ """
merge, addrem, unknown, clean, ignored, deleted, unresolved, resolved \
= GStatus.get_menu_info(self)
- return (merge + ((_('_commit'), self._commit_file),),
- addrem + ((_('_commit'), self._commit_file),),
- unknown + ((_('_commit'), self._commit_file),),
+ return (merge + ((_('_commit'), self.commit_file),),
+ addrem + ((_('_commit'), self.commit_file),),
+ unknown + ((_('_commit'), self.commit_file),),
clean,
ignored,
- deleted + ((_('_commit'), self._commit_file),),
+ deleted + ((_('_commit'), self.commit_file),),
unresolved,
resolved,
)
- def _delete(self, window, event):
+ def delete(self, window, event):
if not self.should_live():
self.destroy()
else:
@@ -293,7 +293,7 @@ res = dialog.run()
if res == gtk.RESPONSE_YES:
begin, end = buf.get_bounds()
- self._update_recent_messages(buf.get_text(begin, end))
+ self.update_recent_messages(buf.get_text(begin, end))
buf.set_modified(False)
elif res != gtk.RESPONSE_NO:
live = True
@@ -303,11 +303,11 @@
def reload_status(self):
- if not self._ready: return False
+ if not self.ready: return False
success = GStatus.reload_status(self)
- self._check_merge()
- self._check_patch_queue()
- self._check_undo()
+ self.check_merge()
+ self.check_patch_queue()
+ self.check_undo()
self.refresh_branchop()
return success
@@ -323,13 +323,13 @@ text = _('branch: ') + self.repo[None].branch()
self.branchbutton.set_label(text)
- def _check_undo(self):
+ def check_undo(self):
can_undo = os.path.exists(self.repo.sjoin("undo")) and \
- self._last_commit_id is not None
- self._undo_button.set_sensitive(can_undo)
+ self.last_commit_id is not None
+ self.undo_button.set_sensitive(can_undo)
- def _check_merge(self):
+ def check_merge(self):
self.get_toolbutton(_('Re_vert')).set_sensitive(not self.merging)
self.get_toolbutton(_('_Add')).set_sensitive(not self.merging)
self.get_toolbutton(_('_Remove')).set_sensitive(not self.merging)
@@ -340,7 +340,7 @@ for entry in self.filemodel:
if entry[FM_STATUS] in 'MARD':
entry[FM_CHECKED] = True
- self._update_check_count()
+ self.update_check_count()
# pre-fill commit message
buf = self.text.get_buffer()
@@ -348,7 +348,7 @@ buf.set_modified(False)
- def _check_patch_queue(self):
+ def check_patch_queue(self):
'''See if an MQ patch is applied, switch to qrefresh mode'''
self.qheader = None
if self.mqmode:
@@ -384,38 +384,36 @@ c_btn.set_tooltip(self.tooltips, _('commit'))
self.branchbutton.set_sensitive(not (self.mqmode or self.qnew))
- def _commit_clicked(self, toolbutton, data=None):
- if not self._ready_message():
+ def commit_clicked(self, toolbutton, data=None):
+ if not self.ready_message():
return
if self.merging:
# merges must be committed without specifying file list.
- self._hg_commit([])
+ self.hg_commit([])
else:
commitable = 'MAR'
- addremove_list = self._relevant_files('?!')
- if len(addremove_list) and self._should_addremove(addremove_list):
+ addremove_list = self.relevant_files('?!')
+ if len(addremove_list) and self.should_addremove(addremove_list):
commitable += '?!'
- commit_list = self._relevant_files(commitable)
+ commit_list = self.relevant_files(commitable)
if len(commit_list) > 0:
- self._commit_selected(commit_list)
+ self.commit_selected(commit_list)
elif len(self.filemodel) == 0 and self.qnew:
- self._commit_selected([])
+ self.commit_selected([])
else:
gdialog.Prompt(_('Nothing Commited'),
_('No committable files selected'), self).run()
return
self.reload_status()
shlib.update_thgstatus(self.ui, self.repo.root, wait=True)
- shlib.shell_notify([self.cwd] + self._relevant_files('MAR'))
+ files = [self.repo.wjoin(x) for x in self.relevant_files('MAR')]
+ shlib.shell_notify(files)
- def _commit_selected(self, files):
+ def commit_selected(self, files):
# 1a. get list of chunks not rejected
- repo, chunks, ui = self.repo, self._shelve_chunks, self.ui
- model = self.diff_model
- files = [util.pconvert(f) for f in files]
- hlist = [x[DM_CHUNK_ID] for x in model if not x[DM_REJECTED]]
+ repo, ui = self.repo, self.ui
# 2. backup changed files, so we can restore them in the end
backups = {}
@@ -424,28 +422,31 @@ os.mkdir(backupdir)
except OSError, err:
if err.errno != errno.EEXIST:
- gdialog.Prompt(_('Commit'), _('Unable to create ') + backupdir,
- self).run()
+ gdialog.Prompt(_('Commit'),
+ _('Unable to create ') + backupdir, self).run()
return
try:
# backup continues
+ allchunks = []
for f in files:
if f not in self.modified: continue
- fh = self._filechunks.get(f)
- if not fh or len(fh) < 2: continue
+ if f not in self.filechunks: continue
+ chunks = self.filechunks[f]
+ if len(chunks) < 2: continue
+
# unfiltered files do not go through backup-revert-patch cycle
- rejected = [x for x in fh[1:] if model[x][DM_REJECTED]]
+ rejected = [c for c in chunks[1:] if not c.active]
if len(rejected) == 0: continue
+ allchunks.extend(chunks)
fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
dir=backupdir)
os.close(fd)
- ui.debug(_('backup %r as %r\n') % (f, tmpname))
util.copyfile(repo.wjoin(f), tmpname)
backups[f] = tmpname
fp = cStringIO.StringIO()
- for n, c in enumerate(chunks):
- if c.filename() in backups and n in hlist:
+ for n, c in enumerate(allchunks):
+ if c.filename() in backups and c.active:
c.write(fp)
dopatch = fp.tell()
fp.seek(0)
@@ -461,8 +462,6 @@ # 3b. (apply)
if dopatch:
try:
- ui.debug(_('applying patch\n'))
- ui.debug(fp.getvalue())
pfiles = {}
patch.internalpatch(fp, ui, 1, repo.root, files=pfiles)
patch.updatedir(ui, repo, pfiles)
@@ -477,14 +476,14 @@ del fp
# 4. We prepared working directory according to filtered patch.
- # Now is the time to delegate the job to commit/qrefresh or the like!
-
+ # Now is the time to delegate the job to commit/qrefresh
+ # or the like!
# it is important to first chdir to repo root -- we'll call a
# highlevel command with list of pathnames relative to repo root
cwd = os.getcwd()
os.chdir(repo.root)
try:
- self._hg_commit(files)
+ self.hg_commit(files)
finally:
os.chdir(cwd)
@@ -493,7 +492,6 @@ # 5. finally restore backed-up files
try:
for realname, tmpname in backups.iteritems():
- ui.debug(_('restoring %r to %r\n') % (tmpname, realname))
util.copyfile(tmpname, repo.wjoin(realname))
os.unlink(tmpname)
os.rmdir(backupdir)
@@ -501,24 +499,24 @@ pass
- def _commit_file(self, stat, file):
- if self._ready_message():
- if stat not in '?!' or self._should_addremove([file]):
- self._hg_commit([file])
+ def commit_file(self, stat, file):
+ if self.ready_message():
+ if stat not in '?!' or self.should_addremove([file]):
+ self.hg_commit([file])
self.reload_status()
shlib.update_thgstatus(self.ui, self.repo.root, wait=True)
- shlib.shell_notify([file])
+ shlib.shell_notify([self.repo.wjoin(file)])
return True
- def _undo_clicked(self, toolbutton, data=None):
+ def undo_clicked(self, toolbutton, data=None):
response = gdialog.Confirm(_('Confirm Undo commit'),
[], self, _('Undo last commit')).run()
if response != gtk.RESPONSE_YES:
return
- tip = self._get_tip_rev(True)
- if not tip == self._last_commit_id:
+ tip = self.get_tip_rev(True)
+ if not tip == self.last_commit_id:
gdialog.Prompt(_('Undo commit'),
_('Unable to undo!\n\n'
'Tip revision differs from last commit.'),
@@ -527,14 +525,14 @@
try:
self.repo.rollback()
- self._last_commit_id = None
+ self.last_commit_id = None
self.reload_status()
except:
gdialog.Prompt(_('Undo commit'),
_('Errors during rollback!'), self).run()
- def _should_addremove(self, files):
+ def should_addremove(self, files):
if self.test_opt('addremove'):
return True
else:
@@ -548,7 +546,7 @@ return False
- def _ready_message(self):
+ def ready_message(self):
buf = self.text.get_buffer()
if buf.get_char_count() == 0:
gdialog.Prompt(_('Nothing Commited'),
@@ -563,7 +561,7 @@ gdialog.Prompt(_('Error'),
_('Message format configuration error'),
self).run()
- self._msg_config(None)
+ self.msg_config(None)
return
lines = buf.get_text(buf.get_start_iter(),
@@ -603,7 +601,7 @@ return True
- def _hg_commit(self, files):
+ def hg_commit(self, files):
if not self.repo.ui.config('ui', 'username'):
gdialog.Prompt(_('Commit: Invalid username'),
_('Your username has not been configured.\n\n'
@@ -659,7 +657,7 @@ cmdline.extend(['--date', self.opts['date']])
cmdline += ['--message', hglib.fromutf(self.opts['message'])]
if self.qnew:
- cmdline += [hglib.fromutf(self._get_qnew_name())]
+ cmdline += [hglib.fromutf(self.get_qnew_name())]
cmdline += [self.repo.wjoin(x) for x in files]
dialog = hgcmd.CmdDialog(cmdline, True)
dialog.set_transient_for(self)
@@ -671,9 +669,10 @@ self.closebranch = False
self.nextbranch = None
self.opts['check'] = True # recheck MAR after commit
+ self.filechunks = {} # do not keep chunks
buf = self.text.get_buffer()
if buf.get_modified():
- self._update_recent_messages(self.opts['message'])
+ self.update_recent_messages(self.opts['message'])
buf.set_modified(False)
if self.qnew:
self.qnew_name.set_text('')
@@ -682,31 +681,31 @@ self.qnew = False
elif self.qheader is None:
self.text.set_buffer(gtk.TextBuffer())
- self._last_commit_id = self._get_tip_rev(True)
+ self.last_commit_id = self.get_tip_rev(True)
if self.notify_func:
self.notify_func(self.notify_args)
- def _get_tip_rev(self, refresh=False):
+ def get_tip_rev(self, refresh=False):
if refresh:
self.repo.invalidate()
return self.repo['tip'].node()
- def _get_qnew_name(self):
+ def get_qnew_name(self):
return self.qnew_name and self.qnew_name.get_text().strip() or ''
- def _qnew_changed(self, element):
- qnew = bool(self._get_qnew_name())
+ def qnew_changed(self, element):
+ qnew = bool(self.get_qnew_name())
if self.qnew != qnew:
self.qnew = qnew
self.mode = qnew and 'status' or 'commit'
self.reload_status()
self.qnew_name.grab_focus() # set focus back
- def _msg_add_to_popup(self, textview, menu):
+ def msg_add_to_popup(self, textview, menu):
menu_items = (('----', None),
- (_('Paste _Filenames'), self._msg_paste_fnames),
- (_('App_ly Format'), self._msg_word_wrap),
- (_('C_onfigure Format'), self._msg_config))
+ (_('Paste _Filenames'), self.msg_paste_fnames),
+ (_('App_ly Format'), self.msg_word_wrap),
+ (_('C_onfigure Format'), self.msg_config))
for label, handler in menu_items:
if label == '----':
menuitem = gtk.SeparatorMenuItem()
@@ -717,14 +716,14 @@ menu.append(menuitem)
menu.show_all()
- def _msg_paste_fnames(self, sender):
+ def msg_paste_fnames(self, sender):
buf = self.text.get_buffer()
fnames = [ file[FM_PATH_UTF8] for file in self.filemodel
if file[FM_CHECKED] ]
buf.delete_selection(True, True)
buf.insert_at_cursor('\n'.join(fnames))
- def _msg_word_wrap(self, sender):
+ def msg_word_wrap(self, sender):
try:
sumlen = int(self.repo.ui.config('tortoisehg', 'summarylen', 0))
maxlen = int(self.repo.ui.config('tortoisehg', 'messagewrap', 0))
@@ -735,7 +734,7 @@ gdialog.Prompt(_('Info required'),
_('Message format needs to be configured'),
self).run()
- self._msg_config(None)
+ self.msg_config(None)
return
buf = self.text.get_buffer()
@@ -773,7 +772,7 @@ lnum += 1
buf.set_text('\n'.join(lines))
- def _msg_config(self, sender):
+ def msg_config(self, sender):
dlg = thgconfig.ConfigDialog(True)
dlg.show_all()
dlg.focus_field('tortoisehg.summarylen')
|
|
|
@@ -29,14 +29,44 @@ FM_MERGE_STATUS = 4
FM_PARTIAL_SELECTED = 5
-# diff_model row enumerations
-DM_REJECTED = 0
-DM_MARKUP = 1
-DM_TEXT = 2
-DM_DISPLAYED = 3
-DM_IS_HEADER = 4
-DM_CHUNK_ID = 5
-DM_FONT = 6
+# diffmodel row enumerations
+DM_REJECTED = 0
+DM_DISP_TEXT = 1
+DM_IS_HEADER = 2
+DM_PATH = 3
+DM_CHUNK_ID = 4
+DM_FONT = 5
+
+def hunk_markup(text):
+ 'Format a diff hunk for display in a TreeView row with markup'
+ hunk = ""
+ lines = text.splitlines()
+ for line in lines:
+ line = gobject.markup_escape_text(hglib.toutf(line[:128]))
+ if line[-1] != '\n':
+ line += '\n'
+ if line.startswith('---') or line.startswith('+++'):
+ hunk += '<span foreground="#000090">%s</span>' % line
+ elif line.startswith('-'):
+ hunk += '<span foreground="#900000">%s</span>' % line
+ elif line.startswith('+'):
+ hunk += '<span foreground="#006400">%s</span>' % line
+ elif line.startswith('@@'):
+ hunk = '<span foreground="#FF8000">%s</span>' % line
+ else:
+ hunk += line
+ return hunk
+
+def hunk_unmarkup(text):
+ 'Format a diff hunk for display in a TreeView row without markup'
+ hunk = ""
+ lines = text.splitlines()
+ for line in lines:
+ line = gobject.markup_escape_text(hglib.toutf(line[:128]))
+ if line[-1] != '\n':
+ line += '\n'
+ hunk += line
+ return hunk
class GStatus(gdialog.GDialog):
"""GTK+ based dialog for displaying repository status
@@ -56,12 +86,16 @@ def init(self):
gdialog.GDialog.init(self)
self.mode = 'status'
+ self.ready = True
+ self.last_file = None
+ self.filerowstart = {}
+ self.filechunks = {}
def auto_check(self):
if self.pats or self.opts.get('check'):
for entry in self.filemodel:
entry[FM_CHECKED] = True
- self._update_check_count()
+ self.update_check_count()
def get_menu_info(self):
"""Returns menu info in this order:
@@ -72,49 +106,49 @@ # merge
((_('_difference'), self._diff_file),
(_('edit'), self._view_file),
- (_('view other'), self._view_left_file),
- (_('_revert'), self._revert_file),
- (_('l_og'), self._log_file)),
+ (_('view other'), self.view_left_file),
+ (_('_revert'), self.revert_file),
+ (_('l_og'), self.log_file)),
# addrem
((_('_difference'), self._diff_file),
(_('_view'), self._view_file),
- (_('_revert'), self._revert_file),
- (_('l_og'), self._log_file)),
+ (_('_revert'), self.revert_file),
+ (_('l_og'), self.log_file)),
# unknown
((_('_view'), self._view_file),
- (_('_delete'), self._delete_file),
- (_('_add'), self._add_file),
- (_('_guess rename'), self._guess_rename),
- (_('_ignore'), self._ignore_file)),
+ (_('_delete'), self.delete_file),
+ (_('_add'), self.add_file),
+ (_('_guess rename'), self.guess_rename),
+ (_('_ignore'), self.ignore_file)),
# clean
((_('_view'), self._view_file),
- (_('re_move'), self._remove_file),
- (_('re_name'), self._rename_file),
- (_('_copy'), self._copy_file),
- (_('l_og'), self._log_file)),
+ (_('re_move'), self.remove_file),
+ (_('re_name'), self.rename_file),
+ (_('_copy'), self.copy_file),
+ (_('l_og'), self.log_file)),
# ignored
((_('_view'), self._view_file),
- (_('_delete'), self._delete_file)),
+ (_('_delete'), self.delete_file)),
# deleted
((_('_view'), self._view_file),
- (_('_revert'), self._revert_file),
- (_('re_move'), self._remove_file),
- (_('l_og'), self._log_file)),
+ (_('_revert'), self.revert_file),
+ (_('re_move'), self.remove_file),
+ (_('l_og'), self.log_file)),
# unresolved
((_('_difference'), self._diff_file),
(_('edit'), self._view_file),
- (_('view other'), self._view_left_file),
- (_('_revert'), self._revert_file),
- (_('l_og'), self._log_file),
- (_('resolve'), self._do_resolve),
- (_('mark resolved'), self._mark_resolved)),
+ (_('view other'), self.view_left_file),
+ (_('_revert'), self.revert_file),
+ (_('l_og'), self.log_file),
+ (_('resolve'), self.do_resolve),
+ (_('mark resolved'), self.mark_resolved)),
# resolved
((_('_difference'), self._diff_file),
(_('edit'), self._view_file),
- (_('view other'), self._view_left_file),
- (_('_revert'), self._revert_file),
- (_('l_og'), self._log_file),
- (_('mark unresolved'), self._unmark_resolved)),
+ (_('view other'), self.view_left_file),
+ (_('_revert'), self.revert_file),
+ (_('l_og'), self.log_file),
+ (_('mark unresolved'), self.unmark_resolved)),
)
### End of overridable methods ###
@@ -123,7 +157,7 @@ ### Overrides of base class methods ###
def parse_opts(self):
- self._ready = False
+ self.ready = False
# Determine which files to display
if self.test_opt('all'):
@@ -161,35 +195,25 @@
def get_tbbuttons(self):
tbuttons = [self.make_toolbutton(gtk.STOCK_REFRESH, _('Re_fresh'),
- self._refresh_clicked, tip=_('refresh')),
+ self.refresh_clicked, tip=_('refresh')),
gtk.SeparatorToolItem()]
if self.count_revs() == 2:
tbuttons += [
self.make_toolbutton(gtk.STOCK_SAVE_AS, _('Save As'),
- self._save_clicked, tip=_('Save selected changes'))]
+ self.save_clicked, tip=_('Save selected changes'))]
else:
tbuttons += [
self.make_toolbutton(gtk.STOCK_MEDIA_REWIND, _('Re_vert'),
- self._revert_clicked, tip=_('revert')),
+ self.revert_clicked, tip=_('revert')),
self.make_toolbutton(gtk.STOCK_ADD, _('_Add'),
- self._add_clicked, tip=_('add')),
+ self.add_clicked, tip=_('add')),
self.make_toolbutton(gtk.STOCK_JUMP_TO, _('Move'),
- self._move_clicked,
+ self.move_clicked,
tip=_('move selected files to other directory')),
self.make_toolbutton(gtk.STOCK_DELETE, _('_Remove'),
- self._remove_clicked, tip=_('remove')),
+ self.remove_clicked, tip=_('remove')),
gtk.SeparatorToolItem()]
-
- self.showdiff_toggle = gtk.ToggleToolButton(gtk.STOCK_JUSTIFY_FILL)
- self.showdiff_toggle.set_use_underline(True)
- self.showdiff_toggle.set_label(_('_Show Diff'))
- self.showdiff_toggle.set_tooltip(self.tooltips, _('show diff pane'))
- self.showdiff_toggle.set_active(False)
- self._showdiff_toggled_id = self.showdiff_toggle.connect('toggled',
- self._showdiff_toggled )
- tbuttons.append(self.showdiff_toggle)
-
return tbuttons
@@ -217,8 +241,6 @@ def get_body(self):
self.merging = len(self.repo.parents()) == 2
- self.connect('map-event', self._displayed)
-
# TODO: should generate menus dynamically during right-click, currently
# there can be entires that are not always supported or relavant.
merge, addrem, unknown, clean, ignored, deleted, unresolved, resolved \
@@ -246,15 +268,15 @@
# model stores the file list.
self.filemodel = gtk.ListStore(bool, str, str, str, str, bool)
- self.filemodel.set_sort_func(1001, self._sort_by_stat)
- self.filemodel.set_default_sort_func(self._sort_by_stat)
+ self.filemodel.set_sort_func(1001, self.sort_by_stat)
+ self.filemodel.set_default_sort_func(self.sort_by_stat)
self.filetree = gtk.TreeView(self.filemodel)
- self.filetree.connect('button-press-event', self._tree_button_press)
- self.filetree.connect('button-release-event', self._tree_button_release)
- self.filetree.connect('popup-menu', self._tree_popup_menu)
- self.filetree.connect('row-activated', self._tree_row_act)
- self.filetree.connect('key-press-event', self._tree_key_press)
+ self.filetree.connect('button-press-event', self.tree_button_press)
+ self.filetree.connect('button-release-event', self.tree_button_release)
+ self.filetree.connect('popup-menu', self.tree_popup_menu)
+ self.filetree.connect('row-activated', self.tree_row_act)
+ self.filetree.connect('key-press-event', self.tree_key_press)
self.filetree.set_reorderable(False)
self.filetree.set_enable_search(True)
self.filetree.set_search_equal_func(self.search_filelist)
@@ -273,7 +295,7 @@ self.connect('thg-refresh', self.thgrefresh)
toggle_cell = gtk.CellRendererToggle()
- toggle_cell.connect('toggled', self._select_toggle)
+ toggle_cell.connect('toggled', self.select_toggle)
toggle_cell.set_property('activatable', True)
path_cell = gtk.CellRendererText()
@@ -288,11 +310,11 @@ col0.add_attribute(toggle_cell, 'radio', FM_PARTIAL_SELECTED)
col0.set_resizable(False)
self.filetree.append_column(col0)
- self.selcb = self._add_header_checkbox(col0, self._sel_clicked)
+ self.selcb = self.add_header_checkbox(col0, self.sel_clicked)
col1 = gtk.TreeViewColumn(_('st'), stat_cell)
col1.add_attribute(stat_cell, 'text', FM_STATUS)
- col1.set_cell_data_func(stat_cell, self._text_color)
+ col1.set_cell_data_func(stat_cell, self.text_color)
col1.set_sort_column_id(1001)
col1.set_resizable(False)
self.filetree.append_column(col1)
@@ -306,7 +328,7 @@
col2 = gtk.TreeViewColumn(_('path'), path_cell)
col2.add_attribute(path_cell, 'text', FM_PATH_UTF8)
- col2.set_cell_data_func(path_cell, self._text_color)
+ col2.set_cell_data_func(path_cell, self.text_color)
col2.set_sort_column_id(2)
col2.set_resizable(True)
self.filetree.append_column(col2)
@@ -334,7 +356,7 @@ self.merge_diff_text.modify_font(self.difffont)
self.filetree.get_selection().set_mode(gtk.SELECTION_SINGLE)
self.filetree.get_selection().connect('changed',
- self.merge_sel_changed, False)
+ self.merge_sel_changed)
scroller.add(self.merge_diff_text)
diff_frame.add(scroller)
else:
@@ -342,29 +364,33 @@ sel = (os.name == 'nt') and 'CLIPBOARD' or 'PRIMARY'
self.clipboard = gtk.Clipboard(selection=sel)
- self.diff_model = gtk.ListStore(bool, str, str, str, bool, int,
+ self.diffmodel = gtk.ListStore(
+ bool, # DM_REJECTED
+ str, # DM_DISP_TEXT
+ bool, # DM_IS_HEADER
+ str, # DM_PATH
+ int, # DM_CHUNK_ID
pango.FontDescription)
- self.diff_tree = gtk.TreeView(self.diff_model)
+ difftree = gtk.TreeView(self.diffmodel)
# set CTRL-c accelerator for copy-clipboard
mod = gtklib.get_thg_modifier()
key, modifier = gtk.accelerator_parse(mod+'c')
- self.diff_tree.add_accelerator('copy-clipboard', accelgroup, key,
+ difftree.add_accelerator('copy-clipboard', accelgroup, key,
modifier, gtk.ACCEL_VISIBLE)
- self.diff_tree.connect('copy-clipboard', self.copy_to_clipboard)
+ difftree.connect('copy-clipboard', self.copy_to_clipboard)
- self.diff_tree.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
- self.diff_tree.set_headers_visible(False)
- self.diff_tree.set_enable_search(False)
- self.diff_tree.set_property('enable-grid-lines', True)
- self.diff_tree.connect('row-activated',
- self._diff_tree_row_act)
+ difftree.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
+ difftree.set_headers_visible(False)
+ difftree.set_enable_search(False)
+ difftree.set_property('enable-grid-lines', True)
+ difftree.connect('row-activated', self.diff_tree_row_act)
cell = gtk.CellRendererText()
diffcol = gtk.TreeViewColumn('diff', cell)
diffcol.set_resizable(True)
- diffcol.add_attribute(cell, 'markup', DM_DISPLAYED)
+ diffcol.add_attribute(cell, 'markup', DM_DISP_TEXT)
# differentiate header chunks
cell.set_property('cell-background', '#DDDDDD')
@@ -381,11 +407,11 @@ diffcol.add_attribute(cell, 'background-set', DM_REJECTED)
diffcol.add_attribute(cell, 'foreground-set', DM_REJECTED)
- self.diff_tree.append_column(diffcol)
+ difftree.append_column(diffcol)
self.filetree.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
self.filetree.get_selection().connect('changed',
- self.tree_sel_changed, False)
- scroller.add(self.diff_tree)
+ self.tree_sel_changed)
+ scroller.add(difftree)
diff_frame.add(scroller)
if self.diffbottom:
@@ -395,12 +421,86 @@
self._diffpane.pack1(tree_frame, True, False)
self._diffpane.pack2(diff_frame, True, True)
- self._diffpane_moved_id = self._diffpane.connect('notify::position',
- self._diffpane_moved)
self.filetree.set_headers_clickable(True)
gobject.idle_add(self.realize_status_settings)
return self._diffpane
+
+ def get_extras(self):
+ table = gtk.Table(rows=2, columns=3)
+ table.set_col_spacings(8)
+
+ self._show_checks = {}
+ row, col = 0, 0
+ # Tuple: (ctype, translated label)
+ checks = (('modified', _('modified')),
+ ('added', _('added')),
+ ('removed', _('removed')))
+ if self.count_revs() <= 1:
+ checks += (('deleted', _('deleted')),
+ ('unknown', _('unknown')),
+ ('close', _('clean')),
+ ('ignored', _('ignored')))
+
+ for ctuple in checks:
+ check = gtk.CheckButton(ctuple[1])
+ check.connect('toggled', self.show_toggle, ctuple[0])
+ table.attach(check, col, col+1, row, row+1)
+ self._show_checks[ctuple[0]] = check
+ col += row
+ row = not row
+
+ self.counter = gtk.Label('')
+ self.counter.set_alignment(1.0, 0.0) # right up
+
+ hbox = gtk.HBox()
+ hbox.pack_start(table, expand=False)
+ hbox.pack_end(self.counter, expand=True, padding=2)
+
+ return hbox
+
+ def add_header_checkbox(self, col, post=None, pre=None, toggle=False):
+ def cbclick(hdr, cb):
+ state = cb.get_active()
+ if pre:
+ pre(state)
+ if toggle:
+ cb.set_active(not state)
+ if post:
+ post(not state)
+
+ cb = gtk.CheckButton(col.get_title())
+ cb.show()
+ col.set_widget(cb)
+ wgt = cb.get_parent()
+ while wgt:
+ if type(wgt) == gtk.Button:
+ wgt.connect('clicked', cbclick, cb)
+ return cb
+ wgt = wgt.get_parent()
+ return
+
+ def update_check_count(self):
+ file_count = 0
+ check_count = 0
+ for row in self.filemodel:
+ file_count = file_count + 1
+ if row[FM_CHECKED]:
+ check_count = check_count + 1
+ self.counter.set_text(_('%d selected, %d total') % (check_count,
+ file_count))
+ if self.selcb:
+ self.selcb.set_active(file_count and file_count == check_count)
+
+ def prepare_display(self):
+ self.ready = True
+ # If the status load failed, no reason to continue
+ if not self.reload_status():
+ raise util.Abort('could not load status')
+
+
+ ### End of overrides ###
+
def realize_status_settings(self):
self._diffpane.set_position(self._setting_pos)
@@ -426,108 +526,25 @@ w = self.get_focus()
w.emit('copy-clipboard')
return False
+ saves = {}
model, tpaths = treeview.get_selection().get_selected_rows()
- cids = [ model[row][DM_CHUNK_ID] for row, in tpaths ]
- headers = {}
+ for row, _ in tpaths:
+ wfile, cid = model[row][DM_PATH], model[row][DM_CHUNK_ID]
+ if wfile not in saves:
+ saves[wfile] = [cid]
+ else:
+ saves[wfile].append(cid)
fp = cStringIO.StringIO()
- for cid in cids:
- chunk = self._shelve_chunks[cid]
- wfile = chunk.filename()
- if not isinstance(chunk, hgshelve.header):
- # Ensure each hunk has a file header
- if wfile not in headers:
- hrow = self._filechunks[wfile][0]
- hcid = model[hrow][DM_CHUNK_ID]
- self._shelve_chunks[hcid].write(fp)
- headers[wfile] = cid
- chunk.write(fp)
+ for wfile in saves.keys():
+ chunks = self.filechunks[wfile]
+ chunks[0].write(fp)
+ for cid in saves[wfile]:
+ if cid != 0:
+ chunks[cid].write(fp)
fp.seek(0)
self.clipboard.set_text(fp.read())
- def get_extras(self):
- table = gtk.Table(rows=2, columns=3)
- table.set_col_spacings(8)
-
- self._show_checks = {}
- row, col = 0, 0
- # Tuple: (ctype, translated label)
- checks = (('modified', _('modified')),
- ('added', _('added')),
- ('removed', _('removed')))
- if self.count_revs() <= 1:
- checks += (('deleted', _('deleted')),
- ('unknown', _('unknown')),
- ('close', _('clean')),
- ('ignored', _('ignored')))
-
- for ctuple in checks:
- check = gtk.CheckButton(ctuple[1])
- check.connect('toggled', self._show_toggle, ctuple[0])
- table.attach(check, col, col+1, row, row+1)
- self._show_checks[ctuple[0]] = check
- col += row
- row = not row
-
- self.counter = gtk.Label('')
- self.counter.set_alignment(1.0, 0.0) # right up
-
- hbox = gtk.HBox()
- hbox.pack_start(table, expand=False)
- hbox.pack_end(self.counter, expand=True, padding=2)
-
- return hbox
-
- def _add_header_checkbox(self, col, post=None, pre=None, toggle=False):
- def cbclick(hdr, cb):
- state = cb.get_active()
- if pre:
- pre(state)
- if toggle:
- cb.set_active(not state)
- if post:
- post(not state)
-
- cb = gtk.CheckButton(col.get_title())
- cb.show()
- col.set_widget(cb)
- wgt = cb.get_parent()
- while wgt:
- if type(wgt) == gtk.Button:
- wgt.connect('clicked', cbclick, cb)
- return cb
- wgt = wgt.get_parent()
- return
-
-
- def _update_check_count(self):
- file_count = 0
- check_count = 0
- for row in self.filemodel:
- file_count = file_count + 1
- if row[FM_CHECKED]:
- check_count = check_count + 1
- self.counter.set_text(_('%d selected, %d total') % (check_count,
- file_count))
- if self.selcb:
- self.selcb.set_active(file_count and file_count == check_count)
-
- def prepare_display(self):
- self._ready = True
- self._last_file = None
- self._shelve_chunks = []
- self._filechunks = {}
- # If the status load failed, no reason to continue
- if not self.reload_status():
- raise util.Abort('could not load status')
-
-
- def _displayed(self, widget, event):
- self._diffpane_moved(self._diffpane)
- return False
-
- ### End of overrides ###
-
- def _do_reload_status(self):
+ def do_reload_status(self):
"""Clear out the existing ListStore model and reload it from the
repository status. Also recheck and reselect files that remain
in the list.
@@ -595,16 +612,15 @@ if row[FM_PATH] in reselect:
selection.select_iter(row.iter)
selected = True
-
if not selected:
selection.select_path((0,))
- files = [row[FM_PATH] for row in self.filemodel]
- self._show_diff_hunks(files)
-
# clear buffer after a merge commit
- if not files and self.merging:
- self.merge_diff_text.set_buffer(gtk.TextBuffer())
+ if not len(self.filemodel):
+ if self.merging:
+ self.merge_diff_text.set_buffer(gtk.TextBuffer())
+ else:
+ self.diffmodel.clear()
self.filetree.show()
if self.mode == 'commit':
@@ -615,73 +631,92 @@
def reload_status(self):
- if not self._ready: return False
- self._last_file = None
- res, outtext = self._hg_call_wrapper('Status', self._do_reload_status)
+ if not self.ready: return False
+ self.last_file = None
+ res, outtext = self._hg_call_wrapper('Status', self.do_reload_status)
self.auto_check()
- self._update_check_count()
+ self.update_check_count()
return res
def make_menu(self, entries):
menu = gtk.Menu()
for entry in entries:
- menu.append(self._make_menuitem(entry[0], entry[1]))
+ menu.append(self.make_menuitem(entry[0], entry[1]))
menu.show_all()
return menu
- def _make_menuitem(self, label, handler):
+ def make_menuitem(self, label, handler):
menuitem = gtk.MenuItem(label, True)
- menuitem.connect('activate', self._context_menu_act, handler)
+ menuitem.connect('activate', self.context_menu_act, handler)
menuitem.set_border_width(1)
return menuitem
- def _select_toggle(self, cellrenderer, path):
- '''User manually toggled file status'''
+ def select_toggle(self, cellrenderer, path):
+ 'User manually toggled file status via checkbox'
self.filemodel[path][FM_CHECKED] = not self.filemodel[path][FM_CHECKED]
- self._update_chunk_state(self.filemodel[path])
- self._update_check_count()
+ self.update_chunk_state(self.filemodel[path])
+ self.update_check_count()
return True
- def _update_chunk_state(self, entry):
- '''Update chunk toggle state to match file toggle state'''
- wfile = util.pconvert(entry[FM_PATH])
- if wfile not in self._filechunks: return
- selected = entry[FM_CHECKED]
- for n in self._filechunks[wfile][1:]:
- self.diff_model[n][DM_REJECTED] = not selected
- self._update_diff_hunk(self.diff_model[n])
- entry[FM_PARTIAL_SELECTED] = False
- self._update_diff_header(self.diff_model, wfile, selected)
+ def update_chunk_state(self, fileentry):
+ 'Update chunk toggle state to match file toggle state'
+ fileentry[FM_PARTIAL_SELECTED] = False
+ wfile = fileentry[FM_PATH]
+ selected = fileentry[FM_CHECKED]
+ chunks = self.filechunks[wfile]
+ for chunk in chunks:
+ chunk.active = selected
+ # this file's chunks may not be in diffmodel
+ if wfile not in self.filerowstart:
+ return
+ rowstart = self.filerowstart[wfile]
+ for n, chunk in enumerate(chunks):
+ if n == 0:
+ continue
+ self.diffmodel[rowstart+n][DM_REJECTED] = not selected
+ self.update_diff_hunk(self.diffmodel[rowstart+n])
+ self.update_diff_header(self.diffmodel, wfile, selected)
- def _update_diff_hunk(self, row):
- if row[DM_REJECTED]:
+ def update_diff_hunk(self, row):
+ 'Update the contents of a diff row based on its chunk state'
+ wfile = row[DM_PATH]
+ chunks = self.filechunks[wfile]
+ chunk = chunks[row[DM_CHUNK_ID]]
+ buf = cStringIO.StringIO()
+ chunk.pretty(buf)
+ buf.seek(0)
+ if chunk.active:
+ row[DM_REJECTED] = False
+ row[DM_FONT] = self.difffont
+ row[DM_DISP_TEXT] = hunk_markup(buf.read())
+ else:
+ row[DM_REJECTED] = True
row[DM_FONT] = self.rejfont
- row[DM_DISPLAYED] = row[DM_TEXT]
- else:
- row[DM_FONT] = self.difffont
- row[DM_DISPLAYED] = row[DM_MARKUP]
+ row[DM_DISP_TEXT] = hunk_unmarkup(buf.read())
- def _update_diff_header(self, dmodel, wfile, selected):
- fc = self._filechunks[wfile]
- hc = fc[0]
- lasthunk = len(fc)-1
- row = dmodel[hc]
+ def update_diff_header(self, dmodel, wfile, selected):
+ try:
+ hc = self.filerowstart[wfile]
+ chunks = self.filechunks[wfile]
+ except IndexError:
+ return
+ lasthunk = len(chunks)-1
sel = lambda x: x >= lasthunk or not dmodel[hc+x+1][DM_REJECTED]
- newtext = self._shelve_chunks[row[DM_CHUNK_ID]].selpretty(sel)
+ newtext = chunks[0].selpretty(sel)
if not selected:
newtext = "<span foreground='#888888'>" + newtext + "</span>"
- row[DM_DISPLAYED] = newtext
+ dmodel[hc][DM_DISP_TEXT] = newtext
- def _show_toggle(self, check, toggletype):
+ def show_toggle(self, check, toggletype):
self.opts[toggletype] = check.get_active()
self.reload_status()
return True
- def _sort_by_stat(self, model, iter1, iter2):
+ def sort_by_stat(self, model, iter1, iter2):
order = 'MAR!?IC'
lhs, rhs = (model.get_value(iter1, FM_STATUS),
model.get_value(iter2, FM_STATUS))
@@ -689,14 +724,14 @@ # values to be None. When this happens, just return any value
# since the call is irrelevant and will be followed by another
# with the correct (non-None) value
- if None in (lhs, rhs) :
+ if None in (lhs, rhs):
return 0
result = order.find(lhs) - order.find(rhs)
return min(max(result, -1), 1)
- def _text_color(self, column, text_renderer, model, row_iter):
+ def text_color(self, column, text_renderer, model, row_iter):
stat = model[row_iter][FM_STATUS]
if stat == 'M':
text_renderer.set_property('foreground', '#000090')
@@ -716,24 +751,24 @@ text_renderer.set_property('foreground', 'black')
- def _view_left_file(self, stat, wfile):
+ def view_left_file(self, stat, wfile):
return self._view_file(stat, wfile, True)
- def _remove_file(self, stat, wfile):
- self._hg_remove([wfile])
+ def remove_file(self, stat, wfile):
+ self.hg_remove([wfile])
return True
- def _rename_file(self, stat, wfile):
+ def rename_file(self, stat, wfile):
fdir, fname = os.path.split(wfile)
newfile = dialog.entry_dialog(self, _('Rename file to:'), True, fname)
if newfile and newfile != fname:
- self._hg_move([wfile, os.path.join(fdir, newfile)])
+ self.hg_move([wfile, os.path.join(fdir, newfile)])
return True
- def _copy_file(self, stat, wfile):
+ def copy_file(self, stat, wfile):
wfile = self.repo.wjoin(wfile)
fdir, fname = os.path.split(wfile)
dlg = gtk.FileChooserDialog(parent=self,
@@ -750,11 +785,11 @@ newfile = dlg.get_filename()
dlg.destroy()
if newfile != wfile:
- self._hg_copy([wfile, newfile])
+ self.hg_copy([wfile, newfile])
return True
- def _hg_remove(self, files):
+ def hg_remove(self, files):
wfiles = [self.repo.wjoin(x) for x in files]
if self.count_revs() > 1:
gdialog.Prompt(_('Nothing Removed'),
@@ -773,7 +808,7 @@ self.reload_status()
- def _hg_move(self, files):
+ def hg_move(self, files):
wfiles = [self.repo.wjoin(x) for x in files]
if self.count_revs() > 1:
gdialog.Prompt(_('Nothing Moved'), _('Move is not enabled when '
@@ -792,7 +827,7 @@ self.reload_status()
- def _hg_copy(self, files):
+ def hg_copy(self, files):
wfiles = [self.repo.wjoin(x) for x in files]
if self.count_revs() > 1:
gdialog.Prompt(_('Nothing Copied'), _('Copy is not enabled when '
@@ -809,97 +844,178 @@ shlib.update_thgstatus(self.ui, self.repo.root, wait=True)
self.reload_status()
- def merge_sel_changed(self, selection, force):
- ''' Update the diff text with merge diff to both parents'''
- def dohgdiff():
- difftext = [_('===== Diff to first parent =====\n')]
+ def merge_sel_changed(self, selection):
+ 'Selected row in file tree activated changed (merge mode)'
+ # Update the diff text with merge diff to both parents
+ model, paths = selection.get_selected_rows()
+ if not paths:
+ self.last_file = None
+ return
+ wfile = self.filemodel[paths[0]][FM_PATH]
+ if wfile == self.last_file:
+ return
+ self.last_file = wfile
+ difftext = [_('===== Diff to first parent =====\n')]
+ wfiles = [self.repo.wjoin(wfile)]
+ wctx = self.repo[None]
+ matcher = cmdutil.match(self.repo, wfiles, self.opts)
+ for s in patch.diff(self.repo, wctx.p1().node(), None,
+ match=matcher, opts=patch.diffopts(self.ui, self.opts)):
+ difftext.extend(s.splitlines(True))
+ difftext.append(_('\n===== Diff to second parent =====\n'))
+ for s in patch.diff(self.repo, wctx.p2().node(), None,
+ match=matcher, opts=patch.diffopts(self.ui, self.opts)):
+ difftext.extend(s.splitlines(True))
+
+ buf = gtk.TextBuffer()
+ buf.create_tag('removed', foreground='#900000')
+ buf.create_tag('added', foreground='#006400')
+ buf.create_tag('position', foreground='#FF8000')
+ buf.create_tag('header', foreground='#000090')
+
+ bufiter = buf.get_start_iter()
+ for line in difftext:
+ line = hglib.toutf(line)
+ if line.startswith('---') or line.startswith('+++'):
+ buf.insert_with_tags_by_name(bufiter, line, 'header')
+ elif line.startswith('-'):
+ line = hglib.diffexpand(line)
+ buf.insert_with_tags_by_name(bufiter, line, 'removed')
+ elif line.startswith('+'):
+ line = hglib.diffexpand(line)
+ buf.insert_with_tags_by_name(bufiter, line, 'added')
+ elif line.startswith('@@'):
+ buf.insert_with_tags_by_name(bufiter, line, 'position')
+ else:
+ line = hglib.diffexpand(line)
+ buf.insert(bufiter, line)
+ self.merge_diff_text.set_buffer(buf)
+
+
+ def tree_sel_changed(self, selection):
+ 'Selected row in file tree activated changed'
+ # Read this file's diffs into diff model
+ model, paths = selection.get_selected_rows()
+ if not paths:
+ self.last_file = None
+ return
+ wfile = self.filemodel[paths[0]][FM_PATH]
+ if wfile == self.last_file:
+ return
+ # TODO: this could be a for-loop
+ self.last_file = wfile
+ self.filerowstart = {}
+ self.diffmodel.clear()
+ self.append_diff_hunks(wfile)
+
+ def read_file_chunks(self, wfile):
+ 'Get diffs of working file, parse into (c)hunks'
+ difftext = cStringIO.StringIO()
+ ctx = self.repo[self._node1]
+ fctx = ctx.filectx(wfile)
+ if fctx and fctx.size() > hglib.getmaxdiffsize(self.ui):
+ # Fake patch that displays size warning
+ lines = ['diff --git -r a/%s b/%s\n' % (wfile, wfile)]
+ lines.append('--- a/%s\n' % wfile)
+ lines.append('+++ b/%s\n' % wfile)
+ lines.append(_('File is larger than the specified max diff size\n'))
+ difftext.writelines(lines)
+ else:
wfiles = [self.repo.wjoin(wfile)]
- wctx = self.repo[None]
matcher = cmdutil.match(self.repo, wfiles, self.opts)
- for s in patch.diff(self.repo, wctx.p1().node(), None,
- match=matcher, opts=patch.diffopts(self.ui, self.opts)):
- difftext.extend(s.splitlines(True))
- difftext.append(_('\n===== Diff to second parent =====\n'))
- for s in patch.diff(self.repo, wctx.p2().node(), None,
- match=matcher, opts=patch.diffopts(self.ui, self.opts)):
- difftext.extend(s.splitlines(True))
+ diffopts = mdiff.diffopts(git=True, nodates=True)
+ for s in patch.diff(self.repo, self._node1, self._node2,
+ match=matcher, opts=diffopts):
+ difftext.writelines(s.splitlines(True))
+ difftext.seek(0)
+ return hgshelve.parsepatch(difftext)
- buf = gtk.TextBuffer()
- buf.create_tag('removed', foreground='#900000')
- buf.create_tag('added', foreground='#006400')
- buf.create_tag('position', foreground='#FF8000')
- buf.create_tag('header', foreground='#000090')
+ def append_diff_hunks(self, wfile):
+ 'Append diff hunks of one file to the diffmodel'
+ chunks = self.read_file_chunks(wfile)
+ rows = []
+ for n, chunk in enumerate(chunks):
+ chunk.active = True
+ if isinstance(chunk, hgshelve.header):
+ rows.append([False, '', True, wfile, n, self.headerfont])
+ if chunk.special():
+ chunks = chunks[:1]
+ break
+ else:
+ rows.append([False, '', False, wfile, n, self.difffont])
- bufiter = buf.get_start_iter()
- for line in difftext:
- line = hglib.toutf(line)
- if line.startswith('---') or line.startswith('+++'):
- buf.insert_with_tags_by_name(bufiter, line, 'header')
- elif line.startswith('-'):
- line = hglib.diffexpand(line)
- buf.insert_with_tags_by_name(bufiter, line, 'removed')
- elif line.startswith('+'):
- line = hglib.diffexpand(line)
- buf.insert_with_tags_by_name(bufiter, line, 'added')
- elif line.startswith('@@'):
- buf.insert_with_tags_by_name(bufiter, line, 'position')
+
+ # recover old chunk selection/rejection states, match fromline
+ if wfile in self.filechunks:
+ ochunks = self.filechunks[wfile]
+ chunks[0].active = ochunks[0].active
+ next = 1
+ for oc in ochunks[1:]:
+ for n in xrange(next, len(chunks)):
+ nc = chunks[n]
+ if oc.fromline == nc.fromline:
+ nc.active = oc.active
+ next = n+1
+ break
+ elif nc.fromline > oc.fromline:
+ break
+
+ self.filerowstart[wfile] = len(self.diffmodel)
+ self.filechunks[wfile] = chunks
+
+ # Set row status based on chunk state
+ rej, nonrej = False, False
+ for n, row in enumerate(rows):
+ if row[DM_IS_HEADER]:
+ row[DM_REJECTED] = not chunks[n].active
+ else:
+ if chunks[n].active:
+ nonrej = True
else:
- line = hglib.diffexpand(line)
- buf.insert(bufiter, line)
+ rej = True
+ row[DM_REJECTED] = not chunks[n].active
+ self.update_diff_hunk(row)
+ self.diffmodel.append(row)
- self.merge_diff_text.set_buffer(buf)
+ newvalue = nonrej
+ partial = rej and nonrej
+ for fr in self.filemodel:
+ if fr[FM_PATH] == wfile:
+ break
+ if fr[FM_PARTIAL_SELECTED] != partial:
+ fr[FM_PARTIAL_SELECTED] = partial
+ if fr[FM_CHECKED] != newvalue:
+ fr[FM_CHECKED] = newvalue
+ self.update_check_count()
+ self.update_diff_header(self.diffmodel, wfile, newvalue)
- if self.showdiff_toggle.get_active():
- sel = self.filetree.get_selection().get_selected_rows()[1]
- if not sel:
- self._last_file = None
- return False
- wfile = self.filemodel[sel[0]][FM_PATH]
- if force or wfile != self._last_file:
- self._last_file = wfile
- self._hg_call_wrapper('Diff', dohgdiff)
- return False
-
- def tree_sel_changed(self, selection, force):
- if self.showdiff_toggle.get_active():
- sel = self.filetree.get_selection().get_selected_rows()[1]
- if not sel:
- self._last_file = None
- return False
- wfile = util.pconvert(self.filemodel[sel[0]][FM_PATH])
- if force or wfile != self._last_file:
- self._last_file = wfile
- if wfile in self._filechunks:
- row = self._filechunks[wfile][0]
- self.diff_tree.scroll_to_cell((row, ), None, True)
- selection = self.diff_tree.get_selection()
- selection.unselect_all()
- selection.select_path((row,))
- return False
-
- def _diff_tree_row_act(self, dtree, path, column):
+ def diff_tree_row_act(self, dtree, path, column):
+ 'Row in diff tree (hunk) activated/toggled'
dmodel = dtree.get_model()
row = dmodel[path]
- chunk = self._shelve_chunks[row[DM_CHUNK_ID]]
- wfile = chunk.filename()
- if wfile not in self._filechunks:
- return
+ wfile = row[DM_PATH]
+ try:
+ startrow = self.filerowstart[wfile]
+ chunks = self.filechunks[wfile]
+ except IndexError:
+ pass
+ chunkrows = xrange(startrow+1, startrow+len(chunks))
for fr in self.filemodel:
- if util.pconvert(fr[FM_PATH]) == wfile:
+ if fr[FM_PATH] == wfile:
break
- fchunks = self._filechunks[wfile][1:]
if row[DM_IS_HEADER]:
- for n in fchunks:
- dmodel[n][DM_REJECTED] = fr[FM_CHECKED]
- self._update_diff_hunk(dmodel[n])
+ for chunk in chunks[1:]:
+ chunk.active = fr[FM_CHECKED]
+ self.update_diff_hunk(dmodel[startrow+n])
newvalue = not fr[FM_CHECKED]
partial = False
else:
- row[DM_REJECTED] = not row[DM_REJECTED]
- self._update_diff_hunk(row)
- rej = [ n for n in fchunks if dmodel[n][DM_REJECTED] ]
- nonrej = [ n for n in fchunks if not dmodel[n][DM_REJECTED] ]
+ chunk = chunks[row[DM_CHUNK_ID]]
+ chunk.active = not chunk.active
+ self.update_diff_hunk(row)
+ rej = [ n for n in chunkrows if dmodel[n][DM_REJECTED] ]
+ nonrej = [ n for n in chunkrows if not dmodel[n][DM_REJECTED] ]
newvalue = nonrej and True or False
partial = rej and nonrej and True or False
@@ -908,133 +1024,15 @@ fr[FM_PARTIAL_SELECTED] = partial
if fr[FM_CHECKED] != newvalue:
fr[FM_CHECKED] = newvalue
- self._update_check_count()
- self._update_diff_header(dmodel, wfile, newvalue)
+ self.update_check_count()
+ self.update_diff_header(dmodel, wfile, newvalue)
- def _show_diff_hunks(self, files):
- ''' Update the diff text '''
- def markup(chunk):
- hunk = ""
- chunk.seek(0)
- lines = chunk.readlines()
- lines[-1] = lines[-1].strip('\n\r')
- for line in lines:
- line = gobject.markup_escape_text(hglib.toutf(line[:128]))
- if line[-1] != '\n':
- line += '\n'
- if line.startswith('---') or line.startswith('+++'):
- hunk += '<span foreground="#000090">%s</span>' % line
- elif line.startswith('-'):
- hunk += '<span foreground="#900000">%s</span>' % line
- elif line.startswith('+'):
- hunk += '<span foreground="#006400">%s</span>' % line
- elif line.startswith('@@'):
- hunk = '<span foreground="#FF8000">%s</span>' % line
- else:
- hunk += line
- return hunk
- def unmarkup(fp):
- hunk = ""
- fp.seek(0)
- lines = fp.readlines()
- lines[-1] = lines[-1].strip('\n\r')
- for line in lines:
- line = gobject.markup_escape_text(hglib.toutf(line[:128]))
- if line[-1] != '\n':
- line += '\n'
- hunk += line
- return hunk
-
- def dohgdiff():
- self.diff_model.clear()
- difftext = cStringIO.StringIO()
- try:
- if len(files) != 0:
- wfiles = [self.repo.wjoin(x) for x in files]
- matcher = cmdutil.match(self.repo, wfiles, self.opts)
- diffopts = mdiff.diffopts(git=True, nodates=True,
- nobinary=True)
- for s in patch.diff(self.repo, self._node1, self._node2,
- match=matcher, opts=diffopts):
- difftext.writelines(s.splitlines(True))
- difftext.seek(0)
-
- self._shelve_chunks = hgshelve.parsepatch(difftext)
- self._filechunks = {}
- skip = False
- for n, chunk in enumerate(self._shelve_chunks):
- if isinstance(chunk, hgshelve.header):
- text = chunk.selpretty(lambda x: True)
- for f in chunk.files():
- self._filechunks[f] = [len(self.diff_model)]
- row = [False, text, text, text,
- True, n, self.headerfont]
- self.diff_model.append(row)
- skip = chunk.special()
- elif not skip:
- fp = cStringIO.StringIO()
- chunk.pretty(fp)
- markedup = markup(fp)
- text = unmarkup(fp)
- f = chunk.filename()
- self._filechunks[f].append(len(self.diff_model))
- row = [False, markedup, text, markedup,
- False, n, self.difffont]
- self.diff_model.append(row)
- finally:
- difftext.close()
-
- if self.merging:
- return
- self._hg_call_wrapper('Diff', dohgdiff)
-
- def _showdiff_toggled(self, togglebutton, data=None):
- # prevent movement events while setting position
- self._diffpane.handler_block(self._diffpane_moved_id)
-
- if togglebutton.get_active():
- if self.merging:
- self.merge_sel_changed(self.filetree.get_selection(), True)
- else:
- self.tree_sel_changed(self.filetree.get_selection(), True)
- self._diffpane.set_position(self._setting_lastpos)
- else:
- self._setting_lastpos = self._diffpane.get_position()
- self._diffpane.set_position(64000)
-
- self._diffpane.handler_unblock(self._diffpane_moved_id)
- return True
-
-
- def _diffpane_moved(self, paned, data=None):
- # prevent toggle events while setting toolbar state
- self.showdiff_toggle.handler_block(self._showdiff_toggled_id)
- if self.diffbottom:
- sizemax = self._diffpane.get_allocation().height
- else:
- sizemax = self._diffpane.get_allocation().width
-
- if self.showdiff_toggle.get_active():
- if paned.get_position() >= sizemax - 55:
- self.showdiff_toggle.set_active(False)
- elif paned.get_position() < sizemax - 55:
- self.showdiff_toggle.set_active(True)
- selection = self.filetree.get_selection()
- if self.merging:
- self.merge_sel_changed(selection, True)
- else:
- self.tree_sel_changed(selection, True)
-
- self.showdiff_toggle.handler_unblock(self._showdiff_toggled_id)
- return False
-
-
- def _refresh_clicked(self, toolbutton, data=None):
+ def refresh_clicked(self, toolbutton, data=None):
self.reload_status()
return True
- def _save_clicked(self, toolbutton, data=None):
+ def save_clicked(self, toolbutton, data=None):
'Write selected diff hunks to a patch file'
revrange = self.opts.get('rev')[0]
filename = "%s.patch" % revrange.replace(':', '_to_')
@@ -1045,40 +1043,45 @@ if not result:
return
- cids = []
- dmodel = self.diff_model
+ buf = cStringIO.StringIO()
+ dmodel = self.diffmodel
for row in self.filemodel:
- if row[FM_CHECKED]:
- wfile = util.pconvert(row[FM_PATH])
- fc = self._filechunks[wfile]
- cids.append(fc[0])
- cids += [dmodel[r][DM_CHUNK_ID] for r in fc[1:]
- if not dmodel[r][DM_REJECTED]]
+ if not row[FM_CHECKED]:
+ continue
+ wfile = row[FM_PATH]
+ if wfile not in self.filechunks:
+ continue
+ chunks = self.filechunks[wfile]
+ for i, chunk in enumerate(chunks):
+ if i == 0:
+ chunk.write(buf)
+ elif not dmodel[start+i][DM_REJECTED]:
+ chunk.write(buf)
+ buf.seek(0)
try:
fp = open(result, "w")
- for cid in cids:
- self._shelve_chunks[cid].write(fp)
+ fp.write(buf.read())
except OSError:
pass
finally:
fp.close()
- def _revert_clicked(self, toolbutton, data=None):
- revert_list = self._relevant_files('MAR!')
+ def revert_clicked(self, toolbutton, data=None):
+ revert_list = self.relevant_files('MAR!')
if len(revert_list) > 0:
- self._hg_revert(revert_list)
+ self.hg_revert(revert_list)
else:
gdialog.Prompt(_('Nothing Reverted'),
_('No revertable files selected'), self).run()
return True
- def _revert_file(self, stat, wfile):
- self._hg_revert([wfile])
+ def revert_file(self, stat, wfile):
+ self.hg_revert([wfile])
return True
- def _log_file(self, stat, wfile):
+ def log_file(self, stat, wfile):
# Might want to include 'rev' here... trying without
from hggtk import history
dlg = history.GLog(self.ui, self.repo, self.cwd, [wfile], self.opts)
@@ -1086,7 +1089,7 @@ return True
- def _hg_revert(self, files):
+ def hg_revert(self, files):
wfiles = [self.repo.wjoin(x) for x in files]
if self.count_revs() > 1:
gdialog.Prompt(_('Nothing Reverted'),
@@ -1130,22 +1133,22 @@ shlib.shell_notify(wfiles)
self.reload_status()
- def _add_clicked(self, toolbutton, data=None):
- add_list = self._relevant_files('?I')
+ def add_clicked(self, toolbutton, data=None):
+ add_list = self.relevant_files('?I')
if len(add_list) > 0:
- self._hg_add(add_list)
+ self.hg_add(add_list)
else:
gdialog.Prompt(_('Nothing Added'),
_('No addable files selected'), self).run()
return True
- def _add_file(self, stat, wfile):
- self._hg_add([wfile])
+ def add_file(self, stat, wfile):
+ self.hg_add([wfile])
return True
- def _hg_add(self, files):
+ def hg_add(self, files):
wfiles = [self.repo.wjoin(x) for x in files]
# Create new opts, so nothing unintented gets through
addopts = self.merge_opts(commands.table['^add'][1],
@@ -1158,20 +1161,20 @@ shlib.shell_notify(wfiles)
self.reload_status()
- def _remove_clicked(self, toolbutton, data=None):
- remove_list = self._relevant_files('C!')
- delete_list = self._relevant_files('?I')
+ def remove_clicked(self, toolbutton, data=None):
+ remove_list = self.relevant_files('C!')
+ delete_list = self.relevant_files('?I')
if len(remove_list) > 0:
- self._hg_remove(remove_list)
+ self.hg_remove(remove_list)
if len(delete_list) > 0:
- self._delete_files(delete_list)
+ self.delete_files(delete_list)
if not remove_list and not delete_list:
gdialog.Prompt(_('Nothing Removed'),
_('No removable files selected'), self).run()
return True
- def _move_clicked(self, toolbutton, data=None):
- move_list = self._relevant_files('C')
+ def move_clicked(self, toolbutton, data=None):
+ move_list = self.relevant_files('C')
if move_list:
# get destination directory to files into
dlg = gtk.FileChooserDialog(title=_('Move files to diretory...'),
@@ -1196,18 +1199,18 @@
# move the files to dest directory
move_list.append(hglib.fromutf(destdir))
- self._hg_move(move_list)
+ self.hg_move(move_list)
else:
gdialog.Prompt(_('Nothing Moved'), _('No movable files selected\n\n'
'Note: only clean files can be moved.'), self).run()
return True
- def _delete_file(self, stat, wfile):
- self._delete_files([wfile])
+ def delete_file(self, stat, wfile):
+ self.delete_files([wfile])
- def _delete_files(self, files):
+ def delete_files(self, files):
dlg = gdialog.Confirm(_('Confirm Delete Unrevisioned'), files, self)
- if dlg.run() == gtk.RESPONSE_YES :
+ if dlg.run() == gtk.RESPONSE_YES:
errors = ''
for wfile in files:
try:
@@ -1224,12 +1227,12 @@ self.reload_status()
return True
- def _guess_rename(self, stat, wfile):
+ def guess_rename(self, stat, wfile):
dlg = guess.DetectRenameDialog()
dlg.show_all()
dlg.set_notify_func(self.ignoremask_updated)
- def _ignore_file(self, stat, wfile):
+ def ignore_file(self, stat, wfile):
dlg = hgignore.HgIgnoreDialog(self.repo.root, util.pconvert(wfile))
dlg.show_all()
dlg.set_notify_func(self.ignoremask_updated)
@@ -1239,19 +1242,19 @@ '''User has changed the ignore mask in hgignore dialog'''
self.reload_status()
- def _mark_resolved(self, stat, wfile):
+ def mark_resolved(self, stat, wfile):
ms = merge_.mergestate(self.repo)
ms.mark(util.pconvert(wfile), "r")
self.reload_status()
- def _unmark_resolved(self, stat, wfile):
+ def unmark_resolved(self, stat, wfile):
ms = merge_.mergestate(self.repo)
ms.mark(util.pconvert(wfile), "u")
self.reload_status()
- def _do_resolve(self, stat, wfile):
+ def do_resolve(self, stat, wfile):
ms = merge_.mergestate(self.repo)
wctx = self.repo[None]
mctx = wctx.parents()[-1]
@@ -1259,27 +1262,27 @@ self.reload_status()
- def _sel_clicked(self, state):
- self._select_files(state)
+ def sel_clicked(self, state):
+ self.select_files(state)
return True
- def _select_files(self, state, ctype=None):
+ def select_files(self, state, ctype=None):
for entry in self.filemodel:
if ctype and not entry[FM_STATUS] in ctype:
continue
if entry[FM_CHECKED] != state:
entry[FM_CHECKED] = state
- self._update_chunk_state(entry)
- self._update_check_count()
+ self.update_chunk_state(entry)
+ self.update_check_count()
- def _relevant_files(self, stats):
+ def relevant_files(self, stats):
return [item[FM_PATH] for item in self.filemodel \
if item[FM_CHECKED] and item[FM_STATUS] in stats]
- def _context_menu_act(self, menuitem, handler):
+ def context_menu_act(self, menuitem, handler):
selection = self.filetree.get_selection()
assert(selection.count_selected_rows() == 1)
@@ -1289,7 +1292,7 @@ return True
- def _tree_button_press(self, widget, event) :
+ def tree_button_press(self, widget, event):
# Set the flag to ignore the next activation when the
# shift/control keys are pressed. This avoids activations with
# multiple rows selected.
@@ -1301,14 +1304,14 @@ return False
- def _tree_button_release(self, widget, event) :
+ def tree_button_release(self, widget, event):
if event.button != 3:
return False
if not (event.state & (gtk.gdk.SHIFT_MASK | gtk.gdk.CONTROL_MASK)):
- self._tree_popup_menu(widget, event.button, event.time)
+ self.tree_popup_menu(widget, event.button, event.time)
return False
- def _get_file_context_menu(self, rowdata):
+ def get_file_context_menu(self, rowdata):
st = rowdata[FM_STATUS]
ms = rowdata[FM_MERGE_STATUS]
if ms:
@@ -1317,31 +1320,31 @@ menu = self._menus[st]
return menu
- def _tree_popup_menu(self, widget, button=0, time=0) :
+ def tree_popup_menu(self, widget, button=0, time=0):
selection = self.filetree.get_selection()
if selection.count_selected_rows() != 1:
return False
model, tpaths = selection.get_selected_rows()
- menu = self._get_file_context_menu(model[tpaths[0]])
+ menu = self.get_file_context_menu(model[tpaths[0]])
menu.popup(None, None, None, button, time)
return True
- def _tree_key_press(self, tree, event):
+ def tree_key_press(self, tree, event):
if event.keyval == 32:
def toggler(model, path, bufiter):
model[path][FM_CHECKED] = not model[path][FM_CHECKED]
- self._update_chunk_state(model[path])
+ self.update_chunk_state(model[path])
selection = self.filetree.get_selection()
selection.selected_foreach(toggler)
- self._update_check_count()
+ self.update_check_count()
return True
return False
- def _tree_row_act(self, tree, path, column) :
+ def tree_row_act(self, tree, path, column):
"""Default action is the first entry in the context menu
"""
# Ignore activations (like double click) on the first column,
@@ -1355,7 +1358,7 @@ return False
model, tpaths = selection.get_selected_rows()
- menu = self._get_file_context_menu(model[tpaths[0]])
+ menu = self.get_file_context_menu(model[tpaths[0]])
menu.get_children()[0].activate()
return True
|
This file's diff was not loaded because this changeset is very large. Load changes Loading... |
Loading...