/tortoisehg/hgtk/thgpbranch.py
https://bitbucket.org/tortoisehg/hgtk/ · Python · 871 lines · 788 code · 43 blank · 40 comment · 42 complexity · f6223616c8d7fab4d883b30b4d21d742 MD5 · raw file
- # thgpbranch.py - embeddable widget for the PatchBranch extension
- #
- # Copyright 2009 Peer Sommerlund <peer.sommerlund@gmail.com>
- #
- # This software may be used and distributed according to the terms of the
- # GNU General Public License version 2, incorporated herein by reference.
- import os
- import tempfile
- import gtk
- import gobject
- from mercurial import cmdutil, extensions, util
- from mercurial import commands as hg
- import mercurial.ui
- from tortoisehg.util.i18n import _
- from tortoisehg.hgtk import hgcmd
- from tortoisehg.hgtk import update
- from tortoisehg.hgtk import gtklib, dialog
- from tortoisehg.hgtk.logview import graphcell
- # Patch Branch model enumeration
- M_NODE = 0
- M_IN_LINES = 1
- M_OUT_LINES = 2
- M_NAME = 3
- M_STATUS = 4
- M_TITLE = 5
- M_MSG = 6
- M_MSGESC = 7
- # Patch Branch column enumeration
- C_GRAPH = 0
- C_STATUS = 1
- C_NAME = 2
- C_TITLE = 3
- C_MSG = 4
- class PBranchWidget(gtk.VBox):
- __gproperties__ = {
- 'graph-column-visible': (gobject.TYPE_BOOLEAN,
- 'Graph',
- 'Show graph column',
- False,
- gobject.PARAM_READWRITE),
- 'status-column-visible': (gobject.TYPE_BOOLEAN,
- 'Status',
- 'Show status column',
- False,
- gobject.PARAM_READWRITE),
- 'name-column-visible': (gobject.TYPE_BOOLEAN,
- 'Name',
- 'Show name column',
- False,
- gobject.PARAM_READWRITE),
- 'title-column-visible': (gobject.TYPE_BOOLEAN,
- 'Title',
- 'Show title column',
- False,
- gobject.PARAM_READWRITE),
- 'message-column-visible': (gobject.TYPE_BOOLEAN,
- 'Title',
- 'Show title column',
- False,
- gobject.PARAM_READWRITE),
- 'show-internal-branches': (gobject.TYPE_BOOLEAN,
- 'ShowInternalBranches',
- "Show internal branches",
- False,
- gobject.PARAM_READWRITE)
- }
- __gsignals__ = {
- 'repo-invalidated': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ()),
- 'patch-selected': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- (int, # revision number for patch head
- str)) # patch name
- }
- def __init__(self, parentwin, repo, statusbar, accelgroup=None, tooltips=None):
- gtk.VBox.__init__(self)
- self.parent_window = parentwin
- self.repo = repo
- self.pbranch = extensions.find('pbranch')
- self.statusbar = statusbar
- # top toolbar
- tbar = gtklib.SlimToolbar(tooltips)
- ## buttons
- self.btn = {}
- pmergebtn = tbar.append_button(gtk.STOCK_CONVERT,
- _('Merge all pending dependencies'))
- pmergebtn.connect('clicked', self.pmerge_clicked)
- self.btn['pmerge'] = pmergebtn
- pbackoutbtn = tbar.append_button(gtk.STOCK_GO_BACK,
- _('Backout current patch branch'))
- pbackoutbtn.connect('clicked', self.pbackout_clicked)
- self.btn['pbackout'] = pbackoutbtn
- reapplybtn = gtk.ToolButton(gtk.STOCK_GO_FORWARD)
- reapplybtn = tbar.append_button(gtk.STOCK_GO_FORWARD,
- _('Backport part of a changeset to a dependency'))
- reapplybtn.connect('clicked', self.reapply_clicked)
- self.btn['reapply'] = reapplybtn
- pnewbtn = tbar.append_button(gtk.STOCK_NEW,
- _('Start a new patch branch'))
- pnewbtn.connect('clicked', self.pnew_clicked)
- self.btn['pnew'] = pnewbtn
- pgraphbtn = tbar.append_button(gtk.STOCK_EDIT,
- _('Edit patch dependency graph'))
- pgraphbtn.connect('clicked', self.edit_pgraph_clicked)
- self.btn['pnew'] = pnewbtn
- ## separator
- tbar.append_space()
- ## drop-down menu
- menubtn = gtk.MenuToolButton('')
- menubtn.set_menu(self.create_view_menu())
- tbar.append_widget(menubtn, padding=0)
- self.btn['menu'] = menubtn
- def after_init():
- menubtn.child.get_children()[0].hide()
- gtklib.idle_add_single_call(after_init)
- self.pack_start(tbar, False, False)
- # center pane
- mainbox = gtk.VBox()
- self.pack_start(mainbox, True, True)
- ## scrolled pane
- pane = gtk.ScrolledWindow()
- pane.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- pane.set_shadow_type(gtk.SHADOW_IN)
- mainbox.pack_start(pane)
- ### patch list
- #### patch list model
- self.model = gtk.ListStore(
- gobject.TYPE_PYOBJECT, # node info
- gobject.TYPE_PYOBJECT, # in-lines
- gobject.TYPE_PYOBJECT, # out-lines
- str, # patch name
- str, # patch status
- str, # patch title
- str, # patch message
- str) # patch message escaped
- #### patch list view
- self.list = gtk.TreeView(self.model)
- # To support old PyGTK (<2.12)
- if hasattr(self.list, 'set_tooltip_column'):
- self.list.set_tooltip_column(M_MSGESC)
- self.list.connect('cursor-changed', self.list_sel_changed)
- self.list.connect('button-press-event', self.list_pressed)
- self.list.connect('row-activated', self.list_row_activated)
- self.list.connect('size-allocate', self.list_size_allocated)
- #### patch list columns
- self.cols = {}
- self.cells = {}
- def addcol(header, col_idx, model_idx=None, right=False, resizable=False,
- editable=False, editfunc=None, cell_renderer=None,
- properties=[]):
- header = (right and '%s ' or ' %s') % header
- cell = cell_renderer or gtk.CellRendererText()
- if editfunc:
- cell.set_property('editable', editable)
- cell.connect('edited', editfunc)
- col = gtk.TreeViewColumn(header, cell)
- if cell_renderer is None:
- col.add_attribute(cell, 'text', model_idx)
- col.set_resizable(resizable)
- col.set_visible(self.get_property(self.col_to_prop(col_idx)))
- if right:
- col.set_alignment(1)
- cell.set_property('xalign', 1)
- for (property_name, model_index) in properties:
- col.add_attribute(cell, property_name, model_index)
- self.list.append_column(col)
- self.cols[col_idx] = col
- self.cells[col_idx] = cell
- def cell_edited(cell, path, newname):
- row = self.model[path]
- patchname = row[M_NAME]
- if newname != patchname:
- self.qrename(newname, patch=patchname)
- #### patch list columns and cell renderers
- addcol(_('Graph'), C_GRAPH, resizable=True,
- cell_renderer=graphcell.CellRendererGraph(),
- properties=[("node", M_NODE),
- ("in-lines",M_IN_LINES),
- ("out-lines", M_OUT_LINES)]
- )
- addcol(_('St'), C_STATUS, M_STATUS)
- addcol(_('Name'), C_NAME, M_NAME, editfunc=cell_edited)
- addcol(_('Title'), C_TITLE, M_TITLE)
- addcol(_('Message'), C_MSG, M_MSG)
- pane.add(self.list)
- ## command widget
- self.cmd = hgcmd.CmdWidget(style=hgcmd.STYLE_COMPACT,
- tooltips=tooltips)
- mainbox.pack_start(self.cmd, False, False)
- # accelerator
- if accelgroup:
- # TODO
- pass
- ### public functions ###
- def refresh(self):
- """
- Refresh the list of patches.
- This operation will try to keep selection state.
- """
- if not self.pbranch:
- return
- # store selected patch name
- selname = None
- model, paths = self.list.get_selection().get_selected_rows()
- if len(paths) > 0:
- selname = model[paths[0]][M_NAME]
- # compute model data
- self.model.clear()
- opts = {'tips': True}
- mgr = self.pbranch.patchmanager(self.repo.ui, self.repo, opts)
- graph = mgr.graphforopts(opts)
- if not self.get_property('show-internal-branches'):
- graph = mgr.patchonlygraph(graph)
- names = None
- patch_list = graph.topolist(names)
- in_lines = []
- if patch_list:
- dep_list = [patch_list[0]]
- cur_branch = self.repo['.'].branch()
- patch_status = {}
- for name in patch_list:
- patch_status[name] = self.pstatus(name)
- for name in patch_list:
- parents = graph.deps(name)
- # Node properties
- if name in dep_list:
- node_column = dep_list.index(name)
- else:
- node_column = len(dep_list)
- node_colour = patch_status[name] and '#ff0000' or 0
- node_status = (name == cur_branch) and 4 or 0
- node = (node_column, node_colour, node_status)
-
- # Find next dependency list
- my_deps = []
- for p in parents:
- if p not in dep_list:
- my_deps.append(p)
- next_dep_list = dep_list[:]
- next_dep_list[node_column:node_column+1] = my_deps
-
- # Dependency lines
- shift = len(parents) - 1
- out_lines = []
- for p in parents:
- dep_column = next_dep_list.index(p)
- colour = 0 # black
- if patch_status[p]:
- colour = '#ff0000' # red
- style = 0 # solid lines
- out_lines.append((node_column, dep_column, colour, style))
- for lines in in_lines:
- (start_column, end_column, colour, style) = lines
- if end_column == node_column:
- # Deps to current patch end here
- pass
- else:
- # Find line continuations
- dep = dep_list[end_column]
- dep_column = next_dep_list.index(dep)
- out_lines.append((end_column, dep_column, colour, style))
-
- stat = patch_status[name] and 'M' or 'C' # patch status
- patchname = name
- msg = self.pmessage(name) # summary
- if msg:
- msg_esc = gtklib.markup_escape_text(msg) # escaped summary (utf-8)
- title = msg.split('\n')[0]
- else:
- msg_esc = None
- title = None
- self.model.append((node, in_lines, out_lines, patchname, stat,
- title, msg, msg_esc))
- # Loop
- in_lines = out_lines
- dep_list = next_dep_list
- # restore patch selection
- if selname:
- iter = self.get_iter_by_patchname(selname)
- if iter:
- self.list.get_selection().select_iter(iter)
- # update UI sensitives
- self.update_sensitives()
- # report status
- status_text = ''
- idle_text = None
- if self.has_patch():
- status_text = self.pending_merges() \
- and _('pending pmerges') \
- or _('no pending pmerges')
- self.statusbar.set_text(status_text, 'pbranch')
- self.statusbar.set_idle_text(idle_text)
- def pgraph(self):
- """
- [pbranch] Execute 'pgraph' command.
-
- :returns: A list of patches and dependencies
- """
- if self.pbranch is None:
- return None
- opts = {}
- mgr = self.pbranch.patchmanager(self.repo.ui, self.repo, opts)
- return mgr.graphforopts(opts)
- def patch_list(self, opts={}):
- """List all patches in pbranch dependency DAG"""
- mgr = self.pbranch.patchmanager(self.repo.ui, self.repo, opts)
- graph = mgr.graphforopts(opts)
- names = None
- return graph.topolist(names)
-
- def pending_merges(self):
- """Return True if there are pending pmerge operations"""
- for patch in self.patch_list():
- if self.pstatus(patch):
- return True
- return False
- def pstatus(self, patch_name):
- """
- [pbranch] Execute 'pstatus' command.
-
- :param patch_name: Name of patch-branch
- :retv: list of status messages. If empty there is no pending merges
- """
- if self.pbranch is None:
- return None
- status = []
- opts = {}
- mgr = self.pbranch.patchmanager(self.repo.ui, self.repo, opts)
- graph = mgr.graphforopts(opts)
- heads = self.repo.branchheads(patch_name)
- if len(heads) > 1:
- status.append(_('needs merge of %i heads\n') % len(heads))
- for dep, through in graph.pendingmerges(patch_name):
- if through:
- status.append(_('needs merge with %s (through %s)\n') %
- (dep, ", ".join(through)))
- else:
- status.append(_('needs merge with %s\n') % dep)
- for dep in graph.pendingrebases(patch_name):
- status.append(_('needs update of diff base to tip of %s\n') % dep)
- return status
- def pmessage(self, patch_name):
- """
- Get patch message
- :param patch_name: Name of patch-branch
- :retv: Full patch message. If you extract the first line
- you will get the patch title. If the repo does not contain
- message or patch, the function returns None
- """
- opts = {}
- mgr = self.pbranch.patchmanager(self.repo.ui, self.repo, opts)
- try:
- return mgr.patchdesc(patch_name)
- except:
- return None
- def peditmessage(self, patch_name):
- """
- Edit patch message
- :param patch_name: Name of patch-branch
- """
- if not patch_name in self.patch_list():
- return
- cmdline = ['hg', 'peditmessage', patch_name]
- self.cmd.execute(cmdline, self.cmd_done)
-
- def pdiff(self, patch_name):
- """
- [pbranch] Execute 'pdiff --tips' command.
-
- :param patch_name: Name of patch-branch
- :retv: list of lines of generated patch
- """
- opts = {}
- mgr = self.pbranch.patchmanager(self.repo.ui, self.repo, opts)
- graph = mgr.graphattips()
- return graph.diff(patch_name, None, opts)
- def pnew_ui(self):
- """
- Create new patch.
- Propmt user for new patch name. Patch is created
- on current branch.
- """
- parent = None
- title = _('New Patch Name')
- new_name = dialog.entry_dialog(parent, title)
- if not new_name:
- return False
- self.pnew(new_name)
- return True
- def pnew(self, patch_name):
- """
- [pbranch] Execute 'pnew' command.
-
- :param patch_name: Name of new patch-branch
- """
- if self.pbranch is None:
- return False
- self.pbranch.cmdnew(self.repo.ui, self.repo, patch_name)
- self.emit('repo-invalidated')
- return True
-
- def pmerge(self, patch_name=None):
- """
- [pbranch] Execute 'pmerge' command.
- :param patch_name: Merge to this patch-branch
- """
- if not self.has_patch():
- return
- cmdline = ['hg', 'pmerge']
- if patch_name:
- cmdline += [patch_name]
- else:
- cmdline += ['--all']
- self.cmd.execute(cmdline, self.cmd_done)
-
- def pbackout(self):
- """
- [pbranch] Execute 'pbackout' command.
- """
- assert False
- def pfinish(self, patch_name):
- """
- [pbranch] Execute 'pfinish' command.
-
- The workdir must be clean.
- The patch branch dependencies must be merged.
-
- :param patch_name: A patch branch (not an internal branch)
- """
- # Check preconditions for pfinish
- assert self.is_patch(patch_name)
- pmerge_status = self.pstatus(patch_name)
- if pmerge_status != []:
- dialog.error_dialog(self.parent_window,
- _('Pending Pmerge'),
- _('You cannot finish this patch branch unless you pmerge it first.\n'
- 'pmerge will solve the following issues with %(patch)s:\n'
- '* %(issuelist)s') %
- {'patch': patch_name,
- 'issuelist': '* '.join(pmerge_status)}
- )
- return
- if not self.workdir_is_clean():
- dialog.error_dialog(self.parent_window,
- _('Uncommitted Local Changes'),
- _('pfinish uses your working directory for temporary work.\n'
- 'Please commit your local changes before issuing pfinish.')
- )
- return
- if hasattr(self.repo, 'mq') and len(self.repo.mq.applied) > 0:
- dialog.error_dialog(self.parent_window,
- _('Applied MQ patch'),
- _('pfinish must be able to commit, but this is not allowed\n'
- 'as long as you have MQ patches applied.')
- )
- return
- # Set up environment for mercurial commands
- class CmdWidgetUi(mercurial.ui.ui):
- def __init__(self, cmdLogWidget):
- src = None
- super(CmdWidgetUi, self).__init__(src)
- self.cmdLogWidget = cmdLogWidget
- def write(self, *args, **opts):
- for a in args:
- self.cmdLogWidget.append(str(a))
- def write_err(self, *args, **opts):
- for a in args:
- self.cmdLogWidget.append(str(a), error=True)
- def flush(self):
- pass
- def prompt(self, msg, choices=None, default="y"):
- raise util.Abort("Internal Error: prompt not available")
- def promptchoice(self, msg, choices, default=0):
- raise util.Abort("Internal Error: promptchoice not available")
- def getpass(self, prompt=None, default=None):
- raise util.Abort("Internal Error: getpass not available")
- repo = self.repo
- ui = CmdWidgetUi(self.cmd.log)
- old_ui = repo.ui
- repo.ui = ui
- # Commit patch to dependency
- fd, patch_file_name = tempfile.mkstemp(prefix='thg-patch-')
- patch_file = os.fdopen(fd, 'w')
- patch_file.writelines(self.pdiff(patch_name))
- patch_file.close()
- upstream_branch = self.pgraph().deps(patch_name)[0]
- hg.update(ui, repo, rev=upstream_branch)
- hg.import_(ui, repo, patch_file_name, base='', strip=1)
- os.unlink(patch_file_name)
-
- # Close patch branch
- hg.update(ui, repo, rev=patch_name)
- hg.merge(ui, repo, upstream_branch)
- msg = _('Patch branch finished')
- hg.commit(ui, repo, close_branch=True, message=msg)
-
- # Update GUI
- repo.ui = old_ui
- self.emit('repo-invalidated')
- def has_pbranch(self):
- """ return True if pbranch extension can be used """
- return self.pbranch is not None
- def has_patch(self):
- """ return True if pbranch extension is in use on repo """
- if not self.has_pbranch():
- return False
- g = self.pgraph()
- return len(self.pbranch.patchonlygraph(g.mgr, g)._nodes) > 0
- def is_patch(self, branch_name):
- """ return True if branch is a patch. This excludes root branches
- and internal diff base branches (for patches with multiple
- dependencies. """
- return self.has_pbranch() and self.pgraph().ispatch(branch_name)
- def cur_branch(self):
- """ Return branch that workdir belongs to. """
- return self.repo.dirstate.branch()
- def workdir_is_clean(self):
- """ return True if the working directory is clean """
- c = self.repo[None]
- return not (c.modified() or c.added() or c.removed())
- ### internal functions ###
- def get_iter_by_patchname(self, name):
- """ return iter has specified patch name """
- if name:
- for row in self.model:
- if row[M_NAME] == name:
- return row.iter
- return None
- def get_path_by_patchname(self, name):
- """ return path has specified patch name """
- iter = self.get_iter_by_patchname(name)
- if iter:
- return self.model.get_path(iter)
- return None
- def update_sensitives(self):
- """ Update the sensitives of entire UI """
- def disable_pbranchcmd():
- for name in ('pbackout', 'pmerge', 'pnew', 'reapply'):
- self.btn[name].set_sensitive(False)
- if self.pbranch:
- self.list.set_sensitive(True)
- self.btn['menu'].set_sensitive(True)
- in_pbranch = True #TODO
- is_merge = len(self.repo.parents()) > 1
- self.btn['pmerge'].set_sensitive(in_pbranch)
- self.btn['pbackout'].set_sensitive(in_pbranch)
- self.btn['pnew'].set_sensitive(not is_merge)
- self.btn['reapply'].set_sensitive(True)
- else:
- self.list.set_sensitive(False)
- self.btn['menu'].set_sensitive(False)
- disable_pbranchcmd()
- def scroll_to_current(self):
- """
- Scroll to current patch in the patch list.
- If the patch is selected, it will do nothing.
- """
- if self.list.get_selection().count_selected_rows() > 0:
- return
- curpatch = self.cur_branch()
- if not curpatch:
- return
- path = self.get_path_by_patchname(curpatch)
- if path:
- self.list.scroll_to_cell(path)
- def show_patch_cmenu(self, list, path):
- """Context menu for selected patch"""
- row = self.model[path]
- menu = gtk.Menu()
- def append(label, handler=None):
- item = gtk.MenuItem(label, True)
- item.set_border_width(1)
- if handler:
- item.connect('activate', handler, row)
- menu.append(item)
- has_pbranch = self.has_pbranch()
- is_current = self.has_patch() and self.cur_branch() == row[M_NAME]
- is_patch = self.is_patch(row[M_NAME])
- is_internal = self.pbranch.isinternal(row[M_NAME])
- is_merge = len(self.repo.branchheads(row[M_NAME])) > 1
- if has_pbranch and not is_merge and not is_internal:
- append(_('_new'), self.pnew_activated)
- if not is_current:
- append(_('_goto (update workdir)'), self.goto_activated)
- if is_patch:
- append(_('_edit message'), self.edit_message_activated)
- append(_('_rename'), self.rename_activated)
- append(_('_delete'), self.delete_activated)
- append(_('_finish'), self.finish_activated)
- if len(menu.get_children()) > 0:
- menu.show_all()
- menu.popup(None, None, None, 0, 0)
- def create_view_menu(self):
- """Top right menu for selection of columns and
- view configuration."""
- menu = gtk.Menu()
- def append(item=None, handler=None, check=False,
- active=False, sep=False):
- if sep:
- item = gtk.SeparatorMenuItem()
- else:
- if isinstance(item, str):
- if check:
- item = gtk.CheckMenuItem(item)
- item.set_active(active)
- else:
- item = gtk.MenuItem(item)
- item.set_border_width(1)
- if handler:
- item.connect('activate', handler)
- menu.append(item)
- return item
- def colappend(label, col_idx, active=True):
- def handler(menuitem):
- col = self.cols[col_idx]
- col.set_visible(menuitem.get_active())
- propname = self.col_to_prop(col_idx)
- item = append(label, handler, check=True, active=active)
- self.vmenu[propname] = item
- self.vmenu = {}
- colappend(_('Show graph'), C_GRAPH)
- colappend(_('Show status'), C_STATUS, active=False)
- colappend(_('Show name'), C_NAME)
- colappend(_('Show title'), C_TITLE, active=False)
- colappend(_('Show message'), C_MSG, active=False)
- append(sep=True)
- def enable_editable(item):
- self.cells[C_NAME].set_property('editable', item.get_active())
- item = append(_('Enable editable cells'), enable_editable,
- check=True, active=False)
- self.vmenu['editable-cell'] = item
- item = append(_("Show internal branches"), lambda item: self.refresh(),
- check=True, active=False)
- self.vmenu['show-internal-branches'] = item
- menu.show_all()
- return menu
- def show_dialog(self, dlg):
- """Show modal dialog and block application
- See also show_dialog in history.py
- """
- dlg.set_transient_for(self.parent_window)
- dlg.show_all()
- dlg.run()
- if gtk.pygtk_version < (2, 12, 0):
- # Workaround for old PyGTK (< 2.12.0) issue.
- # See background of this: f668034aeda3
- dlg.set_transient_for(None)
-
- def update_by_row(self, row):
- branch = row[M_NAME]
- rev = cmdutil.revrange(self.repo, [branch])
- parents = [x.node() for x in self.repo.parents()]
- dialog = update.UpdateDialog(rev[0])
- self.show_dialog(dialog)
- self.update_completed(parents)
- def update_completed(self, oldparents):
- self.repo.invalidate()
- self.repo.dirstate.invalidate()
- newparents = [x.node() for x in self.repo.parents()]
- if not oldparents == newparents:
- self.emit('repo-invalidated')
- def cmd_done(self, returncode, useraborted, noemit=False):
- if returncode == 0:
- if self.cmd.get_pbar():
- self.cmd.set_result(_('Succeed'), style='ok')
- elif useraborted:
- self.cmd.set_result(_('Canceled'), style='error')
- else:
- self.cmd.set_result(_('Failed'), style='error')
- self.refresh()
- if not noemit:
- self.emit('repo-invalidated')
- def do_get_property(self, property):
- try:
- return self.vmenu[property.name].get_active()
- except:
- raise AttributeError, 'unknown property %s' % property.name
- def do_set_property(self, property, value):
- try:
- self.vmenu[property.name].set_active(value)
- except:
- raise AttributeError, 'unknown property %s' % property.name
- def col_to_prop(self, col_idx):
- if col_idx == C_GRAPH:
- return 'graph-column-visible'
- if col_idx == C_STATUS:
- return 'status-column-visible'
- if col_idx == C_NAME:
- return 'name-column-visible'
- if col_idx == C_TITLE:
- return 'title-column-visible'
- if col_idx == C_MSG:
- return 'message-column-visible'
- return ''
- ### signal handlers ###
- def list_pressed(self, list, event):
- x, y = int(event.x), int(event.y)
- pathinfo = list.get_path_at_pos(x, y)
- if event.button == 1:
- if not pathinfo:
- # HACK: clear selection after this function calling,
- # against selection by getting focus
- def unselect():
- selection = list.get_selection()
- selection.unselect_all()
- gtklib.idle_add_single_call(unselect)
- elif event.button == 3:
- if pathinfo:
- self.show_patch_cmenu(self.list, pathinfo[0])
- def list_sel_changed(self, list):
- path, focus = list.get_cursor()
- row = self.model[path]
- patchname = row[M_NAME]
- try:
- ctx = self.repo[patchname]
- revid = ctx.rev()
- except hglib.RepoError:
- revid = -1
- self.emit('patch-selected', revid, patchname)
- def list_row_activated(self, list, path, column):
- self.update_by_row(self.model[path])
- def list_size_allocated(self, list, req):
- if self.has_patch():
- self.scroll_to_current()
- def pbackout_clicked(self, toolbutton):
- pass
- def pmerge_clicked(self, toolbutton):
- self.pmerge()
- def pnew_clicked(self, toolbutton):
- self.pnew_ui()
- def reapply_clicked(self, toolbutton):
- pass
- def edit_pgraph_clicked(self, toolbutton):
- opts = {} # TODO: How to find user ID
- mgr = self.pbranch.patchmanager(self.repo.ui, self.repo, opts)
- oldtext = mgr.graphdesc()
- # run editor in the repository root
- olddir = os.getcwd()
- os.chdir(self.repo.root)
- newtext = self.repo.ui.edit(oldtext, opts.get('user'))
- os.chdir(olddir)
- mgr.updategraphdesc(newtext)
- ### context menu signal handlers ###
- def pnew_activated(self, menuitem, row):
- """Insert new patch after this row"""
- if self.cur_branch() == row[M_NAME]:
- self.pnew_ui()
- return
- # pnew from patch different than current
- assert False
- if self.wdir_modified():
- # Ask user if current changes should be discarded
- # Abort if user does not agree
- pass
- # remember prev branch
- # Update to row[M_NAME]
- # pnew_ui
- # if aborted, update back to prev branch
- pass
- def edit_message_activated(self, menuitem, row):
- self.peditmessage(row[M_NAME])
- def goto_activated(self, menuitem, row):
- self.update_by_row(row)
- def delete_activated(self, menuitem, row):
- assert False
- def rename_activated(self, menuitem, row):
- assert False
- def finish_activated(self, menuitem, row):
- self.pfinish(row[M_NAME])