Merge lp:~doxxx/qbzr/mergetools into lp:qbzr

Proposed by Gordon Tyler
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
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.

To post a comment you must log in.
Revision history for this message
Gordon Tyler (doxxx) wrote :

Almost ready for review, just need to tweak the way error messages are displayed in qconflicts.

Revision history for this message
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?

lp:~doxxx/qbzr/mergetools updated
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.

Revision history for this message
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.

lp:~doxxx/qbzr/mergetools updated
1316. By Gary van der Merwe

qconfig: Don't crash if no default is set.

Revision history for this message
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.IndexError: list index out of range

Traceback (most recent call last):
  File "/home/garyvdm/qbzr/mergetools/lib/commands.py", line 166, in run
    ret_code = self._qbzr_run(*args, **kwargs)
  File "/home/garyvdm/qbzr/mergetools/lib/commands.py", line 504, in _qbzr_run
    window = QBzrConfigWindow()
  File "/home/garyvdm/bzr/mergetools/bzrlib/lazy_import.py", line 128, in __call__
    return obj(*args, **kwargs)
  File "/home/garyvdm/qbzr/mergetools/lib/config.py", line 247, in __init__
    self.load()
  File "/home/garyvdm/qbzr/mergetools/lib/config.py", line 378, in load
    self.merge_tools_list_model.set_merge_tools(definedMergeTools, defaultMergeTool)
  File "/home/garyvdm/qbzr/mergetools/lib/config.py", line 743, in set_merge_tools
    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.AssertionError:

Traceback (most recent call last):
  File "/home/garyvdm/qbzr/mergetools/lib/config.py", line 606, in merge_tool_name_changed
    assert sel_model.hasSelection()
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.

review: Needs Fixing
lp:~doxxx/qbzr/mergetools updated
1317. By Gary van der Merwe

Merge Trunk.

Revision history for this message
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.AssertionError:
>
> Traceback (most recent call last):
> File "/home/garyvdm/qbzr/mergetools/lib/config.py", line 606, in
> merge_tool_name_changed
> assert sel_model.hasSelection()
> 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.

lp:~doxxx/qbzr/mergetools updated
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.

Revision history for this message
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/qrevet/qadd.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAkzY+Q0ACgkQd/3EdwGKOh2qiQCgg+HP/oPNqu0UC/AkX1ULyqaq
9QYAniJ50ZS/Ak9Ie/iI+qig6zzWmcQN
=svmx
-----END PGP SIGNATURE-----

Revision history for this message
Gordon Tyler (doxxx) wrote :

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'NEWS.txt'
--- NEWS.txt 2010-11-08 14:03:03 +0000
+++ NEWS.txt 2010-11-09 00:10:03 +0000
@@ -24,6 +24,8 @@
24 (Bug #621934)24 (Bug #621934)
25 * qcommit: added option to load commit message from a file.25 * qcommit: added option to load commit message from a file.
26 (Bug #640071, Philip Peitsch)26 (Bug #640071, Philip Peitsch)
27 * Use bzrlib.mergetools for managing and using external merge tools in qconfig
28 and qconflicts. (Bug #489915, Gordon Tyler)
2729
2830
290.19.3 - Under Development310.19.3 - Under Development
3032
=== modified file 'lib/config.py'
--- lib/config.py 2010-09-06 06:56:31 +0000
+++ lib/config.py 2010-11-09 00:10:03 +0000
@@ -20,12 +20,14 @@
20import re20import re
21import os.path21import os.path
22from PyQt4 import QtCore, QtGui22from PyQt4 import QtCore, QtGui
23from PyQt4.Qt import Qt
23from bzrlib.config import (24from bzrlib.config import (
24 ensure_config_dir_exists,25 ensure_config_dir_exists,
25 extract_email_address,26 extract_email_address,
26 )27 )
27from bzrlib import errors, trace28from bzrlib import errors, mergetools, trace
2829
30from bzrlib.plugins.qbzr.lib import ui_merge_config
29from bzrlib.plugins.qbzr.lib.i18n import gettext, N_31from bzrlib.plugins.qbzr.lib.i18n import gettext, N_
30from bzrlib.plugins.qbzr.lib.spellcheck import SpellChecker32from bzrlib.plugins.qbzr.lib.spellcheck import SpellChecker
31from bzrlib.plugins.qbzr.lib.util import (33from bzrlib.plugins.qbzr.lib.util import (
@@ -188,34 +190,48 @@
188 diffLayout.addWidget(self.extDiffList)190 diffLayout.addWidget(self.extDiffList)
189 diffLayout.addLayout(extDiffButtonsLayout)191 diffLayout.addLayout(extDiffButtonsLayout)
190 192
193 self.merge_ui = ui_merge_config.Ui_MergeConfig()
191 mergeWidget = QtGui.QWidget()194 mergeWidget = QtGui.QWidget()
192195 self.merge_ui.setupUi(mergeWidget)
193 label = QtGui.QLabel(gettext("External Merge Apps:"))196 self.merge_ui.merge_tool_details.setEnabled(False)
194 self.extMergeList = QtGui.QTreeWidget(mergeWidget)197
195 self.extMergeList.setRootIsDecorated(False)198 self.merge_tools_list_model = MergeToolsListModel()
196 self.extMergeList.setHeaderLabels([gettext("Definition")])199 self.merge_ui.merge_tools_list.setModel(
197 self.extMergeList.setItemDelegateForColumn(0,200 self.merge_tools_list_model)
198 QRadioCheckItemDelegate(self.extMergeList))201
199 self.connect(self.extMergeList, QtCore.SIGNAL("itemChanged (QTreeWidgetItem *,int)"),202 self.connect(self.merge_ui.merge_tools_list.selectionModel(),
200 self.extMergeListItemChanged) 203 QtCore.SIGNAL("currentChanged(QModelIndex,QModelIndex)"),
201204 self.merge_tools_list_currentChanged)
202 addExtMergeButton = QtGui.QPushButton(gettext("Add"), mergeWidget)205 self.connect(self.merge_tools_list_model,
203 self.connect(addExtMergeButton, QtCore.SIGNAL("clicked()"),206 QtCore.SIGNAL("dataChanged(QModelIndex,QModelIndex)"),
204 self.addExtMerge)207 self.merge_tools_list_dataChanged)
205 removeExtMergeButton = QtGui.QPushButton(gettext("Remove"), mergeWidget)208
206 self.connect(removeExtMergeButton, QtCore.SIGNAL("clicked()"),209 self.connect(self.merge_ui.merge_tools_add,
207 self.removeExtMerge)210 QtCore.SIGNAL("clicked()"),
208211 self.merge_tools_add_clicked)
209 extMergeButtonsLayout = QtGui.QHBoxLayout()212 self.connect(self.merge_ui.merge_tools_remove,
210 extMergeButtonsLayout.addWidget(addExtMergeButton)213 QtCore.SIGNAL("clicked()"),
211 extMergeButtonsLayout.addWidget(removeExtMergeButton)214 self.merge_tools_remove_clicked)
212 extMergeButtonsLayout.addStretch()215 self.connect(self.merge_ui.merge_tools_detect,
213216 QtCore.SIGNAL("clicked()"),
214 mergeLayout = QtGui.QVBoxLayout(mergeWidget)217 self.merge_tools_detect_clicked)
215 mergeLayout.addWidget(label)218
216 mergeLayout.addWidget(self.extMergeList)219 self.connect(self.merge_ui.merge_tool_name,
217 mergeLayout.addLayout(extMergeButtonsLayout)220 QtCore.SIGNAL("editingFinished()"),
218 221 self.merge_tool_name_changed)
222
223 self.connect(self.merge_ui.merge_tool_commandline,
224 QtCore.SIGNAL("editingFinished()"),
225 self.merge_tool_commandline_changed)
226
227 self.connect(self.merge_ui.merge_tool_default,
228 QtCore.SIGNAL("toggled(bool)"),
229 self.merge_tool_default_toggled)
230
231 self.connect(self.merge_ui.merge_tool_browse,
232 QtCore.SIGNAL("clicked()"),
233 self.merge_tool_browse_clicked)
234
219 self.tabwidget.addTab(generalWidget, gettext("General"))235 self.tabwidget.addTab(generalWidget, gettext("General"))
220 self.tabwidget.addTab(aliasesWidget, gettext("Aliases"))236 self.tabwidget.addTab(aliasesWidget, gettext("Aliases"))
221 self.tabwidget.addTab(bugTrackersWidget, gettext("Bug Trackers"))237 self.tabwidget.addTab(bugTrackersWidget, gettext("Bug Trackers"))
@@ -357,30 +373,10 @@
357 self.extDiffListIgnore = False373 self.extDiffListIgnore = False
358374
359 # Merge375 # Merge
360 bzr_config = get_global_config()376 definedMergeTools = mergetools.get_merge_tools(config)
361 defaultMerge = bzr_config.get_user_option("external_merge")377 defaultMergeTool = mergetools.get_default_merge_tool(config)
362 if defaultMerge is None:378 self.merge_tools_list_model.set_merge_tools(definedMergeTools, defaultMergeTool)
363 defaultMerge = ""379 self.merge_tools_list_model.sort(0, Qt.AscendingOrder)
364
365 self.extMergeListIgnore = True
366 def create_ext_merge_item(definition):
367 item = QtGui.QTreeWidgetItem(self.extMergeList)
368 item.setFlags(QtCore.Qt.ItemIsSelectable |
369 QtCore.Qt.ItemIsEditable |
370 QtCore.Qt.ItemIsEnabled |
371 QtCore.Qt.ItemIsUserCheckable)
372 if definition == defaultMerge:
373 item.setCheckState(0, QtCore.Qt.Checked)
374 else:
375 item.setCheckState(0, QtCore.Qt.Unchecked)
376
377 item.setText(0, definition)
378 return item
379
380 for name, value in parser.get('DEFAULT', {}).items():
381 if name == "external_merge":
382 create_ext_merge_item(value)
383 self.extMergeListIgnore = False
384380
385 def save(self):381 def save(self):
386 """Save the configuration."""382 """Save the configuration."""
@@ -465,26 +461,20 @@
465 qconfig.set_option('default_diff',461 qconfig.set_option('default_diff',
466 defaultDiff)462 defaultDiff)
467 463
468 # Merge
469 defaultMerge = None
470 for index in range(self.extMergeList.topLevelItemCount()):
471 item = self.extMergeList.topLevelItem(index)
472 definition = unicode(item.text(0))
473 if item.checkState(0) == QtCore.Qt.Checked:
474 defaultMerge = definition
475
476 set_or_delete_option(parser, 'external_merge',
477 defaultMerge)
478 464
479 def save_config(config, parser):465 def save_config(config, parser):
480 ensure_config_dir_exists(os.path.dirname(config._get_filename()))466 ensure_config_dir_exists(os.path.dirname(config.file_name))
481 f = open(config._get_filename(), 'wb')467 f = open(config.file_name, 'wb')
482 parser.write(f)468 parser.write(f)
483 f.close()469 f.close()
484 470
485 save_config(config, parser)471 save_config(config, parser)
486 qconfig.save()472 qconfig.save()
487473
474 # Merge
475 mergetools.set_merge_tools(self.merge_tools_list_model.get_merge_tools())
476 mergetools.set_default_merge_tool(self.merge_tools_list_model.get_default())
477
488 def do_accept(self):478 def do_accept(self):
489 """Save changes and close the window."""479 """Save changes and close the window."""
490 if not self.validate():480 if not self.validate():
@@ -578,42 +568,80 @@
578 changed_item.setCheckState(0, QtCore.Qt.Checked)568 changed_item.setCheckState(0, QtCore.Qt.Checked)
579 self.extDiffListIgnore = False569 self.extDiffListIgnore = False
580570
581 def addExtMerge(self):571 def merge_tools_list_currentChanged(self, curr, prev):
582 item = QtGui.QTreeWidgetItem(self.extMergeList)572 mt = self.merge_tools_list_model.get_merge_tool(curr)
583 item.setFlags(QtCore.Qt.ItemIsSelectable |573 self.merge_ui.merge_tool_name.setText(mt.get_name())
584 QtCore.Qt.ItemIsEditable |574 self.merge_ui.merge_tool_commandline.setText(
585 QtCore.Qt.ItemIsEnabled |575 mt.get_commandline(quote=True))
586 QtCore.Qt.ItemIsUserCheckable)576 default = self.merge_tools_list_model.get_default()
587 item.setCheckState(0, QtCore.Qt.Unchecked)577 self.merge_ui.merge_tool_default.blockSignals(True)
588 self.extMergeList.setCurrentItem(item)578 self.merge_ui.merge_tool_default.setChecked(default is mt)
589 self.extMergeList.editItem(item, 0)579 self.merge_ui.merge_tool_default.blockSignals(False)
590580 self.merge_ui.merge_tool_details.setEnabled(True)
591 def removeExtMerge(self):581
592 for item in self.extMergeList.selectedItems():582 def merge_tools_list_dataChanged(self, first, last):
593 index = self.extMergeList.indexOfTopLevelItem(item)583 sel_model = self.merge_ui.merge_tools_list.selectionModel()
594 self.extMergeList.takeTopLevelItem(index)584 assert sel_model.hasSelection()
595585 curr = sel_model.currentIndex()
596 def extMergeListItemChanged(self, changed_item, col):586 if curr >= first and curr <= last:
597 if col == 0 and not self.extMergeListIgnore:587 self.merge_tools_list_currentChanged(curr, None)
598 checked_count = 0588
599 for index in range(self.extMergeList.topLevelItemCount()):589 def merge_tools_add_clicked(self):
600 item = self.extMergeList.topLevelItem(index)590 index = self.merge_tools_list_model.new_merge_tool()
601 if item.checkState(0) == QtCore.Qt.Checked:591 sel_model = self.merge_ui.merge_tools_list.selectionModel()
602 checked_count += 1592 sel_model.setCurrentIndex(index, QtGui.QItemSelectionModel.ClearAndSelect)
593 self.merge_ui.merge_tool_name.setFocus(Qt.OtherFocusReason)
594 self.merge_ui.merge_tool_name.selectAll()
595
596 def merge_tools_remove_clicked(self):
597 sel_model = self.merge_ui.merge_tools_list.selectionModel()
598 assert sel_model.hasSelection()
599 self.merge_tools_list_model.remove_merge_tool(sel_model.currentIndex())
600
601 def merge_tools_detect_clicked(self):
602 self.merge_tools_list_model.detect_merge_tools()
603
604 def merge_tool_name_changed(self):
605 sel_model = self.merge_ui.merge_tools_list.selectionModel()
606 assert sel_model.hasSelection()
607 text = unicode(self.merge_ui.merge_tool_name.text())
608 self.merge_tools_list_model.set_merge_tool_name(
609 sel_model.currentIndex(), text)
610 self.merge_tools_list_model.sort(0, Qt.AscendingOrder)
611
612 def merge_tool_commandline_changed(self):
613 sel_model = self.merge_ui.merge_tools_list.selectionModel()
614 assert sel_model.hasSelection()
615 curr = sel_model.currentIndex()
616 mt = self.merge_tools_list_model.get_merge_tool(curr)
617 text = unicode(self.merge_ui.merge_tool_commandline.text())
618 mt.set_commandline(text)
619
620 def merge_tool_default_toggled(self, checked):
621 sel_model = self.merge_ui.merge_tools_list.selectionModel()
622 assert sel_model.hasSelection()
623 if checked:
624 new_default = self.merge_tools_list_model.get_merge_tool(
625 sel_model.currentIndex())
626 if self.merge_tools_list_model.get_default() is not new_default:
627 self.merge_tools_list_model.set_default(new_default)
628 else:
629 self.merge_tools_list_model.set_default(None)
603 630
604 if checked_count == 0:631 def merge_tool_browse_clicked(self):
605 self.extMergeListIgnore = True632 sel_model = self.merge_ui.merge_tools_list.selectionModel()
606 changed_item.setCheckState(0, QtCore.Qt.Checked)633 assert sel_model.hasSelection()
607 self.extMergeListIgnore = False634 self.merge_tool_commandline_changed() # force update of model
608 elif checked_count > 1:635 filename = QtGui.QFileDialog.getOpenFileName(self,
609 self.extMergeListIgnore = True636 gettext('Select merge tool executable'),
610 for index in range(self.extMergeList.topLevelItemCount()):637 '/')
611 item = self.extMergeList.topLevelItem(index)638 if filename is not None:
612 if item.checkState(0) == QtCore.Qt.Checked:639 curr = sel_model.currentIndex()
613 item.setCheckState(0, QtCore.Qt.Unchecked)640 mt = self.merge_tools_list_model.get_merge_tool(curr)
614 changed_item.setCheckState(0, QtCore.Qt.Checked)641 mt.set_executable(unicode(filename))
615 self.extMergeListIgnore = False642 self.merge_ui.merge_tool_commandline.setText(
616643 mt.get_commandline(quote=True))
644
617 def browseEditor(self):645 def browseEditor(self):
618 filename = QtGui.QFileDialog.getOpenFileName(self,646 filename = QtGui.QFileDialog.getOpenFileName(self,
619 gettext('Select editor executable'),647 gettext('Select editor executable'),
@@ -698,3 +726,130 @@
698726
699 import socket727 import socket
700 return realname, (username + '@' + socket.gethostname())728 return realname, (username + '@' + socket.gethostname())
729
730class MergeToolsListModel(QtCore.QAbstractListModel):
731 def __init__(self):
732 super(MergeToolsListModel, self).__init__()
733 self._merge_tools = []
734 self._default = None
735
736 def get_merge_tools(self):
737 return self._merge_tools
738
739 def set_merge_tools(self, merge_tools, default):
740 self.beginResetModel()
741 self._merge_tools = merge_tools
742 # see set_default for explanation
743 for mt in self._merge_tools:
744 if mt == default:
745 self._default = mt
746 break
747 self.endResetModel()
748
749 def get_default(self):
750 return self._default
751
752 def set_default(self, new_default):
753 old_index = None
754 if self._default is not None:
755 i = self._merge_tools.index(self._default)
756 old_index = self.createIndex(i, i)
757 new_index = None
758 if new_default is not None:
759 i = self._merge_tools.index(new_default)
760 new_index = self.createIndex(i, i)
761 # new_default may be == to an instance in self._merge_tools but not
762 # the same instance. To preserve editing semantics in this model,
763 # self._default must refer to an instance in self._merge_tools, so
764 # we find the == instance in self._merge_tools and set self._default
765 # to that instead.
766 for mt in self._merge_tools:
767 if mt == default:
768 self._default = mt
769 break
770 else:
771 self._default = None
772 if old_index:
773 self.emit(QtCore.SIGNAL("dataChanged(QModelIndex,QModelIndex)"),
774 old_index, old_index)
775 if new_index:
776 self.emit(QtCore.SIGNAL("dataChanged(QModelIndex,QModelIndex)"),
777 new_index, new_index)
778
779 def get_merge_tool(self, index):
780 return self._merge_tools[index.row()]
781
782 def index_of(self, tool):
783 return self.createIndex(self._merge_tools.index(tool), 0)
784
785 def new_merge_tool(self):
786 index = self.createIndex(len(self._merge_tools), 0)
787 self.beginInsertRows(QtCore.QModelIndex(), index.row(), index.row())
788 self._merge_tools.append(mergetools.MergeTool(
789 gettext('New Merge Tool'), ''))
790 self.endInsertRows()
791 return index
792
793 def remove_merge_tool(self, index):
794 self.beginRemoveRows(QtCore.QModelIndex(), index.row(), index.row())
795 if self._merge_tools[index.row()] == self._default:
796 self._default = None
797 del self._merge_tools[index.row()]
798 self.endRemoveRows()
799
800 def detect_merge_tools(self):
801 detected_tools = mergetools.detect_merge_tools()
802 for mt in detected_tools:
803 if mt not in self._merge_tools:
804 row = len(self._merge_tools)
805 self.beginInsertRows(QtCore.QModelIndex(), row, row)
806 self._merge_tools.append(mt)
807 self.endInsertRows()
808 self.sort(0, Qt.AscendingOrder)
809
810 def set_merge_tool_name(self, index, name):
811 self._merge_tools[index.row()].set_name(name)
812 self.emit(QtCore.SIGNAL("dataChanged(QModelIndex,QModelIndex)"), index,
813 index)
814
815 def set_merge_tool_commandline(self, index, commandline):
816 self._merge_tools[index.row()].set_commandline(commandline)
817
818 def set_merge_tool_executable(self, index, executable):
819 self._merge_tools[index.row()].set_executable(executable)
820
821 def rowCount(self, parent):
822 return len(self._merge_tools)
823
824 def data(self, index, role):
825 if role == Qt.DisplayRole:
826 name = self._merge_tools[index.row()].get_name()
827 if self._default is not None and self._default.get_name() == name:
828 name = '%s (%s)' % (name, gettext('default'))
829 return QtCore.QVariant(name)
830 elif role == Qt.EditRole:
831 return QtCore.QVariant(self._merge_tools[index.row()].get_name())
832 return QtCore.QVariant()
833
834 def setData(self, index, value, role):
835 if role == Qt.EditRole:
836 self._merge_tools[index.row()].set_name(unicode(value.toString()))
837 self.emit(QtCore.SIGNAL("dataChanged(QModelIndex,QModelIndex)"),
838 index,index)
839 self.sort(0, Qt.AscendingOrder)
840 return True
841 return False
842
843 def flags(self, index):
844 return super(MergeToolsListModel, self).flags(index) | Qt.ItemIsEditable
845
846 def sort(self, column, sortOrder):
847 self.emit(QtCore.SIGNAL("layoutAboutToBeChanged()"))
848 index_map = self._merge_tools[:] # copy
849 self._merge_tools.sort()
850 for i in range(0, len(index_map)):
851 index_map[i] = self._merge_tools.index(index_map[i])
852 from_list = [self.createIndex(i, 0) for i in index_map]
853 to_list = [self.createIndex(i, 0) for i in range(0, len(index_map))]
854 self.changePersistentIndexList(from_list, to_list)
855 self.emit(QtCore.SIGNAL("layoutChanged()"))
701856
=== modified file 'lib/conflicts.py'
--- lib/conflicts.py 2010-09-01 07:30:29 +0000
+++ lib/conflicts.py 2010-11-09 00:10:03 +0000
@@ -20,6 +20,7 @@
20from PyQt4 import QtCore, QtGui20from PyQt4 import QtCore, QtGui
21from bzrlib.config import GlobalConfig21from bzrlib.config import GlobalConfig
22from bzrlib.conflicts import resolve22from bzrlib.conflicts import resolve
23from bzrlib import mergetools
23from bzrlib.workingtree import WorkingTree24from bzrlib.workingtree import WorkingTree
24from bzrlib.plugins.qbzr.lib.i18n import gettext, N_, ngettext25from bzrlib.plugins.qbzr.lib.i18n import gettext, N_, ngettext
25from bzrlib.plugins.qbzr.lib.util import (26from bzrlib.plugins.qbzr.lib.util import (
@@ -58,7 +59,7 @@
58 self.connect(59 self.connect(
59 self.conflicts_list.selectionModel(),60 self.conflicts_list.selectionModel(),
60 QtCore.SIGNAL("selectionChanged(QItemSelection, QItemSelection)"),61 QtCore.SIGNAL("selectionChanged(QItemSelection, QItemSelection)"),
61 self.update_selection)62 self.update_merge_tool_ui)
62 self.connect(63 self.connect(
63 self.conflicts_list,64 self.conflicts_list,
64 QtCore.SIGNAL("customContextMenuRequested(QPoint)"),65 QtCore.SIGNAL("customContextMenuRequested(QPoint)"),
@@ -77,22 +78,14 @@
77 vbox.addWidget(self.conflicts_list)78 vbox.addWidget(self.conflicts_list)
7879
79 hbox = QtGui.QHBoxLayout()80 hbox = QtGui.QHBoxLayout()
80 self.program_edit = QtGui.QLineEdit(self)81 self.merge_tools_combo = QtGui.QComboBox(self)
81 self.program_edit.setEnabled(False)82 self.merge_tools_combo.setEditable(False)
82 self.connect(83 self.connect(self.merge_tools_combo,
83 self.program_edit,84 QtCore.SIGNAL("currentIndexChanged(int)"),
84 QtCore.SIGNAL("textChanged(QString)"),85 self.update_merge_tool_ui)
85 self.check_merge_tool_edit)86
86 self.program_extmerge_default_button = QtGui.QCheckBox(gettext("Use Configured Default"))87 self.merge_tool_error = QtGui.QLabel('', self)
87 self.program_extmerge_default_button.setToolTip(gettext(88
88 "The merge tool configured in qconfig under Merge' file.\n"
89 "It follows the convention used in the bzr plugin: extmerge\n"
90 "external_merge = kdiff3 --output %r %b %t %o\n"
91 "%r is output, %b is .BASE, %t is .THIS and %o is .OTHER file."))
92 self.connect(
93 self.program_extmerge_default_button,
94 QtCore.SIGNAL("clicked()"),
95 self.program_extmerge_default_clicked)
96 self.program_launch_button = QtGui.QPushButton(gettext("&Launch..."), self)89 self.program_launch_button = QtGui.QPushButton(gettext("&Launch..."), self)
97 self.program_launch_button.setEnabled(False)90 self.program_launch_button.setEnabled(False)
98 self.connect(91 self.connect(
@@ -100,10 +93,11 @@
100 QtCore.SIGNAL("clicked()"),93 QtCore.SIGNAL("clicked()"),
101 self.launch_merge_tool)94 self.launch_merge_tool)
102 self.program_label = QtGui.QLabel(gettext("M&erge tool:"), self)95 self.program_label = QtGui.QLabel(gettext("M&erge tool:"), self)
103 self.program_label.setBuddy(self.program_edit)96 self.program_label.setBuddy(self.merge_tools_combo)
104 hbox.addWidget(self.program_label)97 hbox.addWidget(self.program_label)
105 hbox.addWidget(self.program_edit)98 hbox.addWidget(self.merge_tools_combo)
106 hbox.addWidget(self.program_extmerge_default_button)99 hbox.addWidget(self.merge_tool_error)
100 hbox.addStretch(1)
107 hbox.addWidget(self.program_launch_button)101 hbox.addWidget(self.program_launch_button)
108 vbox.addLayout(hbox)102 vbox.addLayout(hbox)
109103
@@ -125,21 +119,13 @@
125 self.initialize_ui() 119 self.initialize_ui()
126120
127 def initialize_ui(self):121 def initialize_ui(self):
128 merge_tool_extmerge = get_qbzr_config().get_option("merge_tool_extmerge")122 defined_tools = mergetools.get_merge_tools()
129 123 default_tool = mergetools.get_default_merge_tool()
130 self.program_extmerge_default_button.setCheckState(QtCore.Qt.Unchecked)124 for merge_tool in defined_tools:
131 if merge_tool_extmerge in ("True", "1"):125 self.merge_tools_combo.insertItem(self.merge_tools_combo.count(), merge_tool.get_name())
132 self.program_extmerge_default_button.setCheckState(QtCore.Qt.Checked)126 if default_tool is not None:
133 self.program_extmerge_default_clicked()127 self.merge_tools_combo.setCurrentIndex(self.merge_tools_combo.findText(default_tool.get_name()))
134 enabled, error_msg = self.is_merge_tool_launchable()128 self.update_merge_tool_ui()
135 self.update_program_edit_text(enabled, error_msg)
136 # if extmerge not configured then resort to using default
137 if not enabled and self.program_extmerge_default_button.isChecked():
138 bzr_config = GlobalConfig()
139 extmerge_tool = bzr_config.get_user_option("external_merge")
140 if not extmerge_tool:
141 self.program_extmerge_default_button.setCheckState(QtCore.Qt.Unchecked)
142 self.update_program_edit_text(False, "")
143129
144 def create_context_menu(self):130 def create_context_menu(self):
145 self.context_menu = QtGui.QMenu(self.conflicts_list)131 self.context_menu = QtGui.QMenu(self.conflicts_list)
@@ -190,56 +176,31 @@
190 gettext('&OK'))176 gettext('&OK'))
191 self.close()177 self.close()
192178
193 def update_selection(self, selected, deselected):179 def update_merge_tool_ui(self):
194 enabled, error_msg = self.is_merge_tool_launchable()180 enabled, error_msg = self.is_merge_tool_launchable()
195 self.program_edit.setEnabled(enabled and not self.program_extmerge_default_button.isChecked())181 self.merge_tool_error.setText(error_msg)
196 self.program_launch_button.setEnabled(enabled)182 self.program_launch_button.setEnabled(enabled)
197 self.update_program_edit_text(enabled, error_msg)
198 if enabled and self.program_extmerge_default_button.isChecked():
199 self.program_edit.setEnabled(False)
200 self.merge_action.setEnabled(enabled)183 self.merge_action.setEnabled(enabled)
201184
202 def check_merge_tool_edit(self, text):
203 enabled, error_msg = self.is_merge_tool_launchable()
204 self.program_launch_button.setEnabled(enabled)
205
206 def launch_merge_tool(self):185 def launch_merge_tool(self):
207 items = self.conflicts_list.selectedItems()186 items = self.conflicts_list.selectedItems()
208 enabled, error_msg = self.is_merge_tool_launchable()187 enabled, error_msg = self.is_merge_tool_launchable()
209 if not enabled:188 if not enabled:
210 return189 return
211 merge_tool = unicode(self.program_edit.text()).strip()190 merge_tool = mergetools.find_merge_tool(self.merge_tools_combo.currentText())
212 if not merge_tool:
213 return
214 file_id = str(items[0].data(0, QtCore.Qt.UserRole).toString())191 file_id = str(items[0].data(0, QtCore.Qt.UserRole).toString())
215 file_name = self.wt.abspath(self.wt.id2path(file_id))192 file_name = self.wt.abspath(self.wt.id2path(file_id))
216 base_file_name = file_name + ".BASE"
217 this_file_name = file_name + ".THIS"
218 other_file_name = file_name + ".OTHER"
219 new_args = [base_file_name, this_file_name, other_file_name]
220 config = get_qbzr_config()
221 config.set_option("merge_tool_extmerge", False)
222
223 if self.program_extmerge_default_button.isChecked():
224 bzr_config = GlobalConfig()
225 extmerge_tool = bzr_config.get_user_option("external_merge")
226 args = cmdline_split(extmerge_tool)
227 new_args = args[1:len(args)]
228 i = 0
229 while i < len(new_args):
230 new_args[i] = new_args[i].replace('%r', file_name)
231 new_args[i] = new_args[i].replace('%o', other_file_name)
232 new_args[i] = new_args[i].replace('%b', base_file_name)
233 new_args[i] = new_args[i].replace('%t', this_file_name)
234 i = i + 1
235 merge_tool = args[0]
236 config.set_option("merge_tool_extmerge", True)
237 else:
238 config.set_option("merge_tool", merge_tool)
239
240 process = QtCore.QProcess(self)193 process = QtCore.QProcess(self)
241 self.connect(process, QtCore.SIGNAL("error(QProcess::ProcessError)"), self.show_merge_tool_error)194 def qprocess_invoker(executable, args, cleanup):
242 process.start(merge_tool, new_args)195 def qprocess_error(error):
196 self.show_merge_tool_error(error)
197 cleanup(process.exitCode())
198 def qprocess_finished(exit_code, exit_status):
199 cleanup(exit_code)
200 self.connect(process, QtCore.SIGNAL("error(QProcess::ProcessError)"), qprocess_error)
201 self.connect(process, QtCore.SIGNAL("finished(int,QProcess::ExitStatus)"), qprocess_finished)
202 process.start(executable, args)
203 merge_tool.invoke(file_name, qprocess_invoker)
243204
244 def show_merge_tool_error(self, error):205 def show_merge_tool_error(self, error):
245 msg = gettext("Error while running merge tool (code %d)") % error206 msg = gettext("Error while running merge tool (code %d)") % error
@@ -261,34 +222,20 @@
261 def show_context_menu(self, pos):222 def show_context_menu(self, pos):
262 self.context_menu.popup(self.conflicts_list.viewport().mapToGlobal(pos))223 self.context_menu.popup(self.conflicts_list.viewport().mapToGlobal(pos))
263224
264 def program_extmerge_default_clicked(self):
265 enabled, error_msg = self.is_merge_tool_launchable()
266 self.program_edit.setEnabled(enabled and not self.program_extmerge_default_button.isChecked())
267 self.program_launch_button.setEnabled(enabled)
268 self.update_program_edit_text(enabled, error_msg)
269 config = get_qbzr_config()
270 config.set_option("merge_tool_extmerge",
271 self.program_extmerge_default_button.isChecked())
272
273 def is_merge_tool_launchable(self):225 def is_merge_tool_launchable(self):
274 items = self.conflicts_list.selectedItems()226 items = self.conflicts_list.selectedItems()
275 error_msg = ""227 error_msg = ""
276 enabled = True228 enabled = True
277 if len(items) != 1 or items[0].data(1, QtCore.Qt.UserRole).toString() != "text conflict":229 if len(items) != 1 or items[0].data(1, QtCore.Qt.UserRole).toString() != "text conflict":
278 enabled = False230 enabled = False
279231 merge_tool = mergetools.find_merge_tool(self.merge_tools_combo.currentText())
280 # check to see if the extmerge config is correct 232 if merge_tool is None:
281 if self.program_extmerge_default_button.isChecked():233 error_msg = gettext("Set up external_merge app in qconfig under the Merge tab")
282 bzr_config = GlobalConfig()234 enabled = False
283 extmerge_tool = bzr_config.get_user_option("external_merge")235 elif not merge_tool.is_available():
284 if not extmerge_tool:236 enabled = False
285 error_msg = gettext("Set up external_merge app in qconfig under the Merge tab")237 error_msg = gettext("External merge tool %(tool)s is not available") % \
286 enabled = False238 { 'tool': merge_tool.get_name() }
287 return enabled, error_msg
288 error = self.is_extmerge_definition_valid(False)
289 if len(error) > 0:
290 enabled = False
291 error_msg = error
292 return enabled, error_msg239 return enabled, error_msg
293240
294 def is_extmerge_definition_valid(self, showErrorDialog):241 def is_extmerge_definition_valid(self, showErrorDialog):
@@ -316,17 +263,6 @@
316 return gettext("Missing the flag: %s. Configure in qconfig under the merge tab.") % flags263 return gettext("Missing the flag: %s. Configure in qconfig under the merge tab.") % flags
317 return ""264 return ""
318265
319 def update_program_edit_text(self, enabled, error_msg):
320 if self.program_extmerge_default_button.isChecked():
321 if enabled or (len(error_msg) <= 0):
322 config = GlobalConfig()
323 extmerge = config.get_user_option("external_merge")
324 self.program_edit.setText(gettext("%s (Configured external merge definition in qconfig)") % extmerge)
325 else:
326 self.program_edit.setText(error_msg)
327 else:
328 config = get_qbzr_config()
329 self.program_edit.setText((config.get_user_option("merge_tool") or "").strip() or "meld")
330266
331if 0:267if 0:
332 N_("path conflict")268 N_("path conflict")
333269
=== added file 'lib/ui_merge_config.py'
--- lib/ui_merge_config.py 1970-01-01 00:00:00 +0000
+++ lib/ui_merge_config.py 2010-11-09 00:10:03 +0000
@@ -0,0 +1,97 @@
1# -*- coding: utf-8 -*-
2
3# Form implementation generated from reading ui file 'ui/merge_config.ui'
4#
5# Created: Sun Nov 07 17:00:49 2010
6# by: PyQt4 UI code generator 4.7.2
7#
8# WARNING! All changes made in this file will be lost!
9
10from PyQt4 import QtCore, QtGui
11from bzrlib.plugins.qbzr.lib.i18n import gettext
12
13
14class Ui_MergeConfig(object):
15 def setupUi(self, MergeConfig):
16 MergeConfig.setObjectName("MergeConfig")
17 MergeConfig.resize(626, 297)
18 self.verticalLayout_2 = QtGui.QVBoxLayout(MergeConfig)
19 self.verticalLayout_2.setObjectName("verticalLayout_2")
20 self.groupBox = QtGui.QGroupBox(MergeConfig)
21 self.groupBox.setObjectName("groupBox")
22 self.verticalLayout_3 = QtGui.QVBoxLayout(self.groupBox)
23 self.verticalLayout_3.setObjectName("verticalLayout_3")
24 self.splitter = QtGui.QSplitter(self.groupBox)
25 self.splitter.setOrientation(QtCore.Qt.Horizontal)
26 self.splitter.setChildrenCollapsible(False)
27 self.splitter.setObjectName("splitter")
28 self.layoutWidget = QtGui.QWidget(self.splitter)
29 self.layoutWidget.setObjectName("layoutWidget")
30 self.verticalLayout = QtGui.QVBoxLayout(self.layoutWidget)
31 self.verticalLayout.setObjectName("verticalLayout")
32 self.merge_tools_list = QtGui.QListView(self.layoutWidget)
33 self.merge_tools_list.setObjectName("merge_tools_list")
34 self.verticalLayout.addWidget(self.merge_tools_list)
35 self.merge_tools_buttons = QtGui.QWidget(self.layoutWidget)
36 self.merge_tools_buttons.setObjectName("merge_tools_buttons")
37 self.horizontalLayout = QtGui.QHBoxLayout(self.merge_tools_buttons)
38 self.horizontalLayout.setObjectName("horizontalLayout")
39 spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
40 self.horizontalLayout.addItem(spacerItem)
41 self.merge_tools_add = QtGui.QPushButton(self.merge_tools_buttons)
42 self.merge_tools_add.setObjectName("merge_tools_add")
43 self.horizontalLayout.addWidget(self.merge_tools_add)
44 self.merge_tools_remove = QtGui.QPushButton(self.merge_tools_buttons)
45 self.merge_tools_remove.setObjectName("merge_tools_remove")
46 self.horizontalLayout.addWidget(self.merge_tools_remove)
47 self.merge_tools_detect = QtGui.QPushButton(self.merge_tools_buttons)
48 self.merge_tools_detect.setObjectName("merge_tools_detect")
49 self.horizontalLayout.addWidget(self.merge_tools_detect)
50 spacerItem1 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
51 self.horizontalLayout.addItem(spacerItem1)
52 self.verticalLayout.addWidget(self.merge_tools_buttons)
53 self.merge_tool_details = QtGui.QWidget(self.splitter)
54 self.merge_tool_details.setMinimumSize(QtCore.QSize(300, 0))
55 self.merge_tool_details.setObjectName("merge_tool_details")
56 self.gridLayout = QtGui.QGridLayout(self.merge_tool_details)
57 self.gridLayout.setContentsMargins(-1, 0, -1, 0)
58 self.gridLayout.setObjectName("gridLayout")
59 self.label = QtGui.QLabel(self.merge_tool_details)
60 self.label.setObjectName("label")
61 self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
62 self.merge_tool_name = QtGui.QLineEdit(self.merge_tool_details)
63 self.merge_tool_name.setObjectName("merge_tool_name")
64 self.gridLayout.addWidget(self.merge_tool_name, 1, 0, 1, 1)
65 self.label_2 = QtGui.QLabel(self.merge_tool_details)
66 self.label_2.setObjectName("label_2")
67 self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1)
68 self.merge_tool_commandline = QtGui.QLineEdit(self.merge_tool_details)
69 self.merge_tool_commandline.setObjectName("merge_tool_commandline")
70 self.gridLayout.addWidget(self.merge_tool_commandline, 3, 0, 1, 1)
71 self.merge_tool_browse = QtGui.QToolButton(self.merge_tool_details)
72 self.merge_tool_browse.setObjectName("merge_tool_browse")
73 self.gridLayout.addWidget(self.merge_tool_browse, 3, 1, 1, 1)
74 spacerItem2 = QtGui.QSpacerItem(248, 130, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
75 self.gridLayout.addItem(spacerItem2, 6, 0, 1, 1)
76 self.merge_tool_default = QtGui.QCheckBox(self.merge_tool_details)
77 self.merge_tool_default.setObjectName("merge_tool_default")
78 self.gridLayout.addWidget(self.merge_tool_default, 4, 0, 1, 1)
79 self.verticalLayout_3.addWidget(self.splitter)
80 self.verticalLayout_2.addWidget(self.groupBox)
81 self.label.setBuddy(self.merge_tool_name)
82 self.label_2.setBuddy(self.merge_tool_commandline)
83
84 self.retranslateUi(MergeConfig)
85 QtCore.QMetaObject.connectSlotsByName(MergeConfig)
86
87 def retranslateUi(self, MergeConfig):
88 MergeConfig.setWindowTitle(gettext("External Merge Tools"))
89 self.groupBox.setTitle(gettext("External Merge Tools"))
90 self.merge_tools_add.setText(gettext("Add"))
91 self.merge_tools_remove.setText(gettext("Remove"))
92 self.merge_tools_detect.setText(gettext("Detect"))
93 self.label.setText(gettext("Name:"))
94 self.label_2.setText(gettext("Command line:"))
95 self.merge_tool_browse.setText(gettext("..."))
96 self.merge_tool_default.setText(gettext("Default"))
97
098
=== added file 'ui/merge_config.ui'
--- ui/merge_config.ui 1970-01-01 00:00:00 +0000
+++ ui/merge_config.ui 2010-11-09 00:10:03 +0000
@@ -0,0 +1,169 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>MergeConfig</class>
4 <widget class="QWidget" name="MergeConfig">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>626</width>
10 <height>297</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>External Merge Tools</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_2">
17 <item>
18 <widget class="QGroupBox" name="groupBox">
19 <property name="title">
20 <string>External Merge Tools</string>
21 </property>
22 <layout class="QVBoxLayout" name="verticalLayout_3">
23 <item>
24 <widget class="QSplitter" name="splitter">
25 <property name="orientation">
26 <enum>Qt::Horizontal</enum>
27 </property>
28 <property name="childrenCollapsible">
29 <bool>false</bool>
30 </property>
31 <widget class="QWidget" name="layoutWidget">
32 <layout class="QVBoxLayout" name="verticalLayout">
33 <item>
34 <widget class="QListView" name="merge_tools_list"/>
35 </item>
36 <item>
37 <widget class="QWidget" name="merge_tools_buttons" native="true">
38 <layout class="QHBoxLayout" name="horizontalLayout">
39 <item>
40 <spacer name="spacer">
41 <property name="orientation">
42 <enum>Qt::Horizontal</enum>
43 </property>
44 <property name="sizeHint" stdset="0">
45 <size>
46 <width>40</width>
47 <height>20</height>
48 </size>
49 </property>
50 </spacer>
51 </item>
52 <item>
53 <widget class="QPushButton" name="merge_tools_add">
54 <property name="text">
55 <string>Add</string>
56 </property>
57 </widget>
58 </item>
59 <item>
60 <widget class="QPushButton" name="merge_tools_remove">
61 <property name="text">
62 <string>Remove</string>
63 </property>
64 </widget>
65 </item>
66 <item>
67 <widget class="QPushButton" name="merge_tools_detect">
68 <property name="text">
69 <string>Detect</string>
70 </property>
71 </widget>
72 </item>
73 <item>
74 <spacer name="spacer_2">
75 <property name="orientation">
76 <enum>Qt::Horizontal</enum>
77 </property>
78 <property name="sizeHint" stdset="0">
79 <size>
80 <width>40</width>
81 <height>20</height>
82 </size>
83 </property>
84 </spacer>
85 </item>
86 </layout>
87 </widget>
88 </item>
89 </layout>
90 </widget>
91 <widget class="QWidget" name="merge_tool_details" native="true">
92 <property name="minimumSize">
93 <size>
94 <width>300</width>
95 <height>0</height>
96 </size>
97 </property>
98 <layout class="QGridLayout" name="gridLayout">
99 <property name="topMargin">
100 <number>0</number>
101 </property>
102 <property name="bottomMargin">
103 <number>0</number>
104 </property>
105 <item row="0" column="0">
106 <widget class="QLabel" name="label">
107 <property name="text">
108 <string>Name:</string>
109 </property>
110 <property name="buddy">
111 <cstring>merge_tool_name</cstring>
112 </property>
113 </widget>
114 </item>
115 <item row="1" column="0">
116 <widget class="QLineEdit" name="merge_tool_name"/>
117 </item>
118 <item row="2" column="0">
119 <widget class="QLabel" name="label_2">
120 <property name="text">
121 <string>Command line:</string>
122 </property>
123 <property name="buddy">
124 <cstring>merge_tool_commandline</cstring>
125 </property>
126 </widget>
127 </item>
128 <item row="3" column="0">
129 <widget class="QLineEdit" name="merge_tool_commandline"/>
130 </item>
131 <item row="3" column="1">
132 <widget class="QToolButton" name="merge_tool_browse">
133 <property name="text">
134 <string>...</string>
135 </property>
136 </widget>
137 </item>
138 <item row="6" column="0">
139 <spacer name="spacer_3">
140 <property name="orientation">
141 <enum>Qt::Vertical</enum>
142 </property>
143 <property name="sizeHint" stdset="0">
144 <size>
145 <width>248</width>
146 <height>130</height>
147 </size>
148 </property>
149 </spacer>
150 </item>
151 <item row="4" column="0">
152 <widget class="QCheckBox" name="merge_tool_default">
153 <property name="text">
154 <string>Default</string>
155 </property>
156 </widget>
157 </item>
158 </layout>
159 </widget>
160 </widget>
161 </item>
162 </layout>
163 </widget>
164 </item>
165 </layout>
166 </widget>
167 <resources/>
168 <connections/>
169</ui>

Subscribers

People subscribed via source and target branches