git-cola v2.11
[git-cola.git] / cola / difftool.py
blob6b4449909d52041fccf4ca28972c2bf890c43efd
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(parent, a, b):
20 """Show a dialog for diffing two commits"""
21 dlg = Difftool(parent, a=a, b=b)
22 dlg.show()
23 dlg.raise_()
24 return dlg.exec_() == QtWidgets.QDialog.Accepted
27 def diff_expression(parent, expr,
28 create_widget=False,
29 hide_expr=False,
30 focus_tree=False):
31 """Show a diff dialog for diff expressions"""
32 dlg = Difftool(parent,
33 expr=expr,
34 hide_expr=hide_expr,
35 focus_tree=focus_tree)
36 if create_widget:
37 return dlg
38 dlg.show()
39 dlg.raise_()
40 return dlg.exec_() == QtWidgets.QDialog.Accepted
43 class Difftool(standard.Dialog):
45 def __init__(self, parent, a=None, b=None, expr=None, title=None,
46 hide_expr=False, focus_tree=False):
47 """Show files with differences and launch difftool"""
49 standard.Dialog.__init__(self, parent=parent)
51 self.a = a
52 self.b = b
53 self.diff_expr = expr
55 if title is None:
56 title = N_('git-cola diff')
58 self.setWindowTitle(title)
59 self.setWindowModality(Qt.WindowModal)
61 self.expr = completion.GitRefLineEdit(parent=self)
62 if expr is not None:
63 self.expr.setText(expr)
65 if expr is None or hide_expr:
66 self.expr.hide()
68 self.tree = filetree.FileTree(parent=self)
70 self.diff_button = qtutils.create_button(text=N_('Compare'),
71 icon=icons.diff(),
72 enabled=False,
73 default=True)
74 self.diff_button.setShortcut(hotkeys.DIFF)
76 self.diff_all_button = qtutils.create_button(text=N_('Compare All'),
77 icon=icons.diff())
78 self.edit_button = qtutils.edit_button()
79 self.edit_button.setShortcut(hotkeys.EDIT)
81 self.close_button = qtutils.close_button()
83 self.button_layout = qtutils.hbox(defs.no_margin, defs.spacing,
84 self.close_button,
85 qtutils.STRETCH,
86 self.edit_button,
87 self.diff_all_button,
88 self.diff_button)
90 self.main_layout = qtutils.vbox(defs.margin, defs.spacing,
91 self.expr, self.tree,
92 self.button_layout)
93 self.setLayout(self.main_layout)
95 self.tree.itemSelectionChanged.connect(self.tree_selection_changed)
96 self.tree.itemDoubleClicked.connect(self.tree_double_clicked)
97 self.tree.up.connect(self.focus_input)
99 self.expr.textChanged.connect(self.text_changed)
101 self.expr.activated.connect(self.focus_tree)
102 self.expr.down.connect(self.focus_tree)
103 self.expr.enter.connect(self.focus_tree)
105 qtutils.connect_button(self.diff_button, self.diff)
106 qtutils.connect_button(self.diff_all_button,
107 lambda: self.diff(dir_diff=True))
108 qtutils.connect_button(self.edit_button, self.edit)
109 qtutils.connect_button(self.close_button, self.close)
111 qtutils.add_action(self, 'Focus Input', self.focus_input, hotkeys.FOCUS)
112 qtutils.add_action(self, 'Diff All', lambda: self.diff(dir_diff=True),
113 hotkeys.CTRL_ENTER, hotkeys.CTRL_RETURN)
114 qtutils.add_close_action(self)
116 self.init_state(None, self.resize_widget, parent)
118 self.refresh()
119 if focus_tree:
120 self.focus_tree()
122 def resize_widget(self, parent):
123 """Set the initial size of the widget"""
124 width, height = qtutils.default_size(parent, 720, 420)
125 self.resize(width, height)
127 def focus_tree(self):
128 """Focus the files tree"""
129 self.tree.setFocus()
131 def focus_input(self):
132 """Focus the expression input"""
133 self.expr.setFocus()
135 def text_changed(self, txt):
136 self.diff_expr = txt
137 self.refresh()
139 def refresh(self):
140 """Redo the diff when the expression changes"""
141 if self.diff_expr is not None:
142 self.diff_arg = utils.shell_split(self.diff_expr)
143 elif self.b is None:
144 self.diff_arg = [self.a]
145 else:
146 self.diff_arg = [self.a, self.b]
147 self.refresh_filenames()
149 def refresh_filenames(self):
150 if self.a and self.b is None:
151 filenames = gitcmds.diff_index_filenames(self.a)
152 else:
153 filenames = gitcmds.diff(self.diff_arg)
154 self.tree.set_filenames(filenames, select=True)
156 def tree_selection_changed(self):
157 has_selection = self.tree.has_selection()
158 self.diff_button.setEnabled(has_selection)
159 self.diff_all_button.setEnabled(has_selection)
161 def tree_double_clicked(self, item, column):
162 path = self.tree.filename_from_item(item)
163 left, right = self._left_right_args()
164 cmds.difftool_launch(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(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, paths)