Kiln » TortoiseHg » TortoiseHg
Clone URL:  
Pushed to one repository · View In Graph Contained in 1.9, 1.9.1, and 1.9.2

settings: add extensions page

Changeset e012e20ee740

Parent 52dc83472776

by Johan Samyn

Changes to 4 files · Browse files at e012e20ee740 Showing diff from parent 52dc83472776 Diff from another changeset...

Change 1 of 1 Show Entire File tortoisehg/​hgqt/​icons/​extensions.svg Stacked
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
@@ -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
22
23
24
25
26
27
28
29
30
 
 
 
 
 
 
31
32
33
 
191
192
193
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
195
196
 
233
234
235
 
 
 
 
236
237
238
 
488
489
490
 
 
 
491
492
493
 
644
645
646
 
647
648
649
 
687
688
689
690
691
692
693
 
 
 
 
 
 
 
 
 
 
694
695
696
697
698
 
699
700
701
 
717
718
719
720
721
 
 
722
723
724
 
744
745
746
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
747
748
749
 
754
755
756
757
758
 
 
 
 
 
 
 
759
760
761
 
804
805
806
807
808
809
810
 
 
 
 
 
 
 
811
812
813
 
815
816
817
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
818
819
820
 
21
22
23
 
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
 
265
266
267
268
269
270
271
272
273
274
 
524
525
526
527
528
529
530
531
532
 
683
684
685
686
687
688
689
 
727
728
729
 
 
 
 
730
731
732
733
734
735
736
737
738
739
740
741
742
743
 
744
745
746
747
 
763
764
765
 
 
766
767
768
769
770
 
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
 
823
824
825
 
 
826
827
828
829
830
831
832
833
834
835
 
878
879
880
 
 
 
 
881
882
883
884
885
886
887
888
889
890
 
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
@@ -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
52
53
 
54
55
 
51
52
53
54
55
56
@@ -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
272
273
 
 
 
 
 
 
 
274
275
276
277
278
279
 
280
281
282
 
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
 
286
287
288
289
@@ -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)