Mercurial and Git clients can push and pull from this alias URL to interact with this repository. You can change to which repository an alias points by going to the Aliases link on the project page.
# 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.importgtkimportgobjectfrommercurialimportcmdutil,extensionsfromtortoisehg.util.i18nimport_fromtortoisehg.hgtkimportupdatefromtortoisehg.hgtkimportgtklib,dialogfromtortoisehg.hgtk.logviewimportgraphcell# Patch Branch model enumerationM_NODE=0M_IN_LINES=1M_OUT_LINES = 2
M_NAME = 3
M_STATUS = 4
+M_TITLE = 5# Patch Branch column enumeration
C_GRAPH = 0
C_STATUS = 1
C_NAME = 2
+C_TITLE = 3class 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), 'show-internal-branches': (gobject.TYPE_BOOLEAN,
'ShowInternalBranches',
"Show internal branches",
False,gobject.PARAM_READWRITE)}__gsignals__={'repo-invalidated':(gobject.SIGNAL_RUN_FIRST,gobject.TYPE_NONE,()),}def__init__(self,parentwin,repo,statusbar,accelgroup=None,tooltips=None):gtk.VBox.__init__(self)self.parent_window=parentwinself.repo=repoself.pbranch=extensions.find('pbranch')self.statusbar=statusbar# top toolbartbar=gtklib.SlimToolbar(tooltips)## buttonsself.btn={}pmergebtn=tbar.append_stock(gtk.STOCK_CONVERT,_('Merge pending dependencies'))pmergebtn.connect('clicked',self.pmerge_clicked)self.btn['pmerge']=pmergebtnpbackoutbtn=tbar.append_stock(gtk.STOCK_GO_BACK,_('Backout current patch branch'))pbackoutbtn.connect('clicked',self.pbackout_clicked)self.btn['pbackout']=pbackoutbtnreapplybtn=gtk.ToolButton(gtk.STOCK_GO_FORWARD)reapplybtn=tbar.append_stock(gtk.STOCK_GO_FORWARD,_('Backport part of a changeset to a dependency'))reapplybtn.connect('clicked',self.reapply_clicked)self.btn['reapply']=reapplybtnpnewbtn=tbar.append_stock(gtk.STOCK_NEW,_('Start a new patch branch'))pnewbtn.connect('clicked',self.pnew_clicked)self.btn['pnew']=pnewbtn## separatortbar.append_space()## drop-down menumenubtn=gtk.MenuToolButton('')menubtn.set_menu(self.create_view_menu())tbar.append_widget(menubtn,padding=0)self.btn['menu']=menubtndefafter_init():menubtn.child.get_children()[0].hide()gtklib.idle_add_single_call(after_init)self.pack_start(tbar,False,False)# center panemainbox=gtk.VBox()self.pack_start(mainbox,True,True)## scrolled panepane=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 modelself.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 status+ str) # patch title #### patch list view
self.list = gtk.TreeView(self.model)
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 columnsself.cols={}self.cells={}defaddcol(header,col_idx,model_idx=None,right=False,resizable=False,editable=False,editfunc=None,cell_renderer=None,properties=[]):header=(rightand'%s 'or' %s')%headercell=cell_rendererorgtk.CellRendererText()ifeditfunc:cell.set_property('editable',editable)cell.connect('edited',editfunc)col=gtk.TreeViewColumn(header,cell)ifcell_rendererisNone:col.add_attribute(cell,'text',model_idx)col.set_resizable(resizable)col.set_visible(self.get_property(self.col_to_prop(col_idx)))ifright:col.set_alignment(1)cell.set_property('xalign',1)for(property_name,model_index)inproperties:col.add_attribute(cell,property_name,model_index)self.list.append_column(col)self.cols[col_idx]=colself.cells[col_idx]=celldefcell_edited(cell,path,newname):row=self.model[path]patchname=row[M_NAME]ifnewname!=patchname:self.qrename(newname,patch=patchname)#### patch list columns and cell renderersaddcol(_('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) pane.add(self.list)
# acceleratorifaccelgroup:# TODOpass### public functions ###defrefresh(self):""" Refresh the list of patches. This operation will try to keep selection state. """ifnotself.pbranch:return# store selected patch nameselname=Nonemodel,paths=self.list.get_selection().get_selected_rows()iflen(paths)>0:selname=model[paths[0]][M_NAME]# compute model dataself.model.clear()opts={}mgr=self.pbranch.patchmanager(self.repo.ui,self.repo,opts)graph=mgr.graphforopts(opts)ifnotself.get_property('show-internal-branches'):graph=mgr.patchonlygraph(graph)names=Nonepatch_list=graph.topolist(names)in_lines=[]ifpatch_list:dep_list=[patch_list[0]]cur_branch=self.repo['.'].branch()patch_status={}fornameinpatch_list:patch_status[name]=self.pstatus(name)fornameinpatch_list:parents=graph.deps(name)# Node propertiesifnameindep_list:node_column=dep_list.index(name)else:node_column=len(dep_list)node_colour=patch_status[name]and'#ff0000'or0node_status=(name==cur_branch)and4or0node=(node_column,node_colour,node_status)# Find next dependency listmy_deps=[]forpinparents:ifpnotindep_list:my_deps.append(p)next_dep_list=dep_list[:]next_dep_list[node_column:node_column+1]=my_deps# Dependency linesshift=len(parents)-1out_lines=[]forpinparents:dep_column=next_dep_list.index(p)colour=0# blackifpatch_status[p]:colour='#ff0000'# redstyle=0# solid linesout_lines.append((node_column,dep_column,colour,style))forlinesinin_lines:(start_column,end_column,colour,style)=linesifend_column==node_column:# Deps to current patch end herepasselse:# Find line continuationsdep=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 = '%s' %parents # summary (utf-8)
-msg_esc = 'what-is-this-for' # escaped summary (utf-8)
- self.model.append((node, in_lines, out_lines, patchname, stat))
+ msg = self.pmessage(name) or '' #summary+ msg_esc = 'message for tool-tip' # escaped summary (utf-8)
+title = msg.split('\n')[0]+ self.model.append((node, in_lines, out_lines, patchname, stat, title))
# Loop
in_lines = out_lines
dep_list = next_dep_list
# restore patch selectionifselname:iter=self.get_iter_by_patchname(selname)ifiter:self.list.get_selection().select_iter(iter)# update UI sensitivesself.update_sensitives()# report statusstatus_text=''idle_text=Noneifself.has_patch():status_text=self.pending_merges() \
and_('pending pmerges') \
or_('no pending pmerges')self.statusbar.set_right3_text(status_text)self.statusbar.set_idle_text(idle_text)defpgraph(self):""" [pbranch] Execute 'pgraph' command. :returns: A list of patches and dependencies """ifself.pbranchisNone:returnNoneopts={}mgr=self.pbranch.patchmanager(self.repo.ui,self.repo,opts)returnmgr.graphforopts(opts)defpatch_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=Nonereturngraph.topolist(names)defpending_merges(self):"""Return True if there are pending pmerge operations"""forpatchinself.patch_list():ifself.pstatus(patch):returnTruereturnFalsedefpstatus(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 """ifself.pbranchisNone:returnNonestatus=[]opts={}mgr=self.pbranch.patchmanager(self.repo.ui,self.repo,opts)graph=mgr.graphforopts(opts)heads=self.repo.branchheads(patch_name)iflen(heads)>1:status.append(_('needs merge of %i heads\n')%len(heads))fordep,throughingraph.pendingmerges(patch_name):ifthrough:status.append(_('needs merge with %s (through %s)\n')%(dep,", ".join(through)))else:status.append(_('needs merge with %s\n')%dep)fordepingraph.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.+ """+ opts = {}+ mgr = self.pbranch.patchmanager(self.repo.ui, self.repo, opts)+ return mgr.patchdesc(patch_name)+ def pnew_ui(self):
"""
Create new patch.
Propmt user for new patch name. Patch is created on current branch. """parent=Nonetitle=_('New Patch Name')new_name=dialog.entry_dialog(parent,title)ifnotnew_name:returnFalseself.pnew(new_name)returnTruedefpnew(self,patch_name):""" [pbranch] Execute 'pnew' command. :param patch_name: Name of new patch-branch """ifself.pbranchisNone:returnFalseself.pbranch.cmdnew(self.repo.ui,self.repo,patch_name)self.emit('repo-invalidated')returnTruedefpmerge(self):""" [pbranch] Execute 'pmerge' command. """assertFalsedefpbackout(self):""" [pbranch] Execute 'pbackout' command. """assertFalsedefhas_pbranch(self):""" return True if pbranch extension can be used """returnself.pbranchisnotNonedefhas_patch(self):""" return True if pbranch extension is in use on repo """returnself.has_pbranch()andself.pgraph()!=[]defcur_patch(self):current_patch=self.repo.dirstate.branch()ifcurrent_patch=='default':returnNoneifcurrent_patchnotinself.patch_list():returnNonereturncurrent_patch### internal functions ###defget_iter_by_patchname(self,name):""" return iter has specified patch name """ifname:forrowinself.model:ifrow[M_NAME]==name:returnrow.iterreturnNonedefget_path_by_patchname(self,name):""" return path has specified patch name """returnself.model.get_path(self.get_iter_by_patchname(name))defupdate_sensitives(self):""" Update the sensitives of entire UI """defdisable_pbranchcmd():fornamein('pbackout','pmerge','pnew','reapply'):self.btn[name].set_sensitive(False)ifself.pbranch:self.list.set_sensitive(True)self.btn['menu'].set_sensitive(True)in_pbranch=True#TODOself.btn['pmerge'].set_sensitive(in_pbranch)self.btn['pbackout'].set_sensitive(in_pbranch)self.btn['pnew'].set_sensitive(True)self.btn['reapply'].set_sensitive(True)else:self.list.set_sensitive(False)self.btn['menu'].set_sensitive(False)disable_pbranchcmd()defscroll_to_current(self):""" Scroll to current patch in the patch list. If the patch is selected, it will do nothing. """ifself.list.get_selection().count_selected_rows()>0:returncurpatch=self.cur_patch()ifnotcurpatch:returnpath=self.get_path_by_patchname(curpatch)ifpath:self.list.scroll_to_cell(path)defshow_patch_cmenu(self,list,path):"""Context menu for selected patch"""row=self.model[path]menu=gtk.Menu()defappend(label,handler=None):item=gtk.MenuItem(label,True)item.set_border_width(1)ifhandler:item.connect('activate',handler,row)menu.append(item)has_pbranch=self.has_pbranch()is_current=self.has_patch()andself.cur_patch()==row[M_NAME]is_patch=row[M_NAME]!='default'ifhas_pbranch:append(_('_new'),self.pnew_activated)ifnotis_current:append(_('_goto (update workdir)'),self.goto_activated)ifis_patch:append(_('_rename'),self.rename_activated)append(_('_delete'),self.delete_activated)iflen(menu.get_children())>0:menu.show_all()menu.popup(None,None,None,0,0)defcreate_view_menu(self):"""Top right menu for selection of columns and view configuration."""menu=gtk.Menu()defappend(item=None,handler=None,check=False,active=False,sep=False):ifsep:item=gtk.SeparatorMenuItem()else:ifisinstance(item,str):ifcheck:item=gtk.CheckMenuItem(item)item.set_active(active)else:item=gtk.MenuItem(item)item.set_border_width(1)ifhandler:item.connect('activate',handler)menu.append(item)returnitemdefcolappend(label,col_idx,active=True):defhandler(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]=itemself.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) append(sep=True)
defenable_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']=itemitem=append(_("Show internal branches"),lambdaitem:self.refresh(),check=True,active=False)self.vmenu['show-internal-branches']=itemmenu.show_all()returnmenudefshow_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()ifgtk.pygtk_version<(2,12,0):# Workaround for old PyGTK (< 2.12.0) issue.# See background of this: f668034aeda3dlg.set_transient_for(None)defupdate_by_row(self,row):branch=row[M_NAME]rev=cmdutil.revrange(self.repo,[branch])parents=[x.node()forxinself.repo.parents()]dialog=update.UpdateDialog(rev[0])self.show_dialog(dialog)self.update_completed(parents)defupdate_completed(self,oldparents):self.repo.invalidate()self.repo.dirstate.invalidate()newparents=[x.node()forxinself.repo.parents()]ifnotoldparents==newparents:self.emit('repo-invalidated')defdo_get_property(self,property):try:returnself.vmenu[property.name].get_active()except:raiseAttributeError,'unknown property %s'%property.namedefdo_set_property(self,property,value):try:self.vmenu[property.name].set_active(value)except:raiseAttributeError,'unknown property %s'%property.namedefcol_to_prop(self,col_idx):ifcol_idx==C_GRAPH:return'graph-column-visible'ifcol_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' return ''
### signal handlers ###
deflist_pressed(self,list,event):x,y=int(event.x),int(event.y)pathinfo=list.get_path_at_pos(x,y)ifevent.button==1:ifnotpathinfo:# HACK: clear selection after this function calling,# against selection by getting focusdefunselect():selection=list.get_selection()selection.unselect_all()gtklib.idle_add_single_call(unselect)elifevent.button==3:ifpathinfo:self.show_patch_cmenu(self.list,pathinfo[0])deflist_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()excepthglib.RepoError:revid=-1self.emit('patch-selected',revid,patchname)deflist_row_activated(self,list,path,column):self.update_by_row(self.model[path])deflist_size_allocated(self,list,req):ifself.has_patch():self.scroll_to_current()defpbackout_clicked(self,toolbutton):passdefpmerge_clicked(self,toolbutton):passdefpnew_clicked(self,toolbutton):self.pnew_ui()defreapply_clicked(self,toolbutton):pass### context menu signal handlers ###defpnew_activated(self,menuitem,row):"""Insert new patch after this row"""ifself.cur_patch()==row[M_NAME]:self.pnew_ui()return# pnew from patch different than currentassertFalseifself.wdir_modified():# Ask user if current changes should be discarded# Abort if user does not agreepass# remember prev branch# Update to row[M_NAME]# pnew_ui# if aborted, update back to prev branchpassdefgoto_activated(self,menuitem,row):self.update_by_row(row)defdelete_activated(self,menuitem,row):assertFalsedefrename_activated(self,menuitem,row):assertFalse
Attach a Trello Card
Add a tag
Your session has expired
You are no longer logged in. Please log in and try your request again.
Filter RSS Feed
This RSS feed URL allows you to see the contents of your current filter using any feed reader.
This link includes a special authentication token. If you share the URL with anyone else, they can see this RSS feed's activity. You can disable these tokens when needed.
Your current filter is unsaved; changing it won't affect this RSS feed.