doc: add 0xflotus to the credits
[git-cola.git] / cola / difftool.py
blob1c0f86b7b452a62ff3658401724c1bf2a6938a3d
1 from __future__ import division, absolute_import, unicode_literals
3 from qtpy import QtWidgets
4 from qtpy.QtCore import Qt
6 from . import cmds
7 from . import gitcmds
8 from . import hotkeys
9 from . import icons
10 from . import qtutils
11 from . import utils
12 from .i18n import N_
13 from .widgets import completion
14 from .widgets import defs
15 from .widgets import filetree
16 from .widgets import standard
19 def diff_commits(context, parent, a, b):
20 """Show a dialog for diffing two commits"""
21 dlg = Difftool(context, parent, a=a, b=b)
22 dlg.show()
23 dlg.raise_()
24 return dlg.exec_() == QtWidgets.QDialog.Accepted
27 def diff_expression(context, parent, expr,
28 create_widget=False, hide_expr=False, focus_tree=False):
29 """Show a diff dialog for diff expressions"""
30 dlg = Difftool(context, parent,
31 expr=expr, hide_expr=hide_expr,
32 focus_tree=focus_tree)
33 if create_widget:
34 return dlg
35 dlg.show()
36 dlg.raise_()
37 return dlg.exec_() == QtWidgets.QDialog.Accepted
40 class Difftool(standard.Dialog):
42 def __init__(self, context, parent, a=None, b=None, expr=None, title=None,
43 hide_expr=False, focus_tree=False):
44 """Show files with differences and launch difftool"""
46 standard.Dialog.__init__(self, parent=parent)
48 self.context = context
49 self.a = a
50 self.b = b
51 self.diff_expr = expr
53 if title is None:
54 title = N_('git-cola diff')
56 self.setWindowTitle(title)
57 self.setWindowModality(Qt.WindowModal)
59 self.expr = completion.GitRefLineEdit(context, parent=self)
60 if expr is not None:
61 self.expr.setText(expr)
63 if expr is None or hide_expr:
64 self.expr.hide()
66 self.tree = filetree.FileTree(parent=self)
68 self.diff_button = qtutils.create_button(text=N_('Compare'),
69 icon=icons.diff(),
70 enabled=False,
71 default=True)
72 self.diff_button.setShortcut(hotkeys.DIFF)
74 self.diff_all_button = qtutils.create_button(text=N_('Compare All'),
75 icon=icons.diff())
76 self.edit_button = qtutils.edit_button()
77 self.edit_button.setShortcut(hotkeys.EDIT)
79 self.close_button = qtutils.close_button()
81 self.button_layout = qtutils.hbox(defs.no_margin, defs.spacing,
82 self.close_button,
83 qtutils.STRETCH,
84 self.edit_button,
85 self.diff_all_button,
86 self.diff_button)
88 self.main_layout = qtutils.vbox(defs.margin, defs.spacing,
89 self.expr, self.tree,
90 self.button_layout)
91 self.setLayout(self.main_layout)
93 self.tree.itemSelectionChanged.connect(self.tree_selection_changed)
94 self.tree.itemDoubleClicked.connect(self.tree_double_clicked)
95 self.tree.up.connect(self.focus_input)
97 self.expr.textChanged.connect(self.text_changed)
99 self.expr.activated.connect(self.focus_tree)
100 self.expr.down.connect(self.focus_tree)
101 self.expr.enter.connect(self.focus_tree)
103 qtutils.connect_button(self.diff_button, self.diff)
104 qtutils.connect_button(self.diff_all_button,
105 lambda: self.diff(dir_diff=True))
106 qtutils.connect_button(self.edit_button, self.edit)
107 qtutils.connect_button(self.close_button, self.close)
109 qtutils.add_action(self, 'Focus Input', self.focus_input, hotkeys.FOCUS)
110 qtutils.add_action(self, 'Diff All', lambda: self.diff(dir_diff=True),
111 hotkeys.CTRL_ENTER, hotkeys.CTRL_RETURN)
112 qtutils.add_close_action(self)
114 self.init_state(None, self.resize_widget, parent)
116 self.refresh()
117 if focus_tree:
118 self.focus_tree()
120 def resize_widget(self, parent):
121 """Set the initial size of the widget"""
122 width, height = qtutils.default_size(parent, 720, 420)
123 self.resize(width, height)
125 def focus_tree(self):
126 """Focus the files tree"""
127 self.tree.setFocus()
129 def focus_input(self):
130 """Focus the expression input"""
131 self.expr.setFocus()
133 def text_changed(self, txt):
134 self.diff_expr = txt
135 self.refresh()
137 def refresh(self):
138 """Redo the diff when the expression changes"""
139 if self.diff_expr is not None:
140 self.diff_arg = utils.shell_split(self.diff_expr)
141 elif self.b is None:
142 self.diff_arg = [self.a]
143 else:
144 self.diff_arg = [self.a, self.b]
145 self.refresh_filenames()
147 def refresh_filenames(self):
148 context = self.context
149 if self.a and self.b is None:
150 filenames = gitcmds.diff_index_filenames(context, self.a)
151 else:
152 filenames = gitcmds.diff(context, self.diff_arg)
153 self.tree.set_filenames(filenames, select=True)
155 def tree_selection_changed(self):
156 has_selection = self.tree.has_selection()
157 self.diff_button.setEnabled(has_selection)
158 self.diff_all_button.setEnabled(has_selection)
160 def tree_double_clicked(self, item, _column):
161 path = filetree.filename_from_item(item)
162 left, right = self._left_right_args()
163 cmds.difftool_launch(
164 self.context, left=left, right=right, paths=[path])
166 def diff(self, dir_diff=False):
167 paths = self.tree.selected_filenames()
168 left, right = self._left_right_args()
169 cmds.difftool_launch(self.context, left=left, right=right, paths=paths,
170 dir_diff=dir_diff)
172 def _left_right_args(self):
173 if self.diff_arg:
174 left = self.diff_arg[0]
175 else:
176 left = None
177 if len(self.diff_arg) > 1:
178 right = self.diff_arg[1]
179 else:
180 right = None
181 return (left, right)
183 def edit(self):
184 paths = self.tree.selected_filenames()
185 cmds.do(cmds.Edit, self.context, paths)