Changeset e012e20ee740…
Parent 52dc83472776…
by
Changes to 4 files · Browse files at e012e20ee740 Showing diff from parent 52dc83472776 Diff from another changeset...
|
|
@@ -0,0 +1,154 @@ + <?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32"
+ height="32"
+ id="svg2939"
+ version="1.1"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="extensions.svg">
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="19.9375"
+ inkscape:cx="31.544811"
+ inkscape:cy="16.093898"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1600"
+ inkscape:window-height="839"
+ inkscape:window-x="-9"
+ inkscape:window-y="-9"
+ inkscape:window-maximized="1" />
+ <title
+ id="title3001">Extensions</title>
+ <defs
+ id="defs2941">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective2947" />
+ <inkscape:perspective
+ id="perspective3909"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3933"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <metadata
+ id="metadata2944">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" />
+ <dc:title>Extensions</dc:title>
+ <dc:date>2010-11-13</dc:date>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Johan Samyn</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:description>Extensions icon for TortoiseHg</dc:description>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://creativecommons.org/licenses/by-sa/3.0/">
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#Reproduction" />
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#Distribution" />
+ <cc:requires
+ rdf:resource="http://creativecommons.org/ns#Notice" />
+ <cc:requires
+ rdf:resource="http://creativecommons.org/ns#Attribution" />
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
+ <cc:requires
+ rdf:resource="http://creativecommons.org/ns#ShareAlike" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-57.714276,-795.50496)">
+ <path
+ sodipodi:type="arc"
+ style="fill:#048a00;fill-opacity:1;stroke:#ff0707;stroke-width:29.30873511;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3889"
+ sodipodi:cx="228.57143"
+ sodipodi:cy="525.2193"
+ sodipodi:rx="171.42857"
+ sodipodi:ry="335.71429"
+ d="m 400,525.2193 a 171.42857,335.71429 0 1 1 -342.857147,-2e-5"
+ sodipodi:start="0"
+ sodipodi:end="3.1415927"
+ sodipodi:open="true"
+ transform="matrix(0,0.0859087,-0.05420367,0,105.63702,791.89438)" />
+ <path
+ sodipodi:type="arc"
+ id="path3895"
+ style="fill:#000000;stroke:none"
+ sodipodi:cx="-38.955654"
+ sodipodi:cy="833.62408"
+ sodipodi:rx="1.5"
+ sodipodi:ry="1.5"
+ d="m -37.455654,833.62408 a 1.5,1.5 0 1 1 -3,0 1.5,1.5 0 1 1 3,0 z" />
+ <rect
+ style="fill:#fdff13;fill-opacity:1;stroke:#010101;stroke-width:1.46622348;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3897"
+ width="8.2754679"
+ height="9.1848412"
+ x="77.855682"
+ y="800.79346" />
+ <rect
+ style="fill:#fdff13;fill-opacity:1;stroke:#010101;stroke-width:1.67026925;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3899"
+ width="9.3480644"
+ height="3.9821112"
+ x="68.029686"
+ y="803.21313" />
+ <rect
+ style="fill:#13c6ff;fill-opacity:1;stroke:#010101;stroke-width:1.50000000000000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3899-1"
+ width="7.0148916"
+ height="7.9028859"
+ x="77.890648"
+ y="814.0813" />
+ <rect
+ style="fill:#13c5ff;fill-opacity:1;stroke:#010101;stroke-width:1.64584875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3899-7"
+ width="9.0213881"
+ height="4.0065312"
+ x="67.82428"
+ y="815.92175" />
+ </g>
+</svg>
|
|
|
@@ -21,13 +21,18 @@
# Technical Debt
# stacked widget or pages need to be scrollable
-# add extensions page after THG 1.1 is released
# we need a consistent icon set
# connect to thgrepo.configChanged signal and refresh
_unspecstr = _('<unspecified>')
ENTRY_WIDTH = 300
+try:
+ from iniparse.config import Undefined
+except ImportError:
+ class Undefined(object):
+ pass
+
class SettingsCombo(QComboBox):
def __init__(self, parent=None, **opts):
QComboBox.__init__(self, parent)
@@ -191,6 +196,33 @@ def isDirty(self):
return self.value() != self.curvalue
+class SettingsCheckBox(QCheckBox):
+ def __init__(self, parent=None, **opts):
+ QCheckBox.__init__(self, parent)
+ self.opts = opts
+ self.curvalue = None
+ self.setText(opts['label'])
+ self.valfunc = self.opts['valfunc']
+ self.toggled.connect(lambda: self.valfunc(self, self.curvalue))
+
+ def focusInEvent(self, e):
+ self.opts['descwidget'].setHtml(self.opts['tooltip'])
+ QCheckBox.focusInEvent(self, e)
+
+ def setFocus(self):
+ pass
+
+ def setValue(self, curvalue):
+ self.curvalue = curvalue
+ self.setChecked(curvalue)
+
+ def value(self):
+ return self.isChecked()
+
+ def isDirty(self):
+ return self.isChecked() != self.curvalue
+
+
def genEditCombo(opts, defaults=[]):
opts['canedit'] = True
opts['defaults'] = defaults
@@ -233,6 +265,10 @@def findMergeTools():
return hglib.mergetools(ui.ui())
+def genCheckBox(opts):
+ opts['nohist'] = True
+ return SettingsCheckBox(**opts)
+
INFO = (
({'name': 'general', 'label': 'TortoiseHg', 'icon': 'thg_logo'}, (
(_('Three-way Merge Tool'), 'ui.merge',
@@ -488,6 +524,9 @@ _('Font used to display changelog data. Default: monospace 10')),
)),
+({'name': 'extensions', 'label': _('Extensions'), 'icon': 'extensions'}, (
+ )),
+
({'name': 'reviewboard', 'label': _('Review Board'), 'icon': 'reviewboard'}, (
(_('Server'), 'reviewboard.server', genEditCombo,
_('Path to review board'
@@ -644,6 +683,7 @@ self.pages = {}
self.stack = stack
self.pageList = pageList
+ self.extspagewidgets = ()
desctext = QTextBrowser()
desctext.setOpenExternalLinks(True)
@@ -687,15 +727,21 @@ self.readonly = not (hasattr(self.ini, 'write') and os.access(self.fn, os.W_OK))
self.stack.setDisabled(self.readonly)
self.fnedit.setText(hglib.tounicode(self.fn))
- for info, widgets in self.pages.values():
- for row, (label, cpath, values, tooltip) in enumerate(info):
- curvalue = self.readCPath(cpath)
- widgets[row].setValue(curvalue)
+ for name, info, widgets in self.pages.values():
+ if name == 'extensions':
+ enabledexts = hglib.enabledextensions()
+ for row, w in enumerate(widgets):
+ curvalue = (w.opts['label'] in enabledexts)
+ w.setValue(curvalue)
+ else:
+ for row, (label, cpath, values, tooltip) in enumerate(info):
+ curvalue = self.readCPath(cpath)
+ widgets[row].setValue(curvalue)
def isDirty(self):
if self.readonly:
return False
- for info, widgets in self.pages.values():
+ for name, info, widgets in self.pages.values():
for w in widgets:
if w.isDirty():
return True
@@ -717,8 +763,8 @@ for n, (label, cpath, values, tip) in enumerate(info):
if cpath == focusfield:
self.pageList.setCurrentRow(i)
- QTimer.singleShot(0, lambda: \
- self.pages[meta['name']][1][n].setFocus())
+ QTimer.singleShot(0, lambda:
+ self.pages[meta['name']][2][n].setFocus())
return
def fillFrame(self, info):
@@ -744,6 +790,29 @@ widgets.append(w)
return widgets
+ def fillExtensionsFrame(self):
+ widgets = []
+ frame = QFrame()
+ grid = QGridLayout()
+ frame.setLayout(grid)
+ self.stack.addWidget(frame)
+ allexts = hglib.allextensions()
+ allextslist = list(allexts)
+ MAXCOLUMNS = 3
+ maxrows = (len(allextslist) + MAXCOLUMNS - 1) / MAXCOLUMNS
+ i = 0
+ extsinfo = ()
+ for i, name in enumerate(sorted(allexts)):
+ tt = allexts[name]
+ opts = {'label':name, 'cpath':'extensions.' + name, 'tooltip':tt,
+ 'descwidget':self.desctext,
+ 'valfunc':self.validateextensions}
+ w = genCheckBox(opts)
+ row, col = i / maxrows, i % maxrows
+ grid.addWidget(w, col, row)
+ widgets.append(w)
+ return extsinfo, widgets
+
def eventFilter(self, obj, event):
if event.type() == QEvent.Enter:
self.desctext.setHtml(obj.tooltip)
@@ -754,8 +823,13 @@ if name == data[0]['name']:
meta, info = data
break
- widgets = self.fillFrame(info)
- self.pages[name] = info, widgets
+ if name == 'extensions':
+ extsinfo, widgets = self.fillExtensionsFrame()
+ self.pages[name] = name, extsinfo, widgets
+ self.extspagewidgets = self.pages[name]
+ else:
+ widgets = self.fillFrame(info)
+ self.pages[name] = name, info, widgets
def readCPath(self, cpath):
'Retrieve a value from the parsed config file'
@@ -804,10 +878,13 @@ if self.readonly:
return
- for info, widgets in self.pages.values():
- for row, (label, cpath, values, tip) in enumerate(info):
- newvalue = widgets[row].value()
- self.recordNewValue(cpath, newvalue)
+ for name, info, widgets in self.pages.values():
+ if name == 'extensions':
+ self.applyChangesForExtensions()
+ else:
+ for row, (label, cpath, values, tip) in enumerate(info):
+ newvalue = widgets[row].value()
+ self.recordNewValue(cpath, newvalue)
try:
wconfig.writefile(self.ini, self.fn)
@@ -815,6 +892,64 @@ qtlib.WarningMsgBox(_('Unable to write configuration file'),
str(e), parent=self)
+ def applyChangesForExtensions(self):
+ section = 'extensions'
+ enabledexts = hglib.enabledextensions()
+ for chk in self.extspagewidgets[2]:
+ key = chk.opts['label']
+ newvalue = chk.value()
+ if newvalue and (key in enabledexts):
+ continue # unchanged
+ if newvalue:
+ self.ini.set(section, key, '')
+ else:
+ for cand in (key, 'hgext.%s' % key, 'hgext/%s' % key):
+ try:
+ del self.ini[section][cand]
+ except KeyError:
+ pass
+
+ def validateextensions(self, widget, curvalue):
+ section = 'extensions'
+ enabledexts = hglib.enabledextensions()
+ selectedexts = ()
+ for chk in self.extspagewidgets[2]:
+ if chk.isChecked():
+ selectedexts += (chk.opts['label'],)
+ invalidexts = hglib.validateextensions(selectedexts)
+
+ def getinival(name):
+ if section not in self.ini:
+ return None
+ for cand in (name, 'hgext.%s' % name, 'hgext/%s' % name):
+ try:
+ v = self.ini[section][cand]
+ if not isinstance(v, Undefined):
+ return v
+ except KeyError:
+ pass
+
+ def changable(name):
+ curval = getinival(name)
+ if curval not in ('', None):
+ # enabled or unspecified, official extensions only
+ return False
+ elif name in enabledexts and curval is None:
+ # re-disabling ext is not supported
+ return False
+ elif name in invalidexts and name not in selectedexts:
+ # disallow to enable bad exts, but allow to disable it
+ return False
+ else:
+ return True
+
+ allexts = hglib.allextensions()
+ for chk in self.extspagewidgets[2]:
+ name = chk.opts['label']
+ chk.setEnabled(changable(name))
+ chk.setToolTip(invalidexts.get(name) or hglib.toutf(allexts[name]))
+
+
def run(ui, *pats, **opts):
return SettingsDialog(opts.get('alias') == 'repoconfig',
focus=opts.get('focus'))
|
@@ -51,5 +51,6 @@ <file>icons/qpop.svg</file>
<file>icons/reviewboard.png</file>
<file>icons/bookmark.png</file>
+ <file>icons/extensions.svg</file>
</qresource>
</RCC>
|
@@ -271,12 +271,19 @@ if 'mq' in repo.__dict__: #do not create if it does not exist
repo.mq.invalidate()
+def enabledextensions():
+ """Return the {name: shortdesc} dict of enabled extensions
+
+ shortdesc is in local encoding.
+ """
+ return extensions.enabled()[0]
+
def allextensions():
"""Return the {name: shortdesc} dict of known extensions
shortdesc is in local encoding.
"""
- enabledexts = extensions.enabled()[0]
+ enabledexts = enabledextensions()
disabledexts = extensions.disabled()[0]
exts = (disabledexts or {}).copy()
exts.update(enabledexts)
|
Loading...