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.
# status.py - working copy browser## Copyright 2010 Steve Borho <steve@borho.org>## This software may be used and distributed according to the terms of the# GNU General Public License version 2, incorporated herein by reference.importosfrommercurialimporthg,util,cmdutil,error,context,mergefromtortoisehg.utilimportpaths,hglibfromtortoisehg.hgqt.i18nimport_fromtortoisehg.hgqtimportqtlib,wctxactions,visdiff,cmdui,fileviewfromPyQt4.QtCoreimport*fromPyQt4.QtGuiimport*# This widget can be used as the basis of the commit tool or any other# working copy browser.# Technical Debt# We need a real icon set for file status types# Thread rowSelected, connect to an external progress bar# Chunk selection, tri-state checkboxes for commit# Maybe, Maybe Not# Investigate folding/nesting of filesCOL_PATH=0COL_STATUS=1COL_MERGE_STATE=2COL_PATH_DISPLAY=3COL_EXTENSION=4COL_SIZE=5_colors={}classStatusWidget(QWidget):'''Working copy status widget SIGNALS: progress() - for progress bar showMessage(unicode) - for status bar titleTextChanged(QString) - for window title '''progress=pyqtSignal(QString,object,QString,QString,object)titleTextChanged=pyqtSignal(QString)linkActivated=pyqtSignal(QString)showMessage=pyqtSignal(unicode)fileDisplayed=pyqtSignal(QString,QString)def__init__(self,repo,pats,opts,parent=None,checkable=True):QWidget.__init__(self,parent)self.opts=dict(modified=True,added=True,removed=True,deleted=True,unknown=True,clean=False,ignored=False,subrepo=True)self.opts.update(opts)self.repo=repoself.pats=patsself.checkable=checkableself.pctx=Noneself.savechecks=Trueself.refthread=None# determine the user configured status colors# (in the future, we could support full rich-text tags)labels=[(stat,val.uilabel)forstat,valinstatusTypes.items()]labels.extend([('r','resolve.resolved'),('u','resolve.unresolved')])forstat,labelinlabels:effect=qtlib.geteffect(label)foreineffect.split(';'):ife.startswith('color:'):_colors[stat]=QColor(e[7:])breakSP=QSizePolicysplit=QSplitter(Qt.Horizontal)split.setChildrenCollapsible(False)layout=QVBoxLayout()layout.setMargin(0)layout.addWidget(split)self.setLayout(layout)vbox=QVBoxLayout()vbox.setMargin(0)frame=QFrame(split)sp=SP(SP.Expanding,SP.Expanding)sp.setHorizontalStretch(0)sp.setVerticalStretch(0)frame.setSizePolicy(sp)frame.setLayout(vbox)hbox=QHBoxLayout()hbox.setMargin(4)hbox.setContentsMargins(0,0,0,0)self.refreshBtn=tb=QToolButton()tb.setToolTip(_('Refresh file list'))tb.setIcon(qtlib.geticon('view-refresh'))tb.clicked.connect(self.refreshWctx)le=QLineEdit()ifhasattr(le,'setPlaceholderText'):# Qt >= 4.7le.setPlaceholderText(_('### filter text ###'))else:lbl=QLabel(_('Filter:'))hbox.addWidget(lbl)st=''forsinstatusTypes:val=statusTypes[s]ifself.opts[val.name]:st=st+sself.statusfilter=StatusFilterButton(statustext=st,types=StatusType.preferredOrder)ifself.checkable:self.checkAllTT=_('Check all files')self.checkNoneTT=_('Uncheck all files')self.checkAllNoneBtn=QCheckBox()self.checkAllNoneBtn.setToolTip(self.checkAllTT)self.checkAllNoneBtn.stateChanged.connect(self.checkAllNone)self.filelistToolbar=QToolBar(_('Status File List Toolbar'))self.filelistToolbar.setIconSize(QSize(16,16))self.filelistToolbar.setStyleSheet(qtlib.tbstylesheet)hbox.addWidget(self.filelistToolbar)ifself.checkable:self.filelistToolbar.addWidget(qtlib.Spacer(3,2))self.filelistToolbar.addWidget(self.checkAllNoneBtn)self.filelistToolbar.addSeparator()self.filelistToolbar.addWidget(le)self.filelistToolbar.addSeparator()self.filelistToolbar.addWidget(self.statusfilter)self.filelistToolbar.addSeparator()self.filelistToolbar.addWidget(self.refreshBtn)self.actions=wctxactions.WctxActions(self.repo,self)tv=WctxFileTree(self.repo,checkable=checkable)vbox.addLayout(hbox)vbox.addWidget(tv)split.addWidget(frame)ifself.pats:defclearPattern():self.pats=[]self.refreshWctx()cpb.setVisible(False)self.titleTextChanged.emit(self.getTitle())cpb=QPushButton(_('Remove filter, show root'))vbox.addWidget(cpb)cpb.clicked.connect(clearPattern)tv.setItemsExpandable(False)tv.setRootIsDecorated(False)tv.sortByColumn(COL_STATUS,Qt.AscendingOrder)tv.clicked.connect(self.onRowClicked)tv.doubleClicked.connect(self.onRowDoubleClicked)tv.menuRequest.connect(self.onMenuRequest)le.textEdited.connect(self.setFilter)defstatusTypeTrigger(status):status=str(status)forsinstatusTypes:val=statusTypes[s]self.opts[val.name]=sinstatusself.refreshWctx()self.statusfilter.statusChanged.connect(statusTypeTrigger)self.tv=tvself.le=le# Diff panel side of splittervbox=QVBoxLayout()vbox.setSpacing(0)vbox.setContentsMargins(0,0,0,0)docf=QFrame(split)sp=SP(SP.Expanding,SP.Expanding)sp.setHorizontalStretch(1)sp.setVerticalStretch(0)docf.setSizePolicy(sp)docf.setLayout(vbox)self.docf=docfself.fileview=fileview.HgFileView(self.repo,self)self.fileview.showMessage.connect(self.showMessage)self.fileview.linkActivated.connect(self.linkActivated)self.fileview.fileDisplayed.connect(self.fileDisplayed)self.fileview.shelveToolExited.connect(self.refreshWctx)self.fileview.setContext(self.repo[None])self.fileview.setMinimumSize(QSize(16,16))vbox.addWidget(self.fileview,1)self.split=splitself.diffvbox=vboxdefcheckAllNone(self):state=self.checkAllNoneBtn.checkState()ifstate==Qt.Checked:self.checkAll()self.checkAllNoneBtn.setToolTip(self.checkNoneTT)else:ifstate==Qt.Unchecked:self.checkNone()self.checkAllNoneBtn.setToolTip(self.checkAllTT)ifstate!=Qt.PartiallyChecked:self.checkAllNoneBtn.setTristate(False)defgetTitle(self):ifself.pats:return_('%s - status (selection filtered)')%self.repo.displaynameelse:return_('%s - status')%self.repo.displaynamedefloadSettings(self,qs,prefix):self.fileview.loadSettings(qs,prefix+'/fileview')self.split.restoreState(qs.value(prefix+'/state').toByteArray())defsaveSettings(self,qs,prefix):self.fileview.saveSettings(qs,prefix+'/fileview')qs.setValue(prefix+'/state',self.split.saveState())@pyqtSlot(QPoint,object)defonMenuRequest(self,point,selected):menu=self.actions.makeMenu(selected)ifmenu.exec_(point):self.refreshWctx()defsetPatchContext(self,pctx):ifpctx!=self.pctx:self.savechecks=Falseelse:self.savechecks=Trueself.pctx=pctxdefrefreshWctx(self,synchronous=False):ifself.refthread:returnself.fileview.clearDisplay()# store selected paths or current pathmodel=self.tv.model()ifmodelandmodel.rowCount(QModelIndex()):smodel=self.tv.selectionModel()curidx=smodel.currentIndex()ifcuridx.isValid():curpath=model.getRow(curidx)[COL_PATH]else:curpath=Nonespaths=[model.getRow(i)[COL_PATH]foriinsmodel.selectedRows()]self.reselection=spaths,curpathelse:self.reselection=Noneifself.checkable:self.checkAllNoneBtn.setEnabled(False)self.refreshBtn.setEnabled(False)self.progress.emit(*cmdui.startProgress(_('Refresh'),_('status')))self.refthread=StatusThread(self.repo,self.pctx,self.pats,self.opts)ifnotsynchronous:self.refthread.finished.connect(self.reloadComplete)self.refthread.showMessage.connect(self.showMessage)self.refthread.start()ifsynchronous:self.reloadComplete()defreloadComplete(self):self.refthread.wait()ifself.checkable:self.checkAllNoneBtn.setEnabled(True)self.refreshBtn.setEnabled(True)self.progress.emit(*cmdui.stopProgress(_('Refresh')))ifself.refthread.wctxisnotNone:self.updateModel(self.refthread.wctx,self.refthread.patchecked)self.refthread=NonedefcanExit(self):returnnotself.refthreaddefupdateModel(self,wctx,patchecked):self.tv.setSortingEnabled(False)ifself.tv.model():checked=self.tv.model().getChecked()else:checked=patcheckedifself.patsandnotchecked:qtlib.WarningMsgBox(_('No appropriate files'),_('No files found for this operation'),parent=self)ms=merge.mergestate(self.repo)tm=WctxModel(wctx,ms,self.pctx,self.savechecks,self.opts,checked,self,checkable=self.checkable)ifself.checkable:tm.checkToggled.connect(self.updateCheckCount)self.tv.setModel(tm)self.tv.setSortingEnabled(True)self.tv.setColumnHidden(COL_PATH,bool(wctx.p2())ornotself.checkable)self.tv.setColumnHidden(COL_MERGE_STATE,nottm.anyMerge())ifself.checkable:self.updateCheckCount()forcolin(COL_PATH,COL_STATUS,COL_MERGE_STATE):w=self.tv.sizeHintForColumn(col)self.tv.setColumnWidth(col,w)forcolin(COL_PATH_DISPLAY,COL_EXTENSION,COL_SIZE):self.tv.resizeColumnToContents(col)# reset selection, or select first rowcuridx=tm.index(0,0)selmodel=self.tv.selectionModel()flags=QItemSelectionModel.Select|QItemSelectionModel.Rowsifself.reselection:selected,current=self.reselectionfori,rowinenumerate(tm.getAllRows()):ifrow[COL_PATH]inselected:selmodel.select(tm.index(i,0),flags)ifrow[COL_PATH]==current:curidx=tm.index(i,0)else:selmodel.select(curidx,flags)selmodel.currentChanged.connect(self.onCurrentChange)selmodel.selectionChanged.connect(self.onSelectionChange)ifcuridxandcuridx.isValid():selmodel.setCurrentIndex(curidx,QItemSelectionModel.Current)self.onSelectionChange(None,None)# Disabled decorator because of bug in older PyQt releases#@pyqtSlot(QModelIndex)defonRowClicked(self,index):'tree view emitted a clicked signal, index guarunteed valid'ifindex.column()==COL_PATH:self.tv.model().toggleRows([index])# Disabled decorator because of bug in older PyQt releases#@pyqtSlot(QModelIndex)defonRowDoubleClicked(self,index):'tree view emitted a doubleClicked signal, index guarunteed valid'path,status,mst,u,ext,sz=self.tv.model().getRow(index) if status in 'MAR!':
self.actions.allactions[0].trigger()
elif status == 'S':
- self.linkActivated.emit(u'subrepo:'+hglib.tounicode(path))
+ self.linkActivated.emit(
+u'subrepo:'+hglib.tounicode(self.repo.wjoin(path))) @pyqtSlot(QString)
def setFilter(self, match):
model=self.tv.model()ifmodel:model.setFilter(match)defupdateCheckCount(self):model=self.tv.model()ifmodel:model.checkCount=len(self.getChecked())ifmodel.checkCount==0:state=Qt.Uncheckedelifmodel.checkCount==len(model.rows):state=Qt.Checkedelse:state=Qt.PartiallyCheckedself.checkAllNoneBtn.setCheckState(state)defcheckAll(self):model=self.tv.model()ifmodel:model.checkAll(True)defcheckNone(self):model=self.tv.model()ifmodel:model.checkAll(False)defgetChecked(self,types=None):model=self.tv.model()ifmodel:checked=model.getChecked()iftypesisNone:return[fforf,vinchecked.iteritems()ifv]else:files=[]forrowinmodel.getAllRows():path,status,mst,upath,ext,sz=rowifstatusintypesandchecked[path]:files.append(path)returnfileselse:return[]# Disabled decorator because of bug in older PyQt releases#@pyqtSlot(QItemSelection, QItemSelection)defonSelectionChange(self,selected,deselected):selrows=[]forindexinself.tv.selectedRows():path,status,mst,u,ext,sz=self.tv.model().getRow(index)selrows.append((set(status+mst.lower()),path))self.actions.updateActionSensitivity(selrows)# Disabled decorator because of bug in older PyQt releases#@pyqtSlot(QModelIndex, QModelIndex)defonCurrentChange(self,index,old):'Connected to treeview "currentChanged" signal'row=index.model().getRow(index)ifrowisNone:returnpath,status,mst,upath,ext,sz=rowwfile=util.pconvert(path)pctx=self.pctxandself.pctx.p1()orNoneself.fileview.setContext(self.repo[None],pctx)self.fileview.displayFile(wfile,status)classStatusThread(QThread):'''Background thread for generating a workingctx'''showMessage=pyqtSignal(QString)def__init__(self,repo,pctx,pats,opts,parent=None):super(StatusThread,self).__init__()self.repo=hg.repository(repo.ui,repo.root)self.pctx=pctxself.pats=patsself.opts=optsself.wctx=Noneself.patchecked={}defrun(self):self.repo.dirstate.invalidate()extract=lambdax,y:dict(zip(x,map(y.get,x)))stopts=extract(('unknown','ignored','clean'),self.opts)patchecked={}try:ifself.pats:ifself.opts.get('checkall'):# quickop sets this flag to pre-check even !?IC filesprecheckfn=lambdax:Trueelse:# status and commit only pre-check MAR filesprecheckfn=lambdax:x<4m=hglib.match(self.repo[None],self.pats)status=self.repo.status(match=m,**stopts)# Record all matched files as initially checkedfori,statinenumerate(StatusType.preferredOrder):ifstat=='S':continueval=statusTypes[stat]ifself.opts[val.name]:d=dict([(fn,precheckfn(i))forfninstatus[i]])patchecked.update(d)wctx=context.workingctx(self.repo,changes=status)self.patchecked=patcheckedelifself.pctx:status=self.repo.status(node1=self.pctx.p1().node(),**stopts)wctx=context.workingctx(self.repo,changes=status)else:wctx=self.repo[None]wctx.status(**stopts)self.wctx=wctxwctx.dirtySubrepos=[]forsinwctx.substate:ifwctx.sub(s).dirty():wctx.dirtySubrepos.append(s)exceptEnvironmentError,e:self.showMessage.emit(hglib.tounicode(str(e)))except(error.LookupError,error.RepoError,error.ConfigError),e:self.showMessage.emit(hglib.tounicode(str(e)))exceptutil.Abort,e:ife.hint:err=_('%s (hint: %s)')%(hglib.tounicode(str(e)),hglib.tounicode(e.hint))else:err=hglib.tounicode(str(e))self.showMessage.emit(err)classWctxFileTree(QTreeView):menuRequest=pyqtSignal(QPoint,object)def__init__(self,repo,parent=None,checkable=True):QTreeView.__init__(self,parent)self.repo=repoself.setSelectionMode(QTreeView.ExtendedSelection)self.setContextMenuPolicy(Qt.CustomContextMenu)self.customContextMenuRequested.connect(self.menuRequested)self.setTextElideMode(Qt.ElideLeft)defscrollTo(self,index,hint=QAbstractItemView.EnsureVisible):# don't update horizontal position by selection changeorighoriz=self.horizontalScrollBar().value()super(WctxFileTree,self).scrollTo(index,hint)self.horizontalScrollBar().setValue(orighoriz)defkeyPressEvent(self,event):ifevent.key()==32:self.model().toggleRows(self.selectedRows())returnsuper(WctxFileTree,self).keyPressEvent(event)defdragObject(self):urls=[]forindexinself.selectedRows():path=self.model().getRow(index)[COL_PATH]urls.append(QUrl.fromLocalFile(self.repo.wjoin(path)))ifurls:drag=QDrag(self)data=QMimeData()data.setUrls(urls)drag.setMimeData(data)drag.start(Qt.CopyAction)defmousePressEvent(self,event):self.pressPos=event.pos()self.pressTime=QTime.currentTime()returnQTreeView.mousePressEvent(self,event)defmouseMoveEvent(self,event):d=event.pos()-self.pressPosifd.manhattanLength()<QApplication.startDragDistance():returnQTreeView.mouseMoveEvent(self,event)elapsed=self.pressTime.msecsTo(QTime.currentTime())ifelapsed<QApplication.startDragTime():returnQTreeView.mouseMoveEvent(self,event)self.dragObject()returnQTreeView.mouseMoveEvent(self,event)defmenuRequested(self,point):selrows=[]forindexinself.selectedRows():path,status,mst,u,ext,sz=self.model().getRow(index)selrows.append((set(status+mst.lower()),path))ifselrows:self.menuRequest.emit(self.viewport().mapToGlobal(point),selrows)defselectedRows(self):ifself.selectionModel():returnself.selectionModel().selectedRows()# Invalid selectionModel foundreturn[]classWctxModel(QAbstractTableModel):checkToggled=pyqtSignal()def__init__(self,wctx,ms,pctx,savechecks,opts,checked,parent,checkable=True):QAbstractTableModel.__init__(self,parent)self.checkCount=0rows=[]nchecked={}excludes=[f.strip()forfinopts.get('ciexclude','').split(',')]defmkrow(fname,st):ext,sizek='',''try:mst=fnameinmsandms[fname].upper()or""name,ext=os.path.splitext(fname)sizebytes=wctx[fname].size()sizek=(sizebytes+1023)//1024exceptEnvironmentError:passreturn[fname,st,mst,hglib.tounicode(fname),ext[1:],sizek]ifnotsavechecks:checked={}ifpctx:# Currently, having a patch context means it's a qrefresh, so only# auto-check files in pctx.files()pctxfiles=pctx.files()pctxmatch=lambdaf:finpctxfileselse:pctxmatch=lambdaf:Trueifopts['modified']:forminwctx.modified():nchecked[m]=checked.get(m,mnotinexcludesandpctxmatch(m))rows.append(mkrow(m,'M'))ifopts['added']:forainwctx.added():nchecked[a]=checked.get(a,anotinexcludesandpctxmatch(a))rows.append(mkrow(a,'A'))ifopts['removed']:forrinwctx.removed():mst=rinmsandms[r].upper()or""nchecked[r]=checked.get(r,rnotinexcludesandpctxmatch(r))rows.append(mkrow(r,'R'))ifopts['deleted']:fordinwctx.deleted():mst=dinmsandms[d].upper()or""nchecked[d]=checked.get(d,dnotinexcludesandpctxmatch(d))rows.append(mkrow(d,'!'))ifopts['unknown']:foruinwctx.unknown()or[]:nchecked[u]=checked.get(u,False)rows.append(mkrow(u,'?'))ifopts['ignored']:foriinwctx.ignored()or[]:nchecked[i]=checked.get(i,False)rows.append(mkrow(i,'I'))ifopts['clean']:forcinwctx.clean()or[]:nchecked[c]=checked.get(c,False)rows.append(mkrow(c,'C'))ifopts['subrepo']:forsinwctx.dirtySubrepos:nchecked[s]=checked.get(s,True)rows.append(mkrow(s,'S'))# include clean unresolved filesforfinms:ifms[f]=='u'andfnotinnchecked:nchecked[f]=checked.get(f,True)rows.append(mkrow(f,'C'))self.headers=('*',_('Stat'),_('M'),_('Filename'),_('Type'),_('Size (KB)'))self.checked=ncheckedself.unfiltered=rowsself.rows=rowsself.checkable=checkabledefrowCount(self,parent):ifparent.isValid():return0# no childreturnlen(self.rows)defcheckAll(self,state):fordatainself.rows:self.checked[data[0]]=stateself.layoutChanged.emit()self.checkToggled.emit()defcolumnCount(self,parent):ifparent.isValid():return0# no childreturnlen(self.headers)defdata(self,index,role):ifnotindex.isValid():returnQVariant()path,status,mst,upath,ext,sz=self.rows[index.row()]ifindex.column()==COL_PATH:ifrole==Qt.CheckStateRoleandself.checkable:# also Qt.PartiallyCheckedifself.checked[path]:returnQt.Checkedelse:returnQt.Uncheckedelifrole==Qt.DisplayRole:returnQVariant("")elifrole==Qt.ToolTipRole:returnQVariant(_('Checked count: %d')%self.checkCount)elifrole==Qt.DisplayRole:returnQVariant(self.rows[index.row()][index.column()])elifrole==Qt.TextColorRole:ifmst:return_colors.get(mst.lower(),QColor('black'))else:return_colors.get(status,QColor('black'))elifrole==Qt.ToolTipRole:returnQVariant(statusMessage(status,mst,upath))''' elif role == Qt.DecorationRole and index.column() == COL_STATUS: if status in statusTypes: ico = QIcon() ico.addPixmap(QPixmap('icons/' + statusTypes[status].icon)) return QVariant(ico) '''returnQVariant()defheaderData(self,col,orientation,role):ifrole!=Qt.DisplayRoleororientation!=Qt.Horizontal:returnQVariant()else:returnQVariant(self.headers[col])defflags(self,index):flags=Qt.ItemIsSelectable|Qt.ItemIsEnabled|Qt.ItemIsDragEnabledifindex.column()==COL_PATHandself.checkable:flags|=Qt.ItemIsUserCheckablereturnflags# Custom methodsdefanyMerge(self):forrinself.rows:ifr[COL_MERGE_STATE]:returnTruereturnFalsedefgetRow(self,index):assertindex.isValid()returnself.rows[index.row()]defgetAllRows(self):forrowinself.rows:yieldrowdeftoggleRows(self,indexes):'Connected to "activated" signal, emitted by dbl-click or enter'ifQApplication.keyboardModifiers()&Qt.ControlModifier:# ignore Ctrl-Enter events, the user does not want a row# toggled just as they are committing.returnself.layoutAboutToBeChanged.emit()forindexinindexes:assertindex.isValid()fname=self.rows[index.row()][COL_PATH]self.checked[fname]=notself.checked[fname]self.layoutChanged.emit()self.checkToggled.emit()defsort(self,col,order):self.layoutAboutToBeChanged.emit()defgetStatusRank(value):"""Helper function used to sort items according to their hg status Statuses are ranked in the following order: 'S','M','A','R','!','?','C','I','' """sortList=['S','M','A','R','!','?','C','I','']try:rank=sortList.index(value)except(IndexError,ValueError):rank=len(shortList)# Set the lowest rank by defaultreturnrankdefgetMergeStatusRank(value):"""Helper function used to sort according to item merge status Merge statuses are ranked in the following order: 'S','U','R','' """sortList=['S','U','R','']try:rank=sortList.index(value)except(IndexError,ValueError):rank=len(shortList)# Set the lowest rank by defaultreturnrank# We want to sort the list by one of the columns (checked state,# mercurial status, file path, file extension, etc)# However, for files which have the same status or extension, etc,# we want them to be sorted alphabetically (without taking into account# the case)# Since Python 2.3 the sort function is guaranteed to be stable.# Thus we can perform the sort in two passes:# 1.- Perform a secondary sort by path# 2.- Perform a primary sort by the actual column that we are sorting on# Secondary sort:self.rows.sort(lambdax,y: \
cmp(x[COL_PATH].lower(),y[COL_PATH].lower()))ifcol==COL_PATH_DISPLAY:# Already sorted!passelse:iforder==Qt.DescendingOrder:# We want the secondary sort to be by _ascending_ path,# even when the primary sort is in descending orderself.rows.reverse()# Now we can perform the primary sortifcol==COL_PATH:c=self.checkedself.rows.sort(lambdax,y:cmp(c[x[col]],c[y[col]]))elifcol==COL_STATUS:self.rows.sort(key=lambdax:getStatusRank(x[col]))elifcol==COL_MERGE_STATE:self.rows.sort(key=lambdax:getMergeStatusRank(x[col]))else:self.rows.sort(lambdax,y:cmp(x[col],y[col]))iforder==Qt.DescendingOrder:self.rows.reverse()self.layoutChanged.emit()self.reset()defsetFilter(self,match):'simple match in filename filter'self.layoutAboutToBeChanged.emit()self.rows=[rforrinself.unfilteredifmatchinr[COL_PATH_DISPLAY]]self.layoutChanged.emit()self.reset()defgetChecked(self):returnself.checked.copy()defstatusMessage(status,mst,upath):tip=''ifstatusinstatusTypes:upath="<span style='font-family:Courier'>%s </span>"%upathtip=statusTypes[status].desc%upathifmst=='R':tip+=_(', resolved merge')elifmst=='U':tip+=_(', unresolved merge')returntipclassStatusType(object):preferredOrder='MAR!?ICS'def__init__(self,name,icon,desc,uilabel,trname):self.name=nameself.icon=iconself.desc=descself.uilabel=uilabelself.trname=trnamestatusTypes={'M':StatusType('modified','menucommit.ico',_('%s is modified'),'status.modified',_('modified')),'A':StatusType('added','fileadd.ico',_('%s is added'),'status.added',_('added')),'R':StatusType('removed','filedelete.ico',_('%s is removed'),'status.removed',_('removed')),'?':StatusType('unknown','shelve.ico',_('%s is not tracked (unknown)'),'status.unknown',_('unknown')),'!':StatusType('deleted','menudelete.ico',_('%s is missing!'),'status.deleted',_('deleted')),'I':StatusType('ignored','ignore.ico',_('%s is ignored'),'status.ignored',_('ignored')),'C':StatusType('clean','',_('%s is not modified (clean)'),'status.clean',_('clean')),'S':StatusType('subrepo','thg-subrepo.ico',_('%s is a dirty subrepo'),'status.subrepo',_('subrepo')),}classStatusFilterButton(QToolButton):"""Button with drop-down menu for status filter"""statusChanged=pyqtSignal(str)def__init__(self,statustext,types=None,parent=None,**kwargs):self._TYPES='MARSC'iftypesisnotNone:self._TYPES=types#if 'text' not in kwargs:# kwargs['text'] = _('Status')super(StatusFilterButton,self).__init__(parent,popupMode=QToolButton.MenuButtonPopup,icon=qtlib.geticon('hg-status'),toolButtonStyle=Qt.ToolButtonTextBesideIcon,**kwargs)self.clicked.connect(self.showMenu)self._initactions(statustext)def_initactions(self,text):self._actions={}menu=QMenu(self)forcinself._TYPES:st=statusTypes[c]a=menu.addAction('%s%s'%(c,st.trname))a.setCheckable(True)a.setChecked(cintext)a.toggled.connect(self._update)self._actions[c]=aself.setMenu(menu)@pyqtSlot()def_update(self):self.statusChanged.emit(self.status())defstatus(self):"""Return the text for status filter"""return''.join(cforcinself._TYPESifself._actions[c].isChecked())@pyqtSlot(str)defsetStatus(self,text):"""Set the status text""" assert util.all(c in self._TYPES for c in text)
for c in self._TYPES:
self._actions[c].setChecked(c in text)
-+class StatusDialog(QDialog):
'Standalone status browser'
def __init__(self, repo, pats, opts, parent=None):
QDialog.__init__(self,parent)self.setWindowIcon(qtlib.geticon('hg-status'))layout=QVBoxLayout()layout.setContentsMargins(0,0,0,0)self.setLayout(layout)toplayout=QVBoxLayout()toplayout.setContentsMargins(10,10,10,0);self.stwidget=StatusWidget(repo,pats,opts,self,checkable=False)toplayout.addWidget(self.stwidget,1)layout.addLayout(toplayout)self.statusbar=cmdui.ThgStatusBar(self)layout.addWidget(self.statusbar)self.stwidget.showMessage.connect(self.statusbar.showMessage)self.stwidget.progress.connect(self.statusbar.progress)self.stwidget.titleTextChanged.connect(self.setWindowTitle)self.stwidget.linkActivated.connect(self.linkActivated)self.setWindowTitle(self.stwidget.getTitle())self.setWindowFlags(Qt.Window)self.loadSettings()QShortcut(QKeySequence.Refresh,self,self.stwidget.refreshWctx)QTimer.singleShot(0,self.stwidget.refreshWctx)deflinkActivated(self,link):link=hglib.fromunicode(link)iflink.startswith('subrepo:'):fromtortoisehg.hgqt.runimportqtrunfromtortoisehg.hgqtimportcommitqtrun(commit.run,self.commit.repo.ui,root=link[8:])iflink.startswith('shelve:'):fromtortoisehg.hgqtimportshelvedlg=shelve.ShelveDialog(self.stwidget.repo,self)dlg.finished.connect(dlg.deleteLater)dlg.exec_()self.refresh()defloadSettings(self):s=QSettings()self.stwidget.loadSettings(s,'status')self.restoreGeometry(s.value('status/geom').toByteArray())defsaveSettings(self):s=QSettings()self.stwidget.saveSettings(s,'status')s.setValue('status/geom',self.saveGeometry())defaccept(self):ifnotself.stwidget.canExit():returnself.saveSettings()QDialog.accept(self)defreject(self):ifnotself.stwidget.canExit():returnself.saveSettings()QDialog.reject(self)defrun(ui,*pats,**opts):fromtortoisehg.utilimportpathsfromtortoisehg.hgqtimportthgreporepo=thgrepo.repository(ui,path=paths.find_root())pats=hglib.canonpaths(pats)os.chdir(repo.root)returnStatusDialog(repo,pats,opts)
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.