git-cola v1.9.1
[git-cola.git] / cola / difftool.py
blobac89813b2b214b52fd2fa3e9817f74b8eaae099e
1 from PyQt4 import QtGui
2 from PyQt4 import QtCore
3 from PyQt4.QtCore import Qt
4 from PyQt4.QtCore import SIGNAL
6 import cola
7 from cola import core
8 from cola import utils
9 from cola import qtutils
10 from cola import gitcmds
11 from cola.i18n import N_
12 from cola.widgets import completion
13 from cola.widgets import defs
14 from cola.widgets import standard
17 def run():
18 s = cola.selection()
19 if s.staged:
20 selection = s.staged
21 elif s.unmerged:
22 selection = s.unmerged
23 elif s.modified:
24 selection = s.modified
25 elif s.untracked:
26 selection = s.untracked
27 else:
28 return
29 model = cola.model()
30 launch_with_head(selection, bool(s.staged), model.head)
33 def launch_with_head(filenames, staged, head):
34 args = []
35 if staged:
36 args.append('--cached')
37 if head != 'HEAD':
38 args.append(head)
39 args.append('--')
40 args.extend(filenames)
41 launch(args)
44 def launch(args):
45 """Launches 'git difftool' with args"""
46 difftool_args = ['git', 'difftool', '--no-prompt']
47 difftool_args.extend(args)
48 core.fork(difftool_args)
51 def diff_commits(parent, a, b):
52 dlg = FileDiffDialog(parent, a=a, b=b)
53 dlg.show()
54 dlg.raise_()
55 return dlg.exec_() == QtGui.QDialog.Accepted
58 def diff_expression(parent, expr,
59 create_widget=False, hide_expr=False):
60 dlg = FileDiffDialog(parent, expr=expr, hide_expr=hide_expr)
61 if create_widget:
62 return dlg
63 dlg.show()
64 dlg.raise_()
65 return dlg.exec_() == QtGui.QDialog.Accepted
68 class FileDiffDialog(QtGui.QDialog):
70 def __init__(self, parent, a=None, b=None, expr=None, title=None,
71 hide_expr=False):
72 QtGui.QDialog.__init__(self, parent)
73 self.setAttribute(Qt.WA_MacMetalStyle)
75 self.a = a
76 self.b = b
77 self.expr = expr
79 if title is None:
80 title = N_('git-cola diff')
82 self.setWindowTitle(title)
83 self.setWindowModality(QtCore.Qt.WindowModal)
85 self._expr = completion.GitRefLineEdit(parent=self)
86 if expr is not None:
87 self._expr.setText(expr)
89 if expr is None or hide_expr:
90 self._expr.hide()
92 self._tree = standard.TreeWidget(self)
93 self._tree.setSelectionMode(self._tree.ExtendedSelection)
94 self._tree.setHeaderHidden(True)
96 self._diff_btn = QtGui.QPushButton(N_('Compare'))
97 self._diff_btn.setIcon(qtutils.ok_icon())
98 self._diff_btn.setEnabled(False)
100 self._close_btn = QtGui.QPushButton(N_('Close'))
101 self._close_btn.setIcon(qtutils.close_icon())
103 self._button_layt = QtGui.QHBoxLayout()
104 self._button_layt.setMargin(0)
105 self._button_layt.addStretch()
106 self._button_layt.addWidget(self._diff_btn)
107 self._button_layt.addWidget(self._close_btn)
109 self._layt = QtGui.QVBoxLayout()
110 self._layt.setMargin(defs.margin)
111 self._layt.setSpacing(defs.spacing)
113 self._layt.addWidget(self._expr)
114 self._layt.addWidget(self._tree)
115 self._layt.addLayout(self._button_layt)
116 self.setLayout(self._layt)
118 self.connect(self._tree, SIGNAL('itemSelectionChanged()'),
119 self._tree_selection_changed)
121 self.connect(self._tree,
122 SIGNAL('itemDoubleClicked(QTreeWidgetItem*,int)'),
123 self._tree_double_clicked)
125 self.connect(self._expr, SIGNAL('textChanged(QString)'),
126 self.text_changed)
128 self.connect(self._expr, SIGNAL('returnPressed()'),
129 self.refresh)
131 qtutils.connect_button(self._diff_btn, self.diff)
132 qtutils.connect_button(self._close_btn, self.close)
133 qtutils.add_close_action(self)
135 self.resize(720, 420)
136 self.refresh()
138 def text_changed(self, txt):
139 self.expr = unicode(txt)
140 self.refresh()
142 def refresh(self):
143 if self.expr is not None:
144 self.diff_arg = utils.shell_split(self.expr)
145 elif self.b is None:
146 self.diff_arg = [self.a]
147 else:
148 self.diff_arg = [self.a, self.b]
149 self.refresh_filenames()
151 def refresh_filenames(self):
152 self._tree.clear()
154 if self.a and self.b is None:
155 filenames = gitcmds.diff_index_filenames(self.a)
156 else:
157 filenames = gitcmds.diff(self.diff_arg)
158 if not filenames:
159 return
161 icon = qtutils.file_icon()
162 items = []
163 for filename in filenames:
164 item = QtGui.QTreeWidgetItem()
165 item.setIcon(0, icon)
166 item.setText(0, filename)
167 item.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(filename))
168 items.append(item)
169 self._tree.addTopLevelItems(items)
171 def _tree_selection_changed(self):
172 self._diff_btn.setEnabled(bool(self._tree.selectedItems()))
174 def _tree_double_clicked(self, item, column):
175 path = item.data(0, QtCore.Qt.UserRole).toPyObject()
176 launch(self.diff_arg + ['--', unicode(path)])
178 def diff(self):
179 items = self._tree.selectedItems()
180 if not items:
181 return
182 paths = [i.data(0, QtCore.Qt.UserRole).toPyObject() for i in items]
183 for path in paths:
184 launch(self.diff_arg + ['--', unicode(path)])