Status: | Rejected |
---|---|
Rejected by: | Gordon Tyler |
Proposed branch: | lp:~doxxx/qbzr/mergetools |
Merge into: | lp:qbzr |
Diff against target: |
926 lines (+564/-205) 5 files modified
NEWS.txt (+2/-0) lib/config.py (+254/-99) lib/conflicts.py (+42/-106) lib/ui_merge_config.py (+97/-0) ui/merge_config.ui (+169/-0) |
To merge this branch: | bzr merge lp:~doxxx/qbzr/mergetools |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gary van der Merwe | Needs Fixing | ||
Review via email: mp+38664@code.launchpad.net |
Commit message
(doxxx) Use bzrlib.mergetools for managing and using external merge tools in qconfig and qconflicts.
Description of the change
This merge proposal changes qconfig and qconflicts to make use of the bzrlib.mergetools module added by lp:~doxxx/bzr/mergetools. It uses the API to display and edit the list of external merge tools in the Merge tab of qconfig, to display a dropdown selector of an external merge tool in qconflicts and to invoke the selected external merge tool from qconflicts.
Gordon Tyler (doxxx) wrote : | # |
Gordon Tyler (doxxx) wrote : | # |
Could I get some feedback on this? Any suggestions for how to better handle notification of problems with the selected merge tool in qconflicts?
- 1313. By Gordon Tyler
-
Updated for API change in bzrlib.mergetools.
- 1314. By Gordon Tyler
-
Use missing gettext call for a user-visible string.
- 1315. By Gordon Tyler
-
Replaced Merge config tab contents with new UI.
Gordon Tyler (doxxx) wrote : | # |
I've redesigned the UI for the Merge tab in qconfig to allow editing the name and commandline of merge tools.
- 1316. By Gary van der Merwe
-
qconfig: Don't crash if no default is set.
Gary van der Merwe (garyvdm) wrote : | # |
Sorry about the delay in looking at this. Been busy at work.
Thank you for tackling this.
* qconfig and qconflicts break if using using a old bzr. I would like to keep some level of backwards compatibility. So if running an old bzr, qconfig should load, but the merge tab should show a error message. qconflicts should load, show conflicts, but you won't be able to launch a ext merges app.
* I would prefer for the qconfig merge tab to be a simple list, like the diff tab, rather than the list + form. My reasons for this are:
+ It's easier to code. With the list + form code, you have to do a lot of work to synchronize the selection in the list, and the form item displayed. (see bug below.)
+ Less widgets = Cleaner look.
Anyway - this is bike shedding. So if you disagree, I don't mind. What I do want is for the diff and merge tabs to share the same design, and code.
* I would be nice to give help on then merge parameters (%b %T %o, etc..). Maybe a link to qhelp, like the qgetnew dialog.
* First time I ran qconfig, I got the following error, because I did not have a default set.
bzr: ERROR: exceptions.
Traceback (most recent call last):
File "/home/
ret_code = self._qbzr_
File "/home/
window = QBzrConfigWindow()
File "/home/
return obj(*args, **kwargs)
File "/home/
self.load()
File "/home/
self.
File "/home/
self._default = [mt for mt in self._merge_tools if mt == default][0]
IndexError: list index out of range
I've fixed this and pushed to lp:~qbzr-dev/qbzr/mergetools. I've added you to ~qbzr-dev, so if you want, you can also push there.
* In qconfig, if you click on then ... button for the command line, and click cancel, it changes the command line. It should not.
* I saw this bug: steps to reproduce:
Click on a merge tool.
Click detect. (There is now no selection in the list box, but the form on the left stays populated.)
Edit the command line.
press tab (or otherwise let the command line text box lose focus.)
Actual result: get this error:
bzr: ERROR: exceptions.
Traceback (most recent call last):
File "/home/
assert sel_model.
AssertionError
* The qconficts code seems pretty solid.
I've been wanting to change qconficts to use the treewidget for a while. I'll see if I can work on it shortly.
I also want to make a merge tool menu, like then diff tool menu, so you can select the merge tool in qcommit, qbrowse, be wt view, etc.
- 1317. By Gary van der Merwe
-
Merge Trunk.
Gordon Tyler (doxxx) wrote : | # |
> Sorry about the delay in looking at this. Been busy at work.
No worries, thanks for the review.
> * qconfig and qconflicts break if using using a old bzr. I would like to keep
> some level of backwards compatibility. So if running an old bzr, qconfig
> should load, but the merge tab should show a error message. qconflicts should
> load, show conflicts, but you won't be able to launch a ext merges app.
Okay.
> * I would prefer for the qconfig merge tab to be a simple list, like the diff
> tab, rather than the list + form. My reasons for this are:
> + It's easier to code. With the list + form code, you have to do a lot of
> work to synchronize the selection in the list, and the form item displayed.
> (see bug below.)
> + Less widgets = Cleaner look.
>
> Anyway - this is bike shedding. So if you disagree, I don't mind. What I do
> want is for the diff and merge tabs to share the same design, and code.
I went with a separate list and edit form design because I thought it would be easier to use, which I felt was a drawback of the previous design. For instance, there's no obvious clue that the radio button of the previous design indicates that it marks the default tool.
I will investigate an alternate design using a table though, to see if I can come up with something that is easier to use than the old design and can be re-used for the Diff tab.
> * I would be nice to give help on then merge parameters (%b %T %o, etc..).
> Maybe a link to qhelp, like the qgetnew dialog.
Okay.
> * First time I ran qconfig, I got the following error, because I did not have
> a default set.
> I've fixed this and pushed to lp:~qbzr-dev/qbzr/mergetools. I've added you to
> ~qbzr-dev, so if you want, you can also push there.
Cool. Thanks. Can I just pull/push --remember to that with my current branch and continue working like that? And if I do, should I resubmit this merge proposal using the new branch?
> * In qconfig, if you click on then ... button for the command line, and click
> cancel, it changes the command line. It should not.
Indeed it should not.
> * I saw this bug: steps to reproduce:
> Click on a merge tool.
> Click detect. (There is now no selection in the list box, but the form on the
> left stays populated.)
> Edit the command line.
> press tab (or otherwise let the command line text box lose focus.)
>
> Actual result: get this error:
> bzr: ERROR: exceptions.
>
> Traceback (most recent call last):
> File "/home/
> merge_tool_
> assert sel_model.
> AssertionError
Yeah, that's to catch code paths like the one you discovered when the list of merge tools has no selection but the edit form is still enabled. I'll investigate.
> I've been wanting to change qconficts to use the treewidget for a while. I'll
> see if I can work on it shortly.
You mean a treewidget to show the conflicts in the context of the working tree structure?
> I also want to make a merge tool menu, like then diff tool menu, so you can
> select the merge tool in qcommit, qbrowse, be wt view, etc.
Nice idea.
- 1318. By Gordon Tyler
-
Fixed the other code site which modifies self._default.
- 1319. By Gordon Tyler
-
Improved how detected tools are added to the list so that its selection is not cleared.
Gary van der Merwe (garyvdm) wrote : | # |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On 09/11/2010 01:46, Gordon Tyler wrote:
>> I've fixed this and pushed to lp:~qbzr-dev/qbzr/mergetools. I've added you to
>> ~qbzr-dev, so if you want, you can also push there.
>
> Cool. Thanks. Can I just pull/push --remember to that with my current
> branch and continue working like that? And if I do, should I
> resubmit this merge proposal using the new branch?
Yes. Then it makes it easier for me to provide fixes.
>> I've been wanting to change qconficts to use the treewidget for a while. I'll
>> see if I can work on it shortly.
>
> You mean a treewidget to show the conflicts in the context of the working tree structure?
Yes - like qcommit/
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://
iEYEARECAAYFAkz
9QYAniJ50ZS/
=svmx
-----END PGP SIGNATURE-----
Gordon Tyler (doxxx) wrote : | # |
Unmerged revisions
Preview Diff
1 | === modified file 'NEWS.txt' |
2 | --- NEWS.txt 2010-11-08 14:03:03 +0000 |
3 | +++ NEWS.txt 2010-11-09 00:10:03 +0000 |
4 | @@ -24,6 +24,8 @@ |
5 | (Bug #621934) |
6 | * qcommit: added option to load commit message from a file. |
7 | (Bug #640071, Philip Peitsch) |
8 | + * Use bzrlib.mergetools for managing and using external merge tools in qconfig |
9 | + and qconflicts. (Bug #489915, Gordon Tyler) |
10 | |
11 | |
12 | 0.19.3 - Under Development |
13 | |
14 | === modified file 'lib/config.py' |
15 | --- lib/config.py 2010-09-06 06:56:31 +0000 |
16 | +++ lib/config.py 2010-11-09 00:10:03 +0000 |
17 | @@ -20,12 +20,14 @@ |
18 | import re |
19 | import os.path |
20 | from PyQt4 import QtCore, QtGui |
21 | +from PyQt4.Qt import Qt |
22 | from bzrlib.config import ( |
23 | ensure_config_dir_exists, |
24 | extract_email_address, |
25 | ) |
26 | -from bzrlib import errors, trace |
27 | +from bzrlib import errors, mergetools, trace |
28 | |
29 | +from bzrlib.plugins.qbzr.lib import ui_merge_config |
30 | from bzrlib.plugins.qbzr.lib.i18n import gettext, N_ |
31 | from bzrlib.plugins.qbzr.lib.spellcheck import SpellChecker |
32 | from bzrlib.plugins.qbzr.lib.util import ( |
33 | @@ -188,34 +190,48 @@ |
34 | diffLayout.addWidget(self.extDiffList) |
35 | diffLayout.addLayout(extDiffButtonsLayout) |
36 | |
37 | + self.merge_ui = ui_merge_config.Ui_MergeConfig() |
38 | mergeWidget = QtGui.QWidget() |
39 | - |
40 | - label = QtGui.QLabel(gettext("External Merge Apps:")) |
41 | - self.extMergeList = QtGui.QTreeWidget(mergeWidget) |
42 | - self.extMergeList.setRootIsDecorated(False) |
43 | - self.extMergeList.setHeaderLabels([gettext("Definition")]) |
44 | - self.extMergeList.setItemDelegateForColumn(0, |
45 | - QRadioCheckItemDelegate(self.extMergeList)) |
46 | - self.connect(self.extMergeList, QtCore.SIGNAL("itemChanged (QTreeWidgetItem *,int)"), |
47 | - self.extMergeListItemChanged) |
48 | - |
49 | - addExtMergeButton = QtGui.QPushButton(gettext("Add"), mergeWidget) |
50 | - self.connect(addExtMergeButton, QtCore.SIGNAL("clicked()"), |
51 | - self.addExtMerge) |
52 | - removeExtMergeButton = QtGui.QPushButton(gettext("Remove"), mergeWidget) |
53 | - self.connect(removeExtMergeButton, QtCore.SIGNAL("clicked()"), |
54 | - self.removeExtMerge) |
55 | - |
56 | - extMergeButtonsLayout = QtGui.QHBoxLayout() |
57 | - extMergeButtonsLayout.addWidget(addExtMergeButton) |
58 | - extMergeButtonsLayout.addWidget(removeExtMergeButton) |
59 | - extMergeButtonsLayout.addStretch() |
60 | - |
61 | - mergeLayout = QtGui.QVBoxLayout(mergeWidget) |
62 | - mergeLayout.addWidget(label) |
63 | - mergeLayout.addWidget(self.extMergeList) |
64 | - mergeLayout.addLayout(extMergeButtonsLayout) |
65 | - |
66 | + self.merge_ui.setupUi(mergeWidget) |
67 | + self.merge_ui.merge_tool_details.setEnabled(False) |
68 | + |
69 | + self.merge_tools_list_model = MergeToolsListModel() |
70 | + self.merge_ui.merge_tools_list.setModel( |
71 | + self.merge_tools_list_model) |
72 | + |
73 | + self.connect(self.merge_ui.merge_tools_list.selectionModel(), |
74 | + QtCore.SIGNAL("currentChanged(QModelIndex,QModelIndex)"), |
75 | + self.merge_tools_list_currentChanged) |
76 | + self.connect(self.merge_tools_list_model, |
77 | + QtCore.SIGNAL("dataChanged(QModelIndex,QModelIndex)"), |
78 | + self.merge_tools_list_dataChanged) |
79 | + |
80 | + self.connect(self.merge_ui.merge_tools_add, |
81 | + QtCore.SIGNAL("clicked()"), |
82 | + self.merge_tools_add_clicked) |
83 | + self.connect(self.merge_ui.merge_tools_remove, |
84 | + QtCore.SIGNAL("clicked()"), |
85 | + self.merge_tools_remove_clicked) |
86 | + self.connect(self.merge_ui.merge_tools_detect, |
87 | + QtCore.SIGNAL("clicked()"), |
88 | + self.merge_tools_detect_clicked) |
89 | + |
90 | + self.connect(self.merge_ui.merge_tool_name, |
91 | + QtCore.SIGNAL("editingFinished()"), |
92 | + self.merge_tool_name_changed) |
93 | + |
94 | + self.connect(self.merge_ui.merge_tool_commandline, |
95 | + QtCore.SIGNAL("editingFinished()"), |
96 | + self.merge_tool_commandline_changed) |
97 | + |
98 | + self.connect(self.merge_ui.merge_tool_default, |
99 | + QtCore.SIGNAL("toggled(bool)"), |
100 | + self.merge_tool_default_toggled) |
101 | + |
102 | + self.connect(self.merge_ui.merge_tool_browse, |
103 | + QtCore.SIGNAL("clicked()"), |
104 | + self.merge_tool_browse_clicked) |
105 | + |
106 | self.tabwidget.addTab(generalWidget, gettext("General")) |
107 | self.tabwidget.addTab(aliasesWidget, gettext("Aliases")) |
108 | self.tabwidget.addTab(bugTrackersWidget, gettext("Bug Trackers")) |
109 | @@ -357,30 +373,10 @@ |
110 | self.extDiffListIgnore = False |
111 | |
112 | # Merge |
113 | - bzr_config = get_global_config() |
114 | - defaultMerge = bzr_config.get_user_option("external_merge") |
115 | - if defaultMerge is None: |
116 | - defaultMerge = "" |
117 | - |
118 | - self.extMergeListIgnore = True |
119 | - def create_ext_merge_item(definition): |
120 | - item = QtGui.QTreeWidgetItem(self.extMergeList) |
121 | - item.setFlags(QtCore.Qt.ItemIsSelectable | |
122 | - QtCore.Qt.ItemIsEditable | |
123 | - QtCore.Qt.ItemIsEnabled | |
124 | - QtCore.Qt.ItemIsUserCheckable) |
125 | - if definition == defaultMerge: |
126 | - item.setCheckState(0, QtCore.Qt.Checked) |
127 | - else: |
128 | - item.setCheckState(0, QtCore.Qt.Unchecked) |
129 | - |
130 | - item.setText(0, definition) |
131 | - return item |
132 | - |
133 | - for name, value in parser.get('DEFAULT', {}).items(): |
134 | - if name == "external_merge": |
135 | - create_ext_merge_item(value) |
136 | - self.extMergeListIgnore = False |
137 | + definedMergeTools = mergetools.get_merge_tools(config) |
138 | + defaultMergeTool = mergetools.get_default_merge_tool(config) |
139 | + self.merge_tools_list_model.set_merge_tools(definedMergeTools, defaultMergeTool) |
140 | + self.merge_tools_list_model.sort(0, Qt.AscendingOrder) |
141 | |
142 | def save(self): |
143 | """Save the configuration.""" |
144 | @@ -465,26 +461,20 @@ |
145 | qconfig.set_option('default_diff', |
146 | defaultDiff) |
147 | |
148 | - # Merge |
149 | - defaultMerge = None |
150 | - for index in range(self.extMergeList.topLevelItemCount()): |
151 | - item = self.extMergeList.topLevelItem(index) |
152 | - definition = unicode(item.text(0)) |
153 | - if item.checkState(0) == QtCore.Qt.Checked: |
154 | - defaultMerge = definition |
155 | - |
156 | - set_or_delete_option(parser, 'external_merge', |
157 | - defaultMerge) |
158 | |
159 | def save_config(config, parser): |
160 | - ensure_config_dir_exists(os.path.dirname(config._get_filename())) |
161 | - f = open(config._get_filename(), 'wb') |
162 | + ensure_config_dir_exists(os.path.dirname(config.file_name)) |
163 | + f = open(config.file_name, 'wb') |
164 | parser.write(f) |
165 | f.close() |
166 | |
167 | save_config(config, parser) |
168 | qconfig.save() |
169 | |
170 | + # Merge |
171 | + mergetools.set_merge_tools(self.merge_tools_list_model.get_merge_tools()) |
172 | + mergetools.set_default_merge_tool(self.merge_tools_list_model.get_default()) |
173 | + |
174 | def do_accept(self): |
175 | """Save changes and close the window.""" |
176 | if not self.validate(): |
177 | @@ -578,42 +568,80 @@ |
178 | changed_item.setCheckState(0, QtCore.Qt.Checked) |
179 | self.extDiffListIgnore = False |
180 | |
181 | - def addExtMerge(self): |
182 | - item = QtGui.QTreeWidgetItem(self.extMergeList) |
183 | - item.setFlags(QtCore.Qt.ItemIsSelectable | |
184 | - QtCore.Qt.ItemIsEditable | |
185 | - QtCore.Qt.ItemIsEnabled | |
186 | - QtCore.Qt.ItemIsUserCheckable) |
187 | - item.setCheckState(0, QtCore.Qt.Unchecked) |
188 | - self.extMergeList.setCurrentItem(item) |
189 | - self.extMergeList.editItem(item, 0) |
190 | - |
191 | - def removeExtMerge(self): |
192 | - for item in self.extMergeList.selectedItems(): |
193 | - index = self.extMergeList.indexOfTopLevelItem(item) |
194 | - self.extMergeList.takeTopLevelItem(index) |
195 | - |
196 | - def extMergeListItemChanged(self, changed_item, col): |
197 | - if col == 0 and not self.extMergeListIgnore: |
198 | - checked_count = 0 |
199 | - for index in range(self.extMergeList.topLevelItemCount()): |
200 | - item = self.extMergeList.topLevelItem(index) |
201 | - if item.checkState(0) == QtCore.Qt.Checked: |
202 | - checked_count += 1 |
203 | + def merge_tools_list_currentChanged(self, curr, prev): |
204 | + mt = self.merge_tools_list_model.get_merge_tool(curr) |
205 | + self.merge_ui.merge_tool_name.setText(mt.get_name()) |
206 | + self.merge_ui.merge_tool_commandline.setText( |
207 | + mt.get_commandline(quote=True)) |
208 | + default = self.merge_tools_list_model.get_default() |
209 | + self.merge_ui.merge_tool_default.blockSignals(True) |
210 | + self.merge_ui.merge_tool_default.setChecked(default is mt) |
211 | + self.merge_ui.merge_tool_default.blockSignals(False) |
212 | + self.merge_ui.merge_tool_details.setEnabled(True) |
213 | + |
214 | + def merge_tools_list_dataChanged(self, first, last): |
215 | + sel_model = self.merge_ui.merge_tools_list.selectionModel() |
216 | + assert sel_model.hasSelection() |
217 | + curr = sel_model.currentIndex() |
218 | + if curr >= first and curr <= last: |
219 | + self.merge_tools_list_currentChanged(curr, None) |
220 | + |
221 | + def merge_tools_add_clicked(self): |
222 | + index = self.merge_tools_list_model.new_merge_tool() |
223 | + sel_model = self.merge_ui.merge_tools_list.selectionModel() |
224 | + sel_model.setCurrentIndex(index, QtGui.QItemSelectionModel.ClearAndSelect) |
225 | + self.merge_ui.merge_tool_name.setFocus(Qt.OtherFocusReason) |
226 | + self.merge_ui.merge_tool_name.selectAll() |
227 | + |
228 | + def merge_tools_remove_clicked(self): |
229 | + sel_model = self.merge_ui.merge_tools_list.selectionModel() |
230 | + assert sel_model.hasSelection() |
231 | + self.merge_tools_list_model.remove_merge_tool(sel_model.currentIndex()) |
232 | + |
233 | + def merge_tools_detect_clicked(self): |
234 | + self.merge_tools_list_model.detect_merge_tools() |
235 | + |
236 | + def merge_tool_name_changed(self): |
237 | + sel_model = self.merge_ui.merge_tools_list.selectionModel() |
238 | + assert sel_model.hasSelection() |
239 | + text = unicode(self.merge_ui.merge_tool_name.text()) |
240 | + self.merge_tools_list_model.set_merge_tool_name( |
241 | + sel_model.currentIndex(), text) |
242 | + self.merge_tools_list_model.sort(0, Qt.AscendingOrder) |
243 | + |
244 | + def merge_tool_commandline_changed(self): |
245 | + sel_model = self.merge_ui.merge_tools_list.selectionModel() |
246 | + assert sel_model.hasSelection() |
247 | + curr = sel_model.currentIndex() |
248 | + mt = self.merge_tools_list_model.get_merge_tool(curr) |
249 | + text = unicode(self.merge_ui.merge_tool_commandline.text()) |
250 | + mt.set_commandline(text) |
251 | + |
252 | + def merge_tool_default_toggled(self, checked): |
253 | + sel_model = self.merge_ui.merge_tools_list.selectionModel() |
254 | + assert sel_model.hasSelection() |
255 | + if checked: |
256 | + new_default = self.merge_tools_list_model.get_merge_tool( |
257 | + sel_model.currentIndex()) |
258 | + if self.merge_tools_list_model.get_default() is not new_default: |
259 | + self.merge_tools_list_model.set_default(new_default) |
260 | + else: |
261 | + self.merge_tools_list_model.set_default(None) |
262 | |
263 | - if checked_count == 0: |
264 | - self.extMergeListIgnore = True |
265 | - changed_item.setCheckState(0, QtCore.Qt.Checked) |
266 | - self.extMergeListIgnore = False |
267 | - elif checked_count > 1: |
268 | - self.extMergeListIgnore = True |
269 | - for index in range(self.extMergeList.topLevelItemCount()): |
270 | - item = self.extMergeList.topLevelItem(index) |
271 | - if item.checkState(0) == QtCore.Qt.Checked: |
272 | - item.setCheckState(0, QtCore.Qt.Unchecked) |
273 | - changed_item.setCheckState(0, QtCore.Qt.Checked) |
274 | - self.extMergeListIgnore = False |
275 | - |
276 | + def merge_tool_browse_clicked(self): |
277 | + sel_model = self.merge_ui.merge_tools_list.selectionModel() |
278 | + assert sel_model.hasSelection() |
279 | + self.merge_tool_commandline_changed() # force update of model |
280 | + filename = QtGui.QFileDialog.getOpenFileName(self, |
281 | + gettext('Select merge tool executable'), |
282 | + '/') |
283 | + if filename is not None: |
284 | + curr = sel_model.currentIndex() |
285 | + mt = self.merge_tools_list_model.get_merge_tool(curr) |
286 | + mt.set_executable(unicode(filename)) |
287 | + self.merge_ui.merge_tool_commandline.setText( |
288 | + mt.get_commandline(quote=True)) |
289 | + |
290 | def browseEditor(self): |
291 | filename = QtGui.QFileDialog.getOpenFileName(self, |
292 | gettext('Select editor executable'), |
293 | @@ -698,3 +726,130 @@ |
294 | |
295 | import socket |
296 | return realname, (username + '@' + socket.gethostname()) |
297 | + |
298 | +class MergeToolsListModel(QtCore.QAbstractListModel): |
299 | + def __init__(self): |
300 | + super(MergeToolsListModel, self).__init__() |
301 | + self._merge_tools = [] |
302 | + self._default = None |
303 | + |
304 | + def get_merge_tools(self): |
305 | + return self._merge_tools |
306 | + |
307 | + def set_merge_tools(self, merge_tools, default): |
308 | + self.beginResetModel() |
309 | + self._merge_tools = merge_tools |
310 | + # see set_default for explanation |
311 | + for mt in self._merge_tools: |
312 | + if mt == default: |
313 | + self._default = mt |
314 | + break |
315 | + self.endResetModel() |
316 | + |
317 | + def get_default(self): |
318 | + return self._default |
319 | + |
320 | + def set_default(self, new_default): |
321 | + old_index = None |
322 | + if self._default is not None: |
323 | + i = self._merge_tools.index(self._default) |
324 | + old_index = self.createIndex(i, i) |
325 | + new_index = None |
326 | + if new_default is not None: |
327 | + i = self._merge_tools.index(new_default) |
328 | + new_index = self.createIndex(i, i) |
329 | + # new_default may be == to an instance in self._merge_tools but not |
330 | + # the same instance. To preserve editing semantics in this model, |
331 | + # self._default must refer to an instance in self._merge_tools, so |
332 | + # we find the == instance in self._merge_tools and set self._default |
333 | + # to that instead. |
334 | + for mt in self._merge_tools: |
335 | + if mt == default: |
336 | + self._default = mt |
337 | + break |
338 | + else: |
339 | + self._default = None |
340 | + if old_index: |
341 | + self.emit(QtCore.SIGNAL("dataChanged(QModelIndex,QModelIndex)"), |
342 | + old_index, old_index) |
343 | + if new_index: |
344 | + self.emit(QtCore.SIGNAL("dataChanged(QModelIndex,QModelIndex)"), |
345 | + new_index, new_index) |
346 | + |
347 | + def get_merge_tool(self, index): |
348 | + return self._merge_tools[index.row()] |
349 | + |
350 | + def index_of(self, tool): |
351 | + return self.createIndex(self._merge_tools.index(tool), 0) |
352 | + |
353 | + def new_merge_tool(self): |
354 | + index = self.createIndex(len(self._merge_tools), 0) |
355 | + self.beginInsertRows(QtCore.QModelIndex(), index.row(), index.row()) |
356 | + self._merge_tools.append(mergetools.MergeTool( |
357 | + gettext('New Merge Tool'), '')) |
358 | + self.endInsertRows() |
359 | + return index |
360 | + |
361 | + def remove_merge_tool(self, index): |
362 | + self.beginRemoveRows(QtCore.QModelIndex(), index.row(), index.row()) |
363 | + if self._merge_tools[index.row()] == self._default: |
364 | + self._default = None |
365 | + del self._merge_tools[index.row()] |
366 | + self.endRemoveRows() |
367 | + |
368 | + def detect_merge_tools(self): |
369 | + detected_tools = mergetools.detect_merge_tools() |
370 | + for mt in detected_tools: |
371 | + if mt not in self._merge_tools: |
372 | + row = len(self._merge_tools) |
373 | + self.beginInsertRows(QtCore.QModelIndex(), row, row) |
374 | + self._merge_tools.append(mt) |
375 | + self.endInsertRows() |
376 | + self.sort(0, Qt.AscendingOrder) |
377 | + |
378 | + def set_merge_tool_name(self, index, name): |
379 | + self._merge_tools[index.row()].set_name(name) |
380 | + self.emit(QtCore.SIGNAL("dataChanged(QModelIndex,QModelIndex)"), index, |
381 | + index) |
382 | + |
383 | + def set_merge_tool_commandline(self, index, commandline): |
384 | + self._merge_tools[index.row()].set_commandline(commandline) |
385 | + |
386 | + def set_merge_tool_executable(self, index, executable): |
387 | + self._merge_tools[index.row()].set_executable(executable) |
388 | + |
389 | + def rowCount(self, parent): |
390 | + return len(self._merge_tools) |
391 | + |
392 | + def data(self, index, role): |
393 | + if role == Qt.DisplayRole: |
394 | + name = self._merge_tools[index.row()].get_name() |
395 | + if self._default is not None and self._default.get_name() == name: |
396 | + name = '%s (%s)' % (name, gettext('default')) |
397 | + return QtCore.QVariant(name) |
398 | + elif role == Qt.EditRole: |
399 | + return QtCore.QVariant(self._merge_tools[index.row()].get_name()) |
400 | + return QtCore.QVariant() |
401 | + |
402 | + def setData(self, index, value, role): |
403 | + if role == Qt.EditRole: |
404 | + self._merge_tools[index.row()].set_name(unicode(value.toString())) |
405 | + self.emit(QtCore.SIGNAL("dataChanged(QModelIndex,QModelIndex)"), |
406 | + index,index) |
407 | + self.sort(0, Qt.AscendingOrder) |
408 | + return True |
409 | + return False |
410 | + |
411 | + def flags(self, index): |
412 | + return super(MergeToolsListModel, self).flags(index) | Qt.ItemIsEditable |
413 | + |
414 | + def sort(self, column, sortOrder): |
415 | + self.emit(QtCore.SIGNAL("layoutAboutToBeChanged()")) |
416 | + index_map = self._merge_tools[:] # copy |
417 | + self._merge_tools.sort() |
418 | + for i in range(0, len(index_map)): |
419 | + index_map[i] = self._merge_tools.index(index_map[i]) |
420 | + from_list = [self.createIndex(i, 0) for i in index_map] |
421 | + to_list = [self.createIndex(i, 0) for i in range(0, len(index_map))] |
422 | + self.changePersistentIndexList(from_list, to_list) |
423 | + self.emit(QtCore.SIGNAL("layoutChanged()")) |
424 | |
425 | === modified file 'lib/conflicts.py' |
426 | --- lib/conflicts.py 2010-09-01 07:30:29 +0000 |
427 | +++ lib/conflicts.py 2010-11-09 00:10:03 +0000 |
428 | @@ -20,6 +20,7 @@ |
429 | from PyQt4 import QtCore, QtGui |
430 | from bzrlib.config import GlobalConfig |
431 | from bzrlib.conflicts import resolve |
432 | +from bzrlib import mergetools |
433 | from bzrlib.workingtree import WorkingTree |
434 | from bzrlib.plugins.qbzr.lib.i18n import gettext, N_, ngettext |
435 | from bzrlib.plugins.qbzr.lib.util import ( |
436 | @@ -58,7 +59,7 @@ |
437 | self.connect( |
438 | self.conflicts_list.selectionModel(), |
439 | QtCore.SIGNAL("selectionChanged(QItemSelection, QItemSelection)"), |
440 | - self.update_selection) |
441 | + self.update_merge_tool_ui) |
442 | self.connect( |
443 | self.conflicts_list, |
444 | QtCore.SIGNAL("customContextMenuRequested(QPoint)"), |
445 | @@ -77,22 +78,14 @@ |
446 | vbox.addWidget(self.conflicts_list) |
447 | |
448 | hbox = QtGui.QHBoxLayout() |
449 | - self.program_edit = QtGui.QLineEdit(self) |
450 | - self.program_edit.setEnabled(False) |
451 | - self.connect( |
452 | - self.program_edit, |
453 | - QtCore.SIGNAL("textChanged(QString)"), |
454 | - self.check_merge_tool_edit) |
455 | - self.program_extmerge_default_button = QtGui.QCheckBox(gettext("Use Configured Default")) |
456 | - self.program_extmerge_default_button.setToolTip(gettext( |
457 | - "The merge tool configured in qconfig under Merge' file.\n" |
458 | - "It follows the convention used in the bzr plugin: extmerge\n" |
459 | - "external_merge = kdiff3 --output %r %b %t %o\n" |
460 | - "%r is output, %b is .BASE, %t is .THIS and %o is .OTHER file.")) |
461 | - self.connect( |
462 | - self.program_extmerge_default_button, |
463 | - QtCore.SIGNAL("clicked()"), |
464 | - self.program_extmerge_default_clicked) |
465 | + self.merge_tools_combo = QtGui.QComboBox(self) |
466 | + self.merge_tools_combo.setEditable(False) |
467 | + self.connect(self.merge_tools_combo, |
468 | + QtCore.SIGNAL("currentIndexChanged(int)"), |
469 | + self.update_merge_tool_ui) |
470 | + |
471 | + self.merge_tool_error = QtGui.QLabel('', self) |
472 | + |
473 | self.program_launch_button = QtGui.QPushButton(gettext("&Launch..."), self) |
474 | self.program_launch_button.setEnabled(False) |
475 | self.connect( |
476 | @@ -100,10 +93,11 @@ |
477 | QtCore.SIGNAL("clicked()"), |
478 | self.launch_merge_tool) |
479 | self.program_label = QtGui.QLabel(gettext("M&erge tool:"), self) |
480 | - self.program_label.setBuddy(self.program_edit) |
481 | + self.program_label.setBuddy(self.merge_tools_combo) |
482 | hbox.addWidget(self.program_label) |
483 | - hbox.addWidget(self.program_edit) |
484 | - hbox.addWidget(self.program_extmerge_default_button) |
485 | + hbox.addWidget(self.merge_tools_combo) |
486 | + hbox.addWidget(self.merge_tool_error) |
487 | + hbox.addStretch(1) |
488 | hbox.addWidget(self.program_launch_button) |
489 | vbox.addLayout(hbox) |
490 | |
491 | @@ -125,21 +119,13 @@ |
492 | self.initialize_ui() |
493 | |
494 | def initialize_ui(self): |
495 | - merge_tool_extmerge = get_qbzr_config().get_option("merge_tool_extmerge") |
496 | - |
497 | - self.program_extmerge_default_button.setCheckState(QtCore.Qt.Unchecked) |
498 | - if merge_tool_extmerge in ("True", "1"): |
499 | - self.program_extmerge_default_button.setCheckState(QtCore.Qt.Checked) |
500 | - self.program_extmerge_default_clicked() |
501 | - enabled, error_msg = self.is_merge_tool_launchable() |
502 | - self.update_program_edit_text(enabled, error_msg) |
503 | - # if extmerge not configured then resort to using default |
504 | - if not enabled and self.program_extmerge_default_button.isChecked(): |
505 | - bzr_config = GlobalConfig() |
506 | - extmerge_tool = bzr_config.get_user_option("external_merge") |
507 | - if not extmerge_tool: |
508 | - self.program_extmerge_default_button.setCheckState(QtCore.Qt.Unchecked) |
509 | - self.update_program_edit_text(False, "") |
510 | + defined_tools = mergetools.get_merge_tools() |
511 | + default_tool = mergetools.get_default_merge_tool() |
512 | + for merge_tool in defined_tools: |
513 | + self.merge_tools_combo.insertItem(self.merge_tools_combo.count(), merge_tool.get_name()) |
514 | + if default_tool is not None: |
515 | + self.merge_tools_combo.setCurrentIndex(self.merge_tools_combo.findText(default_tool.get_name())) |
516 | + self.update_merge_tool_ui() |
517 | |
518 | def create_context_menu(self): |
519 | self.context_menu = QtGui.QMenu(self.conflicts_list) |
520 | @@ -190,56 +176,31 @@ |
521 | gettext('&OK')) |
522 | self.close() |
523 | |
524 | - def update_selection(self, selected, deselected): |
525 | + def update_merge_tool_ui(self): |
526 | enabled, error_msg = self.is_merge_tool_launchable() |
527 | - self.program_edit.setEnabled(enabled and not self.program_extmerge_default_button.isChecked()) |
528 | + self.merge_tool_error.setText(error_msg) |
529 | self.program_launch_button.setEnabled(enabled) |
530 | - self.update_program_edit_text(enabled, error_msg) |
531 | - if enabled and self.program_extmerge_default_button.isChecked(): |
532 | - self.program_edit.setEnabled(False) |
533 | self.merge_action.setEnabled(enabled) |
534 | |
535 | - def check_merge_tool_edit(self, text): |
536 | - enabled, error_msg = self.is_merge_tool_launchable() |
537 | - self.program_launch_button.setEnabled(enabled) |
538 | - |
539 | def launch_merge_tool(self): |
540 | items = self.conflicts_list.selectedItems() |
541 | enabled, error_msg = self.is_merge_tool_launchable() |
542 | if not enabled: |
543 | return |
544 | - merge_tool = unicode(self.program_edit.text()).strip() |
545 | - if not merge_tool: |
546 | - return |
547 | + merge_tool = mergetools.find_merge_tool(self.merge_tools_combo.currentText()) |
548 | file_id = str(items[0].data(0, QtCore.Qt.UserRole).toString()) |
549 | file_name = self.wt.abspath(self.wt.id2path(file_id)) |
550 | - base_file_name = file_name + ".BASE" |
551 | - this_file_name = file_name + ".THIS" |
552 | - other_file_name = file_name + ".OTHER" |
553 | - new_args = [base_file_name, this_file_name, other_file_name] |
554 | - config = get_qbzr_config() |
555 | - config.set_option("merge_tool_extmerge", False) |
556 | - |
557 | - if self.program_extmerge_default_button.isChecked(): |
558 | - bzr_config = GlobalConfig() |
559 | - extmerge_tool = bzr_config.get_user_option("external_merge") |
560 | - args = cmdline_split(extmerge_tool) |
561 | - new_args = args[1:len(args)] |
562 | - i = 0 |
563 | - while i < len(new_args): |
564 | - new_args[i] = new_args[i].replace('%r', file_name) |
565 | - new_args[i] = new_args[i].replace('%o', other_file_name) |
566 | - new_args[i] = new_args[i].replace('%b', base_file_name) |
567 | - new_args[i] = new_args[i].replace('%t', this_file_name) |
568 | - i = i + 1 |
569 | - merge_tool = args[0] |
570 | - config.set_option("merge_tool_extmerge", True) |
571 | - else: |
572 | - config.set_option("merge_tool", merge_tool) |
573 | - |
574 | process = QtCore.QProcess(self) |
575 | - self.connect(process, QtCore.SIGNAL("error(QProcess::ProcessError)"), self.show_merge_tool_error) |
576 | - process.start(merge_tool, new_args) |
577 | + def qprocess_invoker(executable, args, cleanup): |
578 | + def qprocess_error(error): |
579 | + self.show_merge_tool_error(error) |
580 | + cleanup(process.exitCode()) |
581 | + def qprocess_finished(exit_code, exit_status): |
582 | + cleanup(exit_code) |
583 | + self.connect(process, QtCore.SIGNAL("error(QProcess::ProcessError)"), qprocess_error) |
584 | + self.connect(process, QtCore.SIGNAL("finished(int,QProcess::ExitStatus)"), qprocess_finished) |
585 | + process.start(executable, args) |
586 | + merge_tool.invoke(file_name, qprocess_invoker) |
587 | |
588 | def show_merge_tool_error(self, error): |
589 | msg = gettext("Error while running merge tool (code %d)") % error |
590 | @@ -261,34 +222,20 @@ |
591 | def show_context_menu(self, pos): |
592 | self.context_menu.popup(self.conflicts_list.viewport().mapToGlobal(pos)) |
593 | |
594 | - def program_extmerge_default_clicked(self): |
595 | - enabled, error_msg = self.is_merge_tool_launchable() |
596 | - self.program_edit.setEnabled(enabled and not self.program_extmerge_default_button.isChecked()) |
597 | - self.program_launch_button.setEnabled(enabled) |
598 | - self.update_program_edit_text(enabled, error_msg) |
599 | - config = get_qbzr_config() |
600 | - config.set_option("merge_tool_extmerge", |
601 | - self.program_extmerge_default_button.isChecked()) |
602 | - |
603 | def is_merge_tool_launchable(self): |
604 | items = self.conflicts_list.selectedItems() |
605 | error_msg = "" |
606 | enabled = True |
607 | if len(items) != 1 or items[0].data(1, QtCore.Qt.UserRole).toString() != "text conflict": |
608 | enabled = False |
609 | - |
610 | - # check to see if the extmerge config is correct |
611 | - if self.program_extmerge_default_button.isChecked(): |
612 | - bzr_config = GlobalConfig() |
613 | - extmerge_tool = bzr_config.get_user_option("external_merge") |
614 | - if not extmerge_tool: |
615 | - error_msg = gettext("Set up external_merge app in qconfig under the Merge tab") |
616 | - enabled = False |
617 | - return enabled, error_msg |
618 | - error = self.is_extmerge_definition_valid(False) |
619 | - if len(error) > 0: |
620 | - enabled = False |
621 | - error_msg = error |
622 | + merge_tool = mergetools.find_merge_tool(self.merge_tools_combo.currentText()) |
623 | + if merge_tool is None: |
624 | + error_msg = gettext("Set up external_merge app in qconfig under the Merge tab") |
625 | + enabled = False |
626 | + elif not merge_tool.is_available(): |
627 | + enabled = False |
628 | + error_msg = gettext("External merge tool %(tool)s is not available") % \ |
629 | + { 'tool': merge_tool.get_name() } |
630 | return enabled, error_msg |
631 | |
632 | def is_extmerge_definition_valid(self, showErrorDialog): |
633 | @@ -316,17 +263,6 @@ |
634 | return gettext("Missing the flag: %s. Configure in qconfig under the merge tab.") % flags |
635 | return "" |
636 | |
637 | - def update_program_edit_text(self, enabled, error_msg): |
638 | - if self.program_extmerge_default_button.isChecked(): |
639 | - if enabled or (len(error_msg) <= 0): |
640 | - config = GlobalConfig() |
641 | - extmerge = config.get_user_option("external_merge") |
642 | - self.program_edit.setText(gettext("%s (Configured external merge definition in qconfig)") % extmerge) |
643 | - else: |
644 | - self.program_edit.setText(error_msg) |
645 | - else: |
646 | - config = get_qbzr_config() |
647 | - self.program_edit.setText((config.get_user_option("merge_tool") or "").strip() or "meld") |
648 | |
649 | if 0: |
650 | N_("path conflict") |
651 | |
652 | === added file 'lib/ui_merge_config.py' |
653 | --- lib/ui_merge_config.py 1970-01-01 00:00:00 +0000 |
654 | +++ lib/ui_merge_config.py 2010-11-09 00:10:03 +0000 |
655 | @@ -0,0 +1,97 @@ |
656 | +# -*- coding: utf-8 -*- |
657 | + |
658 | +# Form implementation generated from reading ui file 'ui/merge_config.ui' |
659 | +# |
660 | +# Created: Sun Nov 07 17:00:49 2010 |
661 | +# by: PyQt4 UI code generator 4.7.2 |
662 | +# |
663 | +# WARNING! All changes made in this file will be lost! |
664 | + |
665 | +from PyQt4 import QtCore, QtGui |
666 | +from bzrlib.plugins.qbzr.lib.i18n import gettext |
667 | + |
668 | + |
669 | +class Ui_MergeConfig(object): |
670 | + def setupUi(self, MergeConfig): |
671 | + MergeConfig.setObjectName("MergeConfig") |
672 | + MergeConfig.resize(626, 297) |
673 | + self.verticalLayout_2 = QtGui.QVBoxLayout(MergeConfig) |
674 | + self.verticalLayout_2.setObjectName("verticalLayout_2") |
675 | + self.groupBox = QtGui.QGroupBox(MergeConfig) |
676 | + self.groupBox.setObjectName("groupBox") |
677 | + self.verticalLayout_3 = QtGui.QVBoxLayout(self.groupBox) |
678 | + self.verticalLayout_3.setObjectName("verticalLayout_3") |
679 | + self.splitter = QtGui.QSplitter(self.groupBox) |
680 | + self.splitter.setOrientation(QtCore.Qt.Horizontal) |
681 | + self.splitter.setChildrenCollapsible(False) |
682 | + self.splitter.setObjectName("splitter") |
683 | + self.layoutWidget = QtGui.QWidget(self.splitter) |
684 | + self.layoutWidget.setObjectName("layoutWidget") |
685 | + self.verticalLayout = QtGui.QVBoxLayout(self.layoutWidget) |
686 | + self.verticalLayout.setObjectName("verticalLayout") |
687 | + self.merge_tools_list = QtGui.QListView(self.layoutWidget) |
688 | + self.merge_tools_list.setObjectName("merge_tools_list") |
689 | + self.verticalLayout.addWidget(self.merge_tools_list) |
690 | + self.merge_tools_buttons = QtGui.QWidget(self.layoutWidget) |
691 | + self.merge_tools_buttons.setObjectName("merge_tools_buttons") |
692 | + self.horizontalLayout = QtGui.QHBoxLayout(self.merge_tools_buttons) |
693 | + self.horizontalLayout.setObjectName("horizontalLayout") |
694 | + spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) |
695 | + self.horizontalLayout.addItem(spacerItem) |
696 | + self.merge_tools_add = QtGui.QPushButton(self.merge_tools_buttons) |
697 | + self.merge_tools_add.setObjectName("merge_tools_add") |
698 | + self.horizontalLayout.addWidget(self.merge_tools_add) |
699 | + self.merge_tools_remove = QtGui.QPushButton(self.merge_tools_buttons) |
700 | + self.merge_tools_remove.setObjectName("merge_tools_remove") |
701 | + self.horizontalLayout.addWidget(self.merge_tools_remove) |
702 | + self.merge_tools_detect = QtGui.QPushButton(self.merge_tools_buttons) |
703 | + self.merge_tools_detect.setObjectName("merge_tools_detect") |
704 | + self.horizontalLayout.addWidget(self.merge_tools_detect) |
705 | + spacerItem1 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) |
706 | + self.horizontalLayout.addItem(spacerItem1) |
707 | + self.verticalLayout.addWidget(self.merge_tools_buttons) |
708 | + self.merge_tool_details = QtGui.QWidget(self.splitter) |
709 | + self.merge_tool_details.setMinimumSize(QtCore.QSize(300, 0)) |
710 | + self.merge_tool_details.setObjectName("merge_tool_details") |
711 | + self.gridLayout = QtGui.QGridLayout(self.merge_tool_details) |
712 | + self.gridLayout.setContentsMargins(-1, 0, -1, 0) |
713 | + self.gridLayout.setObjectName("gridLayout") |
714 | + self.label = QtGui.QLabel(self.merge_tool_details) |
715 | + self.label.setObjectName("label") |
716 | + self.gridLayout.addWidget(self.label, 0, 0, 1, 1) |
717 | + self.merge_tool_name = QtGui.QLineEdit(self.merge_tool_details) |
718 | + self.merge_tool_name.setObjectName("merge_tool_name") |
719 | + self.gridLayout.addWidget(self.merge_tool_name, 1, 0, 1, 1) |
720 | + self.label_2 = QtGui.QLabel(self.merge_tool_details) |
721 | + self.label_2.setObjectName("label_2") |
722 | + self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1) |
723 | + self.merge_tool_commandline = QtGui.QLineEdit(self.merge_tool_details) |
724 | + self.merge_tool_commandline.setObjectName("merge_tool_commandline") |
725 | + self.gridLayout.addWidget(self.merge_tool_commandline, 3, 0, 1, 1) |
726 | + self.merge_tool_browse = QtGui.QToolButton(self.merge_tool_details) |
727 | + self.merge_tool_browse.setObjectName("merge_tool_browse") |
728 | + self.gridLayout.addWidget(self.merge_tool_browse, 3, 1, 1, 1) |
729 | + spacerItem2 = QtGui.QSpacerItem(248, 130, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) |
730 | + self.gridLayout.addItem(spacerItem2, 6, 0, 1, 1) |
731 | + self.merge_tool_default = QtGui.QCheckBox(self.merge_tool_details) |
732 | + self.merge_tool_default.setObjectName("merge_tool_default") |
733 | + self.gridLayout.addWidget(self.merge_tool_default, 4, 0, 1, 1) |
734 | + self.verticalLayout_3.addWidget(self.splitter) |
735 | + self.verticalLayout_2.addWidget(self.groupBox) |
736 | + self.label.setBuddy(self.merge_tool_name) |
737 | + self.label_2.setBuddy(self.merge_tool_commandline) |
738 | + |
739 | + self.retranslateUi(MergeConfig) |
740 | + QtCore.QMetaObject.connectSlotsByName(MergeConfig) |
741 | + |
742 | + def retranslateUi(self, MergeConfig): |
743 | + MergeConfig.setWindowTitle(gettext("External Merge Tools")) |
744 | + self.groupBox.setTitle(gettext("External Merge Tools")) |
745 | + self.merge_tools_add.setText(gettext("Add")) |
746 | + self.merge_tools_remove.setText(gettext("Remove")) |
747 | + self.merge_tools_detect.setText(gettext("Detect")) |
748 | + self.label.setText(gettext("Name:")) |
749 | + self.label_2.setText(gettext("Command line:")) |
750 | + self.merge_tool_browse.setText(gettext("...")) |
751 | + self.merge_tool_default.setText(gettext("Default")) |
752 | + |
753 | |
754 | === added file 'ui/merge_config.ui' |
755 | --- ui/merge_config.ui 1970-01-01 00:00:00 +0000 |
756 | +++ ui/merge_config.ui 2010-11-09 00:10:03 +0000 |
757 | @@ -0,0 +1,169 @@ |
758 | +<?xml version="1.0" encoding="UTF-8"?> |
759 | +<ui version="4.0"> |
760 | + <class>MergeConfig</class> |
761 | + <widget class="QWidget" name="MergeConfig"> |
762 | + <property name="geometry"> |
763 | + <rect> |
764 | + <x>0</x> |
765 | + <y>0</y> |
766 | + <width>626</width> |
767 | + <height>297</height> |
768 | + </rect> |
769 | + </property> |
770 | + <property name="windowTitle"> |
771 | + <string>External Merge Tools</string> |
772 | + </property> |
773 | + <layout class="QVBoxLayout" name="verticalLayout_2"> |
774 | + <item> |
775 | + <widget class="QGroupBox" name="groupBox"> |
776 | + <property name="title"> |
777 | + <string>External Merge Tools</string> |
778 | + </property> |
779 | + <layout class="QVBoxLayout" name="verticalLayout_3"> |
780 | + <item> |
781 | + <widget class="QSplitter" name="splitter"> |
782 | + <property name="orientation"> |
783 | + <enum>Qt::Horizontal</enum> |
784 | + </property> |
785 | + <property name="childrenCollapsible"> |
786 | + <bool>false</bool> |
787 | + </property> |
788 | + <widget class="QWidget" name="layoutWidget"> |
789 | + <layout class="QVBoxLayout" name="verticalLayout"> |
790 | + <item> |
791 | + <widget class="QListView" name="merge_tools_list"/> |
792 | + </item> |
793 | + <item> |
794 | + <widget class="QWidget" name="merge_tools_buttons" native="true"> |
795 | + <layout class="QHBoxLayout" name="horizontalLayout"> |
796 | + <item> |
797 | + <spacer name="spacer"> |
798 | + <property name="orientation"> |
799 | + <enum>Qt::Horizontal</enum> |
800 | + </property> |
801 | + <property name="sizeHint" stdset="0"> |
802 | + <size> |
803 | + <width>40</width> |
804 | + <height>20</height> |
805 | + </size> |
806 | + </property> |
807 | + </spacer> |
808 | + </item> |
809 | + <item> |
810 | + <widget class="QPushButton" name="merge_tools_add"> |
811 | + <property name="text"> |
812 | + <string>Add</string> |
813 | + </property> |
814 | + </widget> |
815 | + </item> |
816 | + <item> |
817 | + <widget class="QPushButton" name="merge_tools_remove"> |
818 | + <property name="text"> |
819 | + <string>Remove</string> |
820 | + </property> |
821 | + </widget> |
822 | + </item> |
823 | + <item> |
824 | + <widget class="QPushButton" name="merge_tools_detect"> |
825 | + <property name="text"> |
826 | + <string>Detect</string> |
827 | + </property> |
828 | + </widget> |
829 | + </item> |
830 | + <item> |
831 | + <spacer name="spacer_2"> |
832 | + <property name="orientation"> |
833 | + <enum>Qt::Horizontal</enum> |
834 | + </property> |
835 | + <property name="sizeHint" stdset="0"> |
836 | + <size> |
837 | + <width>40</width> |
838 | + <height>20</height> |
839 | + </size> |
840 | + </property> |
841 | + </spacer> |
842 | + </item> |
843 | + </layout> |
844 | + </widget> |
845 | + </item> |
846 | + </layout> |
847 | + </widget> |
848 | + <widget class="QWidget" name="merge_tool_details" native="true"> |
849 | + <property name="minimumSize"> |
850 | + <size> |
851 | + <width>300</width> |
852 | + <height>0</height> |
853 | + </size> |
854 | + </property> |
855 | + <layout class="QGridLayout" name="gridLayout"> |
856 | + <property name="topMargin"> |
857 | + <number>0</number> |
858 | + </property> |
859 | + <property name="bottomMargin"> |
860 | + <number>0</number> |
861 | + </property> |
862 | + <item row="0" column="0"> |
863 | + <widget class="QLabel" name="label"> |
864 | + <property name="text"> |
865 | + <string>Name:</string> |
866 | + </property> |
867 | + <property name="buddy"> |
868 | + <cstring>merge_tool_name</cstring> |
869 | + </property> |
870 | + </widget> |
871 | + </item> |
872 | + <item row="1" column="0"> |
873 | + <widget class="QLineEdit" name="merge_tool_name"/> |
874 | + </item> |
875 | + <item row="2" column="0"> |
876 | + <widget class="QLabel" name="label_2"> |
877 | + <property name="text"> |
878 | + <string>Command line:</string> |
879 | + </property> |
880 | + <property name="buddy"> |
881 | + <cstring>merge_tool_commandline</cstring> |
882 | + </property> |
883 | + </widget> |
884 | + </item> |
885 | + <item row="3" column="0"> |
886 | + <widget class="QLineEdit" name="merge_tool_commandline"/> |
887 | + </item> |
888 | + <item row="3" column="1"> |
889 | + <widget class="QToolButton" name="merge_tool_browse"> |
890 | + <property name="text"> |
891 | + <string>...</string> |
892 | + </property> |
893 | + </widget> |
894 | + </item> |
895 | + <item row="6" column="0"> |
896 | + <spacer name="spacer_3"> |
897 | + <property name="orientation"> |
898 | + <enum>Qt::Vertical</enum> |
899 | + </property> |
900 | + <property name="sizeHint" stdset="0"> |
901 | + <size> |
902 | + <width>248</width> |
903 | + <height>130</height> |
904 | + </size> |
905 | + </property> |
906 | + </spacer> |
907 | + </item> |
908 | + <item row="4" column="0"> |
909 | + <widget class="QCheckBox" name="merge_tool_default"> |
910 | + <property name="text"> |
911 | + <string>Default</string> |
912 | + </property> |
913 | + </widget> |
914 | + </item> |
915 | + </layout> |
916 | + </widget> |
917 | + </widget> |
918 | + </item> |
919 | + </layout> |
920 | + </widget> |
921 | + </item> |
922 | + </layout> |
923 | + </widget> |
924 | + <resources/> |
925 | + <connections/> |
926 | +</ui> |
Almost ready for review, just need to tweak the way error messages are displayed in qconflicts.