cola: add more documentation strings to the cola modules
[git-cola.git] / cola / controllers / compare.py
blob74fc6eaa5474ca7354c05a846e851351b0489807
1 """This controller handles the compare commits dialog."""
3 import os
5 from cola import utils
6 from cola import qtutils
7 from cola.qobserver import QObserver
8 from cola.views import CompareView
9 from cola.views import BranchCompareView
10 from cola.controllers.repobrowser import select_file_from_repo
11 from cola.controllers.util import choose_from_list
13 def compare(model, parent):
14 model = model.clone()
15 model.create(descriptions_start=[], descriptions_end=[],
16 revisions_start=[], revisions_end=[],
17 revision_start='', revision_end='',
18 compare_files=[], num_results=100,
19 show_versions=False)
20 view = CompareView(parent)
21 ctl = CompareController(model, view)
22 view.show()
24 def branch_compare(model, parent):
25 model = model.clone()
26 model.create(left_combo=['Local', 'Remote'],
27 right_combo=['Local', 'Remote'],
28 left_combo_index=0,
29 right_combo_index=1,
30 left_list=[],
31 right_list=[],
32 left_list_index=-1,
33 right_list_index=-1,
34 left_list_selected=False,
35 right_list_selected=False,
36 diff_files=[])
37 view = BranchCompareView(parent)
38 ctl = BranchCompareController(model, view)
39 view.show()
41 class BranchCompareController(QObserver):
42 BRANCH_POINT = '*** Branch Point ***'
43 SANDBOX = '*** Sandbox ***'
45 def init(self, model, view):
46 self.add_observables('left_combo', 'right_combo',
47 'left_list', 'right_list',
48 'diff_files')
50 self.add_callbacks(button_compare =
51 self.diff_files_doubleclick,
52 left_combo =
53 self.gen_update_combo_boxes(left=True),
54 right_combo =
55 self.gen_update_combo_boxes(left=False),
56 left_list =
57 self.update_diff_files,
58 right_list =
59 self.update_diff_files)
60 self.refresh_view()
62 # Pre-select the 0th elements
63 self.view.right_combo.setCurrentIndex(1)
64 item = self.view.left_list.item(0)
65 self.view.left_list.setCurrentItem(item)
66 self.view.left_list.setItemSelected(item, True)
68 item = self.view.right_list.item(0)
69 self.view.right_list.setCurrentItem(item)
70 self.view.right_list.setItemSelected(item, True)
72 def update_diff_files(self, *rest):
73 if (not self.model.has_param('left_list_item') or
74 not self.model.has_param('right_list_item')):
75 return
76 left_item = self.model.get_left_list_item()
77 right_item = self.model.get_right_list_item()
78 if (not left_item or not right_item or
79 left_item == right_item):
80 self.model.set_diff_files([])
81 return
82 left_item = self.get_remote_ref(left_item)
83 right_item = self.get_remote_ref(right_item)
85 if (left_item == BranchCompareController.SANDBOX or
86 right_item == BranchCompareController.SANDBOX):
87 self.use_sandbox = True
88 if left_item == BranchCompareController.SANDBOX:
89 self.diff_arg = right_item
90 else:
91 self.diff_arg = left_item
92 else:
93 self.diff_arg = '%s..%s' % (left_item, right_item)
94 self.use_sandbox = False
96 self.start = left_item
97 self.end = right_item
99 files = self.model.get_diff_filenames(self.diff_arg)
100 self.model.set_diff_files(files)
101 icon = qtutils.get_icon('script.png')
102 for idx in xrange(0, self.view.diff_files.topLevelItemCount()):
103 item = self.view.diff_files.topLevelItem(idx)
104 item.setIcon(0, icon)
106 def get_diff_arg(self, start, end):
107 if start == BranchCompareController.SANDBOX:
108 return end
109 elif end == BranchCompareController.SANDBOX:
110 return start
111 else:
112 return '%s..%s' % (start, end)
114 def get_remote_ref(self, branch):
115 if branch == BranchCompareController.BRANCH_POINT:
116 # Compare against the branch point so find the merge-base
117 branch = self.model.get_currentbranch()
118 remote = self.model.get_corresponding_remote_ref()
119 return self.model.git.merge_base(branch, remote)
120 else:
121 # Compare against the remote branch
122 return branch
125 def gen_update_combo_boxes(self, left=False):
126 """Returns a closure which modifies the listwidgets based on the
127 combobox selection.
129 def update_combo_boxes(notused):
130 if left:
131 which = self.model.get_left_combo_item()
132 param = 'left_list'
133 else:
134 which = self.model.get_right_combo_item()
135 param = 'right_list'
136 if not which:
137 return
138 if which == 'Local':
139 new_list = ([BranchCompareController.SANDBOX]+
140 self.model.get_local_branches())
141 else:
142 new_list = ([BranchCompareController.BRANCH_POINT] +
143 self.model.get_remote_branches())
144 # Update the list widget
145 self.model.set_notify(True)
146 self.model.set_param(param, new_list)
148 return update_combo_boxes
150 def diff_files_doubleclick(self):
151 tree_widget = self.view.diff_files
152 id_num, selected = qtutils.get_selected_treeitem(tree_widget)
153 if not selected:
154 qtutils.information('Oops!', 'Please select a file to compare')
155 return
156 filename = self.model.get_diff_files()[id_num]
157 self.__compare_file(filename)
159 def __compare_file(self, filename):
160 git = self.model.git
161 if self.use_sandbox:
162 kwargs = git.transform_kwargs(no_prompt=True,
163 tool=self.model.get_mergetool(),
164 commit=self.diff_arg)
165 else:
166 kwargs = git.transform_kwargs(no_prompt=True,
167 tool=self.model.get_mergetool(),
168 start=self.start,
169 end=self.end)
170 args = (['git', 'difftool'] + kwargs + ['--', filename])
171 utils.fork(*args)
174 class CompareController(QObserver):
175 """Drives the Commit->Compare Commits dialog.
177 def init (self, model, view):
178 self.add_observables('descriptions_start', 'descriptions_end',
179 'revision_start', 'revision_end',
180 'compare_files', 'num_results',
181 'show_versions')
183 self.add_actions(num_results = self.update_results)
184 self.add_actions(show_versions = self.update_results)
186 self.add_callbacks(descriptions_start =
187 self.gen_update_widgets(True),
188 descriptions_end =
189 self.gen_update_widgets(False),
190 button_compare =
191 self.compare_selected_file)
192 self.refresh_view()
193 revisions = self.update_results()
194 last = len(revisions)
195 # if we can, preselect the latest commit
196 if (last > 1
197 and self.view.descriptions_start.topLevelItemCount() > last-1
198 and self.view.descriptions_end .topLevelItemCount() > last-1):
199 # select the 2nd item on the left treewidget
200 startitem = self.view.descriptions_start.topLevelItem(last-2)
201 self.view.descriptions_start.setCurrentItem(startitem)
202 self.view.descriptions_start.setItemSelected(startitem, True)
203 # select the 1st item on the right treewidget
204 enditem = self.view.descriptions_end.topLevelItem(last-1)
205 self.view.descriptions_end.setCurrentItem(enditem)
206 self.view.descriptions_end.setItemSelected(enditem, True)
208 def get_distance_from_end(self, tree_widget):
209 item_id, item_selected = qtutils.get_selected_treeitem(tree_widget)
210 if item_selected:
211 item_count = tree_widget.topLevelItemCount()
212 item_delta = item_count - item_id
213 return (item_selected, item_delta)
214 else:
215 return (item_selected, 0)
217 def select_nth_item_from_end(self, tree_widget, delta):
218 """selects an item relative to the end of the treeitem list.
219 We select from the end to properly handle changes in the number of
220 displayed commits."""
221 count = self.view.descriptions_start.topLevelItemCount()
222 idx = count - delta
223 qtutils.set_selected_item(tree_widget, idx)
225 def update_results(self, *args):
227 tree_widget = self.view.descriptions_start
228 start_selected, start_delta = self.get_distance_from_end(tree_widget)
230 tree_widget = self.view.descriptions_end
231 end_selected, end_delta = self.get_distance_from_end(tree_widget)
233 self.model.set_notify(True)
234 show_versions = self.model.get_show_versions()
235 revs = self.model.update_revision_lists(show_versions=show_versions)
236 if start_selected:
237 tree_widget = self.view.descriptions_start
238 self.select_nth_item_from_end(tree_widget, start_delta)
240 if end_selected:
241 tree_widget = self.view.descriptions_end
242 self.select_nth_item_from_end(tree_widget, end_delta)
244 return revs
246 def gen_update_widgets(self, left=True):
247 def update(*args):
248 self.update_widgets(left=left)
249 return update
251 def update_widgets(self, left=True):
252 if left:
253 tree_widget = self.view.descriptions_start
254 revisions_param = 'revisions_start'
255 revision_param = 'revision_start'
256 else:
257 tree_widget = self.view.descriptions_end
258 revisions_param = 'revisions_end'
259 revision_param = 'revision_end'
261 id_num, selected = qtutils.get_selected_treeitem(tree_widget)
262 if not selected:
263 return
264 revision = self.model.get_param(revisions_param)[id_num]
265 self.model.set_param(revision_param, revision)
267 # get the changed files list
268 start = self.model.get_revision_start()
269 end = self.model.get_revision_end()
270 files = self.model.get_changed_files(start, end)
272 # get the old name of any renamed files, and prune them
273 # from the changes list
274 renamed_files = self.model.get_renamed_files(start, end)
275 for renamed in renamed_files:
276 try:
277 files.remove(renamed)
278 except:
279 pass
280 self.model.set_compare_files(files)
281 icon = qtutils.get_icon('script.png')
282 for idx in xrange(0, self.view.compare_files.topLevelItemCount()):
283 item = self.view.compare_files.topLevelItem(idx)
284 item.setIcon(0, icon)
285 qtutils.set_clipboard(self.model.get_param(revision_param))
287 def compare_selected_file(self):
288 tree_widget = self.view.compare_files
289 id_num, selected = qtutils.get_selected_treeitem(tree_widget)
290 if not selected:
291 qtutils.information('Oops!', 'Please select a file to compare')
292 return
293 filename = self.model.get_compare_files()[id_num]
294 self.__compare_file(filename)
296 def compare_files_doubleclick(self, tree_item, column):
297 idx = self.view.compare_files.indexOfTopLevelItem(tree_item)
298 filename = self.model.get_compare_files()[idx]
299 self.__compare_file(filename)
301 def __compare_file(self, filename):
302 git = self.model.git
303 start = self.model.get_revision_start()
304 end = self.model.get_revision_end()
305 kwargs = git.transform_kwargs(no_prompt=True,
306 tool=self.model.get_mergetool(),
307 start=start,
308 end=end)
309 args = (['git', 'difftool'] + kwargs + ['--', filename])
310 utils.fork(*args)