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.
# thgmq.py - embeddable widget for MQ extension## Copyright 2009 Yuki KODAMA <endflow.net@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.importosimportgtkimport gobject
import pango
+from mercurial import extensions+from tortoisehg.util.i18n import _
from tortoisehg.util import hglib
fromtortoisehg.hgtkimportgtklib,hgcmd# MQ patches row enumerationsMQ_INDEX=0MQ_STATUS=1MQ_NAME=2MQ_SUMMARY=3MQ_ESCAPED=4# Special patch indicesINDEX_SEPARATOR=-1INDEX_QPARENT=-2classMQWidget(gtk.VBox):__gproperties__={'index-column-visible':(gobject.TYPE_BOOLEAN,'Index','Show index 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),'summary-column-visible':(gobject.TYPE_BOOLEAN,'Summary','Show summary column',False,gobject.PARAM_READWRITE),'editable-cell':(gobject.TYPE_BOOLEAN,'EditableCell','Enable editable cells',False,gobject.PARAM_READWRITE),'show-qparent':(gobject.TYPE_BOOLEAN,'ShowQParent',"Show 'qparent'",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 numberstr))# patch name}def__init__(self,repo,accelgroup=None,tooltips=None):gtk.VBox.__init__(self) self.repo = repo
self.mqloaded = hasattr(repo, 'mq')
+ try:+ extensions.find('qup')+ self.hasqup = True+ except KeyError:+ self.hasqup = False+ # top toolbar
tbar = gtklib.SlimToolbar(tooltips)
## buttonsself.btn={}popallbtn=tbar.append_stock(gtk.STOCK_GOTO_FIRST,_('Unapply all patches'))popallbtn.connect('clicked',self.popall_clicked)self.btn['popall']=popallbtnpopbtn=tbar.append_stock(gtk.STOCK_GO_BACK,_('Unapply last patch'))popbtn.connect('clicked',self.pop_clicked)self.btn['pop']=popbtnpushbtn=gtk.ToolButton(gtk.STOCK_GO_FORWARD)pushbtn=tbar.append_stock(gtk.STOCK_GO_FORWARD,_('Apply next patch'))pushbtn.connect('clicked',self.push_clicked)self.btn['push']=pushbtnpushallbtn=tbar.append_stock(gtk.STOCK_GOTO_LAST,_('Apply all patches'))pushallbtn.connect('clicked',self.pushall_clicked)self.btn['pushall']=pushallbtn## separatortbar.append_space()## drop-down menumenubtn=gtk.MenuToolButton('')menubtn.set_menu(self.create_view_menu())tbar.append_widget(menubtn,padding=0)self.btn['menu']=menubtnself.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 listself.model=gtk.ListStore(int,# patch indexstr,# patch statusstr,# patch namestr,# summarystr)# escaped summaryself.list=gtk.TreeView(self.model)self.list.set_row_separator_func(self.row_sep_func)# To support old PyGTK (<2.12)ifhasattr(self.list,'set_tooltip_column'):self.list.set_tooltip_column(MQ_ESCAPED)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)self.cols={}self.cells={}defaddcol(header,col_idx,right=False,resizable=False,editable=False,editfunc=None):header=(rightand'%s 'or' %s')%headercell=gtk.CellRendererText()ifeditfunc:cell.set_property('editable',editable)cell.connect('edited',editfunc)col=gtk.TreeViewColumn(header,cell)col.add_attribute(cell,'text',col_idx)col.set_cell_data_func(cell,self.cell_data_func)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)self.list.append_column(col)self.cols[col_idx]=colself.cells[col_idx]=celldefcell_edited(cell,path,newname):row=self.model[path]ifrow[MQ_INDEX]<0:returnpatchname=row[MQ_NAME]ifnewname!=patchname:self.qrename(newname,patch=patchname)addcol(_('#'),MQ_INDEX,right=True)addcol(_('st'),MQ_STATUS)addcol(_('Name'),MQ_NAME,editfunc=cell_edited)addcol(_('Summary'),MQ_SUMMARY,resizable=True)pane.add(self.list)## command widgetself.cmd=hgcmd.CmdWidget(style=hgcmd.STYLE_COMPACT,tooltips=tooltips)mainbox.pack_start(self.cmd,False,False)# acceleratorifaccelgroup:key,mod=gtk.accelerator_parse('F2')self.list.add_accelerator('thg-rename',accelgroup,key,mod,gtk.ACCEL_VISIBLE)defthgrename(list):sel=self.list.get_selection()ifsel.count_selected_rows()==1:model,paths=sel.get_selected_rows()self.qrename_ui(model[paths[0]][MQ_NAME])self.list.connect('thg-rename',thgrename)# prepare to showself.refresh()### public functions ###defrefresh(self):""" Refresh the list of patches. This operation will try to keep selection state. """ifnotself.mqloaded:return# store selected patch nameselname=Nonemodel,paths=self.list.get_selection().get_selected_rows()iflen(paths)>0:selname=model[paths[0]][MQ_NAME]# clear model dataself.model.clear()# insert 'qparent' rowtop=Noneifself.get_property('show-qparent'):top=self.model.append((INDEX_QPARENT,None,None,None,None))# add patchesfromhgextimportmqq=self.repo.mqq.parse_series()applied=set([p.nameforpinq.applied])forindex,patchnameinenumerate(q.series):stat=patchnameinappliedand'A'or'U'try:msg=mq.patchheader(q.join(patchname)).message[0]msg_esc=gtklib.markup_escape_text(msg)exceptIndexError:msg=msg_esc=Noneiter=self.model.append((index,stat,patchname,msg,msg_esc))ifstat=='A':top=iter# insert separatoriftop:self.model.insert_after(top,(INDEX_SEPARATOR,None,None,None,None))# restore patch selectionifselname:iter=self.get_iter_by_patchname(selname)ifiter:self.list.get_selection().select_iter(iter)# update UI sensitivesself.update_sensitives()defqgoto(self,patch):""" [MQ] Execute 'qgoto' command. patch: the patch name or an index to specify the patch. """ifnotself.is_operable():returncmdline=['hg','qgoto',patch]self.cmd.execute(cmdline,self.cmd_done)defqpop(self,all=False):""" [MQ] Execute 'qpop' command. all: if True, use '--all' option. (default: False) """ifnotself.is_operable():returncmdline=['hg','qpop']ifall:cmdline.append('--all')self.cmd.execute(cmdline,self.cmd_done)defqpush(self,all=False):""" [MQ] Execute 'qpush' command. all: if True, use '--all' option. (default: False) """ifnotself.is_operable():returncmdline=['hg','qpush']ifall:cmdline.append('--all')self.cmd.execute(cmdline,self.cmd_done)defqdelete(self,patch,keep=False):""" [MQ] Execute 'qdelete' command. patch: the patch name or an index to specify the patch. """ifnotself.has_patch():returncmdline=['hg','qdelete',patch]ifkeep:cmdline.append('--keep')self.cmd.execute(cmdline,self.cmd_done,noemit=True)defqrename(self,name,patch='qtip'):""" [MQ] Execute 'qrename' command. If 'patch' param isn't specified, renaming should be applied 'qtip' (current) patch. name: the new patch name for renaming. patch: the target patch name or index. (default: 'qtip') """ifnotnameornotself.has_patch():returncmdline=['hg','qrename',patch,name]self.cmd.execute(cmdline,self.cmd_done)defqrename_ui(self,patch='qtip'):""" Prepare the user interface for renaming the patch. If 'patch' param isn't specified, renaming should be started 'qtip' (current) patch. Return True if succeed to prepare; otherwise False. patch: the target patch name or index. (default: 'qtip') """ifnotself.mqloadedor \
patch=='qtip'and'qtip'inself.repo.tags():returnFalsetarget=self.repo.mq.lookup(patch)ifnottarget:returnFalsepath=self.get_path_by_patchname(target)ifnotpath:returnFalse# make the cell editablecell=self.cells[MQ_NAME]ifnotcell.get_property('editable'):cell.set_property('editable',True)defcanceled(cell,*arg):cell.disconnect(cancel_id)cell.disconnect(edited_id)cell.set_property('editable',False)cancel_id=cell.connect('editing-canceled',canceled)edited_id=cell.connect('edited',canceled)# start editing patchname cellself.list.set_cursor_on_cell(path,self.cols[MQ_NAME],None,True)returnTruedefqfinish(self,applied=False):""" [MQ] Execute 'qfinish' command. applied: if True, enable '--applied' option. (default: False) """ifnotself.has_applied():returncmdline=['hg','qfinish']ifapplied:cmdline.append('--applied')self.cmd.execute(cmdline,self.cmd_done)defqfold(self,patch):""" [MQ] Execute 'qfold' command. patch: the patch name or an index to specify the patch. """ifnotpatchornotself.has_applied():return cmdline = ['hg', 'qfold', patch]
self.cmd.execute(cmdline, self.cmd_done)
+ def mknext(self, patch):+ """+ [MQ] Execute 'qup patch'++ patch: the patch name or an index to specify the patch.+ """+ if not (self.hasqup and patch and self.is_operable()):+ return+ cmdline = ['hg', 'qup', patch]+ self.cmd.execute(cmdline, self.cmd_done)+ def has_patch(self):
""" return True if MQ has applicable patch """
if self.mqloaded:
returnlen(self.repo.mq.series)>0returnFalsedefhas_applied(self):""" return True if MQ has applied patches """ifself.mqloaded: return len(self.repo.mq.applied) > 0
return False
+ def number_applied(self):+ """ return the number of applied patches """+ if self.mqloaded:+ return len(self.repo.mq.applied)+ return 0+ def is_operable(self):
""" return True if MQ is operable """
if self.mqloaded:
repo=self.repoif'qtip'inself.repo.tags():returnrepo['.']==repo['qtip']returnlen(repo.mq.series)>0returnFalsedefis_qtip(self,patchname):ifpatchname:returnpatchname==self.get_qtip_patchname()returnFalse### internal functions ###defget_iter_by_patchname(self,name):""" return iter has specified patch name """ifname:forrowinself.model:ifrow[MQ_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))defget_qtip_patchname(self):ifself.mqloadedand'qtip'inself.repo.tags():returnself.repo.mq.applied[-1].namereturnNonedefupdate_sensitives(self):""" Update the sensitives of entire UI """defdisable_mqmoves():fornamein('popall','pop','push','pushall'):self.btn[name].set_sensitive(False)ifself.mqloaded:self.list.set_sensitive(True)self.btn['menu'].set_sensitive(True)ifself.is_operable():q=self.repo.mqin_bottom=len(q.applied)==0in_top=len(q.unapplied(self.repo))==0self.btn['popall'].set_sensitive(notin_bottom)self.btn['pop'].set_sensitive(notin_bottom)self.btn['push'].set_sensitive(notin_top)self.btn['pushall'].set_sensitive(notin_top)else:disable_mqmoves()else:self.list.set_sensitive(False)self.btn['menu'].set_sensitive(False)disable_mqmoves()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:returnqtipname=self.get_qtip_patchname()ifnotqtipname:returnpath=self.get_path_by_patchname(qtipname)ifpath:self.list.scroll_to_cell(path)defcell_data_func(self,column,cell,model,iter):row=model[iter]ifrow[MQ_INDEX]==INDEX_QPARENT:ifcolumn==self.cols[MQ_INDEX]:cell.set_property('text','')elifcolumn==self.cols[MQ_NAME]:cell.set_property('text','[qparent]')stat=row[MQ_STATUS]ifstat=='A':cell.set_property('foreground','blue')elifstat=='U':cell.set_property('foreground','#909090')else:cell.set_property('foreground','black')patchname=row[MQ_NAME]ifself.is_qtip(patchname):cell.set_property('weight',pango.WEIGHT_BOLD)else:cell.set_property('weight',pango.WEIGHT_NORMAL)defrow_sep_func(self,model,iter,data=None):returnmodel[iter][MQ_INDEX]==INDEX_SEPARATORdefshow_patch_cmenu(self,list,path):row=self.model[path]ifrow[MQ_INDEX]==INDEX_SEPARATOR:returnmenu=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)is_operable=self.is_operable()has_patch=self.has_patch()has_applied=self.has_applied() is_qtip = self.is_qtip(row[MQ_NAME])
is_qparent = row[MQ_INDEX] == INDEX_QPARENT
is_applied = row[MQ_STATUS] == 'A'
+ is_next = row[MQ_INDEX] == self.number_applied() if is_operable and not is_qtip and (not is_qparent or has_applied):
append(_('_goto'), self.goto_activated)
ifhas_patchandnotis_qparent:append(_('_rename'),self.rename_activated)ifhas_appliedandnotis_qparent:append(_('_finish applied'),self.finish_activated)ifnotis_appliedandnotis_qparent:append(_('_delete'),self.delete_activated) append(_('delete --keep'), self.delete_keep_activated)
if has_applied and not is_qparent:
append(_('f_old'), self.fold_activated)
+ if self.hasqup and not is_next:+ append(_('make it _next'), self.mknext_activated) if len(menu.get_children()) > 0:
menu.show_all()
menu.popup(None,None,None,0,0)defcreate_view_menu(self):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 index'),MQ_INDEX)colappend(_('Show status'),MQ_STATUS,active=False)colappend(_('Show name'),MQ_NAME)colappend(_('Show summary'),MQ_SUMMARY,active=False)append(sep=True)defenable_editable(item):self.cells[MQ_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 'qparent'"),lambdaitem:self.refresh(),check=True,active=True)self.vmenu['show-qparent']=itemmenu.show_all()returnmenudefqgoto_by_row(self,row):ifself.get_qtip_patchname()==row[MQ_NAME]:returnifrow[MQ_INDEX]==INDEX_QPARENT:self.qpop(all=True)else:self.qgoto(row[MQ_NAME])defcmd_done(self,returncode,noemit=False):self.repo.mq.invalidate()self.refresh()ifnotnoemit: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==MQ_INDEX:return'index-column-visible'ifcol_idx==MQ_STATUS:return'status-column-visible'elifcol_idx==MQ_NAME:return'name-column-visible'elifcol_idx==MQ_SUMMARY:return'summary-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()gobject.idle_add(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]ifrow[MQ_INDEX]<0:returnpatchname=row[MQ_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.qgoto_by_row(self.model[path])deflist_size_allocated(self,list,req):ifself.mqloadedandself.has_applied():self.scroll_to_current()defpopall_clicked(self,toolbutton):self.qpop(all=True)defpop_clicked(self,toolbutton):self.qpop()defpush_clicked(self,toolbutton):self.qpush()defpushall_clicked(self,toolbutton):self.qpush(all=True)### context menu signal handlers ###defgoto_activated(self,menuitem,row):self.qgoto_by_row(row)defdelete_activated(self,menuitem,row):self.qdelete(row[MQ_NAME])defdelete_keep_activated(self,menuitem,row):self.qdelete(row[MQ_NAME],keep=True)defrename_activated(self,menuitem,row):self.qrename_ui(row[MQ_NAME])deffinish_activated(self,menuitem,row):self.qfinish(applied=True) def fold_activated(self, menuitem, row):
self.qfold(row[MQ_NAME])
++ def mknext_activated(self, menuitem, row):+ self.mknext(row[MQ_NAME])
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.