Merge lp:~garyvdm/qbzr/newwtlist into lp:~qbzr-dev/qbzr/trunk
- newwtlist
- Merge into trunk
Proposed by
Gary van der Merwe
Status: | Superseded |
---|---|
Proposed branch: | lp:~garyvdm/qbzr/newwtlist |
Merge into: | lp:~qbzr-dev/qbzr/trunk |
Diff against target: | None lines |
To merge this branch: | bzr merge lp:~garyvdm/qbzr/newwtlist |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
QBzr Developers | Pending | ||
Review via email: mp+8639@code.launchpad.net |
This proposal has been superseded by a proposal from 2009-07-14.
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Gary van der Merwe (garyvdm) wrote : | # |
Revision history for this message
Gary van der Merwe (garyvdm) wrote : | # |
Revision history for this message
Gary van der Merwe (garyvdm) wrote : | # |
I just realised that this does not honer the selected list passed from the command line. I need to fix that.
lp:~garyvdm/qbzr/newwtlist
updated
- 814. By Gary van der Merwe
-
qcommit: Use SubProcessDialog, rather than SubProcessWindow.
- 815. By Gary van der Merwe
-
Set the width of the external diff menu button.
Revision history for this message
Gary van der Merwe (garyvdm) wrote : | # |
The above mentioned problem has now been fixed.
lp:~garyvdm/qbzr/newwtlist
updated
- 816. By Gary van der Merwe
-
Merge Javier's qsend dialog.
- 817. By Gary van der Merwe
-
qbrowse: Don't check for is_ignored on the tree root.
- 818. By Gary van der Merwe
-
qcommit: Show spelling suggestions in the message box context menu.
- 819. By Gary van der Merwe
-
Ext diff - Don't pass old revspec if none was provided for working trees.
- 820. By Gary van der Merwe
-
Change qcommit, qadd, and qrevert to use new TreeWidget. Remove lib/wtlist.py.
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/add.py' | |||
2 | --- lib/add.py 2009-06-11 21:21:01 +0000 | |||
3 | +++ lib/add.py 2009-07-10 19:43:43 +0000 | |||
4 | @@ -23,11 +23,16 @@ | |||
5 | 23 | 23 | ||
6 | 24 | from bzrlib.plugins.qbzr.lib.i18n import gettext | 24 | from bzrlib.plugins.qbzr.lib.i18n import gettext |
7 | 25 | from bzrlib.plugins.qbzr.lib.subprocess import SubProcessDialog | 25 | from bzrlib.plugins.qbzr.lib.subprocess import SubProcessDialog |
13 | 26 | from bzrlib.plugins.qbzr.lib.wtlist import ( | 26 | from bzrlib.plugins.qbzr.lib.treewidget import ( |
14 | 27 | ChangeDesc, | 27 | TreeWidget, |
15 | 28 | WorkingTreeFileList, | 28 | SelectAllCheckBox, |
16 | 29 | closure_in_selected_list, | 29 | ) |
17 | 30 | ) | 30 | from bzrlib.plugins.qbzr.lib.util import ( |
18 | 31 | ThrobberWidget, | ||
19 | 32 | runs_in_loading_queue, | ||
20 | 33 | ) | ||
21 | 34 | from bzrlib.plugins.qbzr.lib.uifactory import ui_current_widget | ||
22 | 35 | from bzrlib.plugins.qbzr.lib.trace import reports_exception | ||
23 | 31 | 36 | ||
24 | 32 | 37 | ||
25 | 33 | class AddWindow(SubProcessDialog): | 38 | class AddWindow(SubProcessDialog): |
26 | @@ -46,22 +51,36 @@ | |||
27 | 46 | hide_progress=True, | 51 | hide_progress=True, |
28 | 47 | ) | 52 | ) |
29 | 48 | 53 | ||
30 | 54 | self.throbber = ThrobberWidget(self) | ||
31 | 55 | |||
32 | 49 | # Display the list of unversioned files | 56 | # Display the list of unversioned files |
33 | 50 | groupbox = QtGui.QGroupBox(gettext("Unversioned Files"), self) | 57 | groupbox = QtGui.QGroupBox(gettext("Unversioned Files"), self) |
34 | 51 | vbox = QtGui.QVBoxLayout(groupbox) | 58 | vbox = QtGui.QVBoxLayout(groupbox) |
35 | 52 | 59 | ||
37 | 53 | self.filelist = WorkingTreeFileList(groupbox, self.tree) | 60 | self.filelist = TreeWidget(groupbox) |
38 | 61 | self.filelist.throbber = self.throbber | ||
39 | 62 | self.filelist.tree_model.is_item_in_select_all = lambda item: ( | ||
40 | 63 | item.change is not None and | ||
41 | 64 | item.change.is_ignored() is None and | ||
42 | 65 | not item.change.is_versioned()) | ||
43 | 66 | |||
44 | 67 | def filter_context_menu(): | ||
45 | 68 | self.filelist.action_open_file.setEnabled(True) | ||
46 | 69 | self.filelist.action_open_file.setVisible(True) | ||
47 | 70 | self.filelist.action_show_file.setVisible(False) | ||
48 | 71 | self.filelist.action_show_annotate.setVisible(False) | ||
49 | 72 | self.filelist.action_show_log.setVisible(False) | ||
50 | 73 | self.filelist.action_show_diff.setVisible(False) | ||
51 | 74 | self.filelist.action_add.setVisible(False) | ||
52 | 75 | self.filelist.action_revert.setVisible(False) | ||
53 | 76 | self.filelist.filter_context_menu = filter_context_menu | ||
54 | 77 | |||
55 | 54 | vbox.addWidget(self.filelist) | 78 | vbox.addWidget(self.filelist) |
56 | 55 | self.filelist.sortItems(0, QtCore.Qt.AscendingOrder) | ||
57 | 56 | self.filelist.setup_actions() | ||
58 | 57 | 79 | ||
62 | 58 | selectall_checkbox = QtGui.QCheckBox( | 80 | selectall_checkbox = SelectAllCheckBox(self.filelist, groupbox) |
60 | 59 | gettext(self.filelist.SELECTALL_MESSAGE), | ||
61 | 60 | groupbox) | ||
63 | 61 | vbox.addWidget(selectall_checkbox) | 81 | vbox.addWidget(selectall_checkbox) |
64 | 62 | selectall_checkbox.setCheckState(QtCore.Qt.Checked) | 82 | selectall_checkbox.setCheckState(QtCore.Qt.Checked) |
65 | 63 | selectall_checkbox.setEnabled(True) | 83 | selectall_checkbox.setEnabled(True) |
66 | 64 | self.filelist.set_selectall_checkbox(selectall_checkbox) | ||
67 | 65 | 84 | ||
68 | 66 | self.show_ignored_checkbox = QtGui.QCheckBox( | 85 | self.show_ignored_checkbox = QtGui.QCheckBox( |
69 | 67 | gettext("Show ignored files"), | 86 | gettext("Show ignored files"), |
70 | @@ -69,12 +88,6 @@ | |||
71 | 69 | vbox.addWidget(self.show_ignored_checkbox) | 88 | vbox.addWidget(self.show_ignored_checkbox) |
72 | 70 | self.connect(self.show_ignored_checkbox, QtCore.SIGNAL("toggled(bool)"), self.show_ignored) | 89 | self.connect(self.show_ignored_checkbox, QtCore.SIGNAL("toggled(bool)"), self.show_ignored) |
73 | 71 | 90 | ||
74 | 72 | self.tree.lock_read() | ||
75 | 73 | try: | ||
76 | 74 | self.filelist.fill(self.iter_changes_and_state()) | ||
77 | 75 | finally: | ||
78 | 76 | self.tree.unlock() | ||
79 | 77 | |||
80 | 78 | # groupbox gets disabled as we are executing. | 91 | # groupbox gets disabled as we are executing. |
81 | 79 | QtCore.QObject.connect(self, | 92 | QtCore.QObject.connect(self, |
82 | 80 | QtCore.SIGNAL("subprocessStarted(bool)"), | 93 | QtCore.SIGNAL("subprocessStarted(bool)"), |
83 | @@ -88,44 +101,40 @@ | |||
84 | 88 | self.restoreSplitterSizes([150, 150]) | 101 | self.restoreSplitterSizes([150, 150]) |
85 | 89 | 102 | ||
86 | 90 | layout = QtGui.QVBoxLayout(self) | 103 | layout = QtGui.QVBoxLayout(self) |
87 | 104 | layout.addWidget(self.throbber) | ||
88 | 91 | layout.addWidget(self.splitter) | 105 | layout.addWidget(self.splitter) |
89 | 92 | layout.addWidget(self.buttonbox) | 106 | layout.addWidget(self.buttonbox) |
109 | 93 | 107 | self.throbber.show() | |
110 | 94 | def iter_changes_and_state(self): | 108 | |
111 | 95 | """An iterator for the WorkingTreeFileList widget""" | 109 | |
112 | 96 | 110 | def show(self): | |
113 | 97 | in_selected_list = closure_in_selected_list(self.initial_selected_list) | 111 | SubProcessDialog.show(self) |
114 | 98 | 112 | QtCore.QTimer.singleShot(1, self.initial_load) | |
115 | 99 | show_ignored = self.show_ignored_checkbox.isChecked() | 113 | |
116 | 100 | 114 | @runs_in_loading_queue | |
117 | 101 | for desc in self.tree.iter_changes(self.tree.basis_tree(), | 115 | @ui_current_widget |
118 | 102 | want_unversioned=True): | 116 | @reports_exception() |
119 | 103 | 117 | def initial_load(self): | |
120 | 104 | desc = ChangeDesc(desc) | 118 | self.filelist.tree_model.checkable = True |
121 | 105 | if desc.is_versioned(): | 119 | fmodel = self.filelist.tree_filter_model |
122 | 106 | continue | 120 | fmodel.setFilter(fmodel.CHANGED, False) |
123 | 107 | 121 | self.filelist.set_tree(self.tree, changes_mode = True) | |
124 | 108 | pit = desc.path() | 122 | self.throbber.hide() |
106 | 109 | visible = show_ignored or not self.tree.is_ignored(pit) | ||
107 | 110 | check_state = visible and in_selected_list(pit) | ||
108 | 111 | yield desc, visible, check_state | ||
125 | 112 | 123 | ||
126 | 113 | def start(self): | 124 | def start(self): |
127 | 114 | """Add the files.""" | 125 | """Add the files.""" |
128 | 115 | files = [] | 126 | files = [] |
131 | 116 | for desc in self.filelist.iter_checked(): | 127 | for item_data in self.filelist.tree_model.iter_checked(): |
132 | 117 | files.append(desc.path()) | 128 | files.append(item_data.change.path()) |
133 | 118 | 129 | ||
135 | 119 | self.process_widget.start(self.tree.basedir, "add", *files) | 130 | self.process_widget.start(self.tree.basedir, "add", "--no-recurse", |
136 | 131 | *files) | ||
137 | 120 | 132 | ||
138 | 121 | def show_ignored(self, state): | 133 | def show_ignored(self, state): |
139 | 122 | """Show/hide ignored files.""" | 134 | """Show/hide ignored files.""" |
146 | 123 | state = not state | 135 | fmodel = self.filelist.tree_filter_model |
147 | 124 | for (tree_item, change_desc) in self.filelist.iter_treeitem_and_desc(True): | 136 | fmodel.setFilter(fmodel.IGNORED, state) |
148 | 125 | path = change_desc.path() | 137 | #self.filelist.update_selectall_state(None, None) |
143 | 126 | if self.tree.is_ignored(path): | ||
144 | 127 | self.filelist.set_item_hidden(tree_item, state) | ||
145 | 128 | self.filelist.update_selectall_state(None, None) | ||
149 | 129 | 138 | ||
150 | 130 | def saveSize(self): | 139 | def saveSize(self): |
151 | 131 | SubProcessDialog.saveSize(self) | 140 | SubProcessDialog.saveSize(self) |
152 | 132 | 141 | ||
153 | === modified file 'lib/commit.py' | |||
154 | --- lib/commit.py 2009-06-12 03:25:55 +0000 | |||
155 | +++ lib/commit.py 2009-07-12 14:01:37 +0000 | |||
156 | @@ -36,15 +36,18 @@ | |||
157 | 36 | get_global_config, | 36 | get_global_config, |
158 | 37 | url_for_display, | 37 | url_for_display, |
159 | 38 | ThrobberWidget, | 38 | ThrobberWidget, |
166 | 39 | ) | 39 | runs_in_loading_queue, |
167 | 40 | from bzrlib.plugins.qbzr.lib.wtlist import ( | 40 | ) |
168 | 41 | ChangeDesc, | 41 | from bzrlib.plugins.qbzr.lib.treewidget import TreeWidget |
169 | 42 | WorkingTreeFileList, | 42 | |
164 | 43 | closure_in_selected_list, | ||
165 | 44 | ) | ||
170 | 45 | from bzrlib.plugins.qbzr.lib.logwidget import LogList | 43 | from bzrlib.plugins.qbzr.lib.logwidget import LogList |
171 | 46 | from bzrlib.plugins.qbzr.lib.trace import * | 44 | from bzrlib.plugins.qbzr.lib.trace import * |
172 | 47 | from bzrlib.plugins.qbzr.lib.uifactory import ui_current_widget | 45 | from bzrlib.plugins.qbzr.lib.uifactory import ui_current_widget |
173 | 46 | from bzrlib.plugins.qbzr.lib.treewidget import ( | ||
174 | 47 | TreeWidget, | ||
175 | 48 | SelectAllCheckBox, | ||
176 | 49 | ) | ||
177 | 50 | from bzrlib.plugins.qbzr.lib.trace import reports_exception | ||
178 | 48 | 51 | ||
179 | 49 | 52 | ||
180 | 50 | MAX_AUTOCOMPLETE_FILES = 20 | 53 | MAX_AUTOCOMPLETE_FILES = 20 |
181 | @@ -143,55 +146,6 @@ | |||
182 | 143 | RevisionIdRole = QtCore.Qt.UserRole + 1 | 146 | RevisionIdRole = QtCore.Qt.UserRole + 1 |
183 | 144 | ParentIdRole = QtCore.Qt.UserRole + 2 | 147 | ParentIdRole = QtCore.Qt.UserRole + 2 |
184 | 145 | 148 | ||
185 | 146 | def iter_changes_and_state(self): | ||
186 | 147 | """An iterator for the WorkingTreeFileList widget""" | ||
187 | 148 | # a word list for message completer | ||
188 | 149 | words = set() | ||
189 | 150 | show_nonversioned = self.show_nonversioned_checkbox.isChecked() | ||
190 | 151 | |||
191 | 152 | in_selected_list = closure_in_selected_list(self.initial_selected_list) | ||
192 | 153 | |||
193 | 154 | num_versioned_files = 0 | ||
194 | 155 | for desc in self.tree.iter_changes(self.tree.basis_tree(), | ||
195 | 156 | want_unversioned=True): | ||
196 | 157 | desc = ChangeDesc(desc) | ||
197 | 158 | |||
198 | 159 | if desc.is_tree_root() or desc.is_misadded(): | ||
199 | 160 | # skip uninteresting enties | ||
200 | 161 | continue | ||
201 | 162 | |||
202 | 163 | is_versioned = desc.is_versioned() | ||
203 | 164 | path = desc.path() | ||
204 | 165 | |||
205 | 166 | if not is_versioned and self.tree.is_ignored(path): | ||
206 | 167 | continue | ||
207 | 168 | |||
208 | 169 | visible = is_versioned or show_nonversioned | ||
209 | 170 | check_state = None | ||
210 | 171 | if not self.has_pending_merges: | ||
211 | 172 | check_state = visible and is_versioned and in_selected_list(path) | ||
212 | 173 | yield desc, visible, check_state | ||
213 | 174 | |||
214 | 175 | if is_versioned: | ||
215 | 176 | num_versioned_files += 1 | ||
216 | 177 | |||
217 | 178 | words.update(os.path.split(path)) | ||
218 | 179 | if desc.is_renamed(): | ||
219 | 180 | words.update(os.path.split(desc.oldpath())) | ||
220 | 181 | if num_versioned_files < MAX_AUTOCOMPLETE_FILES: | ||
221 | 182 | ext = file_extension(path) | ||
222 | 183 | builder = get_wordlist_builder(ext) | ||
223 | 184 | if builder is not None: | ||
224 | 185 | try: | ||
225 | 186 | abspath = os.path.join(self.tree.basedir, path) | ||
226 | 187 | file = open(abspath, 'rt') | ||
227 | 188 | words.update(builder.iter_words(file)) | ||
228 | 189 | except EnvironmentError: | ||
229 | 190 | pass | ||
230 | 191 | words = list(words) | ||
231 | 192 | words.sort(lambda a, b: cmp(a.lower(), b.lower())) | ||
232 | 193 | self.completion_words = words | ||
233 | 194 | |||
234 | 195 | def __init__(self, tree, selected_list, dialog=True, parent=None, | 149 | def __init__(self, tree, selected_list, dialog=True, parent=None, |
235 | 196 | local=None, message=None, ui_mode=True): | 150 | local=None, message=None, ui_mode=True): |
236 | 197 | super(CommitWindow, self).__init__( | 151 | super(CommitWindow, self).__init__( |
237 | @@ -202,13 +156,13 @@ | |||
238 | 202 | dialog = dialog, | 156 | dialog = dialog, |
239 | 203 | parent = parent) | 157 | parent = parent) |
240 | 204 | self.tree = tree | 158 | self.tree = tree |
248 | 205 | tree.lock_read() | 159 | #tree.lock_read() |
249 | 206 | try: | 160 | #try: |
250 | 207 | self.basis_tree = self.tree.basis_tree() | 161 | # self.basis_tree = self.tree.basis_tree() |
251 | 208 | self.is_bound = bool(tree.branch.get_bound_location()) | 162 | self.is_bound = bool(tree.branch.get_bound_location()) |
252 | 209 | self.has_pending_merges = len(tree.get_parent_ids())>1 | 163 | self.has_pending_merges = len(tree.get_parent_ids())>1 |
253 | 210 | finally: | 164 | #finally: |
254 | 211 | tree.unlock() | 165 | # tree.unlock() |
255 | 212 | 166 | ||
256 | 213 | self.windows = [] | 167 | self.windows = [] |
257 | 214 | self.initial_selected_list = selected_list | 168 | self.initial_selected_list = selected_list |
258 | @@ -263,16 +217,27 @@ | |||
259 | 263 | self.show_nonversioned_checkbox = QtGui.QCheckBox( | 217 | self.show_nonversioned_checkbox = QtGui.QCheckBox( |
260 | 264 | gettext("Show non-versioned files")) | 218 | gettext("Show non-versioned files")) |
261 | 265 | 219 | ||
265 | 266 | self.filelist = WorkingTreeFileList(message_groupbox, self.tree) | 220 | self.filelist = TreeWidget(self) |
266 | 267 | selectall_checkbox = QtGui.QCheckBox( | 221 | self.filelist.throbber = self.throbber |
267 | 268 | gettext(self.filelist.SELECTALL_MESSAGE)) | 222 | self.filelist.tree_model.is_item_in_select_all = lambda item: ( |
268 | 223 | item.change is not None and | ||
269 | 224 | item.change.is_ignored() is None and | ||
270 | 225 | item.change.is_versioned()) | ||
271 | 226 | |||
272 | 227 | self.file_words = {} | ||
273 | 228 | self.connect(self.filelist.tree_model, | ||
274 | 229 | QtCore.SIGNAL("dataChanged(QModelIndex, QModelIndex)"), | ||
275 | 230 | self.on_filelist_data_changed) | ||
276 | 231 | |||
277 | 232 | selectall_checkbox = SelectAllCheckBox(self.filelist, self) | ||
278 | 269 | selectall_checkbox.setCheckState(QtCore.Qt.Checked) | 233 | selectall_checkbox.setCheckState(QtCore.Qt.Checked) |
279 | 270 | self.filelist.set_selectall_checkbox(selectall_checkbox) | ||
280 | 271 | 234 | ||
281 | 272 | # Equivalent for 'bzr commit --message' | 235 | # Equivalent for 'bzr commit --message' |
282 | 273 | self.message = TextEdit(message_groupbox, main_window=self) | 236 | self.message = TextEdit(message_groupbox, main_window=self) |
283 | 274 | self.message.setToolTip(gettext("Enter the commit message")) | 237 | self.message.setToolTip(gettext("Enter the commit message")) |
284 | 275 | self.completer = QtGui.QCompleter() | 238 | self.completer = QtGui.QCompleter() |
285 | 239 | self.completer_model = QtGui.QStringListModel(self.completer) | ||
286 | 240 | self.completer.setModel(self.completer_model) | ||
287 | 276 | self.message.setCompleter(self.completer) | 241 | self.message.setCompleter(self.completer) |
288 | 277 | self.message.setAcceptRichText(False) | 242 | self.message.setAcceptRichText(False) |
289 | 278 | 243 | ||
290 | @@ -328,8 +293,6 @@ | |||
291 | 328 | 293 | ||
292 | 329 | vbox.addWidget(selectall_checkbox) | 294 | vbox.addWidget(selectall_checkbox) |
293 | 330 | 295 | ||
294 | 331 | self.filelist.sortItems(0, QtCore.Qt.AscendingOrder) | ||
295 | 332 | |||
296 | 333 | # Display a list of pending merges | 296 | # Display a list of pending merges |
297 | 334 | if self.has_pending_merges: | 297 | if self.has_pending_merges: |
298 | 335 | selectall_checkbox.setCheckState(QtCore.Qt.Checked) | 298 | selectall_checkbox.setCheckState(QtCore.Qt.Checked) |
299 | @@ -392,7 +355,8 @@ | |||
300 | 392 | # we show the bare form as soon as possible. | 355 | # we show the bare form as soon as possible. |
301 | 393 | SubProcessWindow.show(self) | 356 | SubProcessWindow.show(self) |
302 | 394 | QtCore.QTimer.singleShot(1, self.load) | 357 | QtCore.QTimer.singleShot(1, self.load) |
304 | 395 | 358 | ||
305 | 359 | @runs_in_loading_queue | ||
306 | 396 | @ui_current_widget | 360 | @ui_current_widget |
307 | 397 | @reports_exception() | 361 | @reports_exception() |
308 | 398 | def load(self): | 362 | def load(self): |
309 | @@ -403,14 +367,56 @@ | |||
310 | 403 | None, | 367 | None, |
311 | 404 | self.tree) | 368 | self.tree) |
312 | 405 | self.pending_merges_list.load() | 369 | self.pending_merges_list.load() |
314 | 406 | self.filelist.fill(self.iter_changes_and_state()) | 370 | self.processEvents() |
315 | 371 | |||
316 | 372 | self.filelist.tree_model.checkable = True | ||
317 | 373 | fmodel = self.filelist.tree_filter_model | ||
318 | 374 | fmodel.setFilter(fmodel.UNVERSIONED, False) | ||
319 | 375 | self.filelist.set_tree(self.tree, changes_mode = True) | ||
320 | 376 | self.processEvents() | ||
321 | 377 | self.update_compleater_words() | ||
322 | 407 | finally: | 378 | finally: |
323 | 408 | self.tree.unlock() | 379 | self.tree.unlock() |
324 | 409 | 380 | ||
325 | 410 | self.filelist.setup_actions() | ||
326 | 411 | self.completer.setModel(QtGui.QStringListModel(self.completion_words, | ||
327 | 412 | self.completer)) | ||
328 | 413 | self.throbber.hide() | 381 | self.throbber.hide() |
329 | 382 | |||
330 | 383 | def on_filelist_data_changed(self, start_index, end_index): | ||
331 | 384 | self.update_compleater_words() | ||
332 | 385 | |||
333 | 386 | def update_compleater_words(self): | ||
334 | 387 | num_files_loaded = 0 | ||
335 | 388 | |||
336 | 389 | words = set() | ||
337 | 390 | for item_data in self.filelist.tree_model.iter_checked(): | ||
338 | 391 | path = item_data.change.path() | ||
339 | 392 | if path not in self.file_words: | ||
340 | 393 | file_words = set() | ||
341 | 394 | if num_files_loaded < MAX_AUTOCOMPLETE_FILES: | ||
342 | 395 | file_words.add(path) | ||
343 | 396 | file_words.add(os.path.split(path)[-1]) | ||
344 | 397 | if item_data.change.is_renamed(): | ||
345 | 398 | file_words.add(item_data.change.oldpath()) | ||
346 | 399 | file_words.add( | ||
347 | 400 | os.path.split(item_data.change.oldpath())[-1]) | ||
348 | 401 | #if num_versioned_files < MAX_AUTOCOMPLETE_FILES: | ||
349 | 402 | ext = file_extension(path) | ||
350 | 403 | builder = get_wordlist_builder(ext) | ||
351 | 404 | if builder is not None: | ||
352 | 405 | try: | ||
353 | 406 | abspath = os.path.join(self.tree.basedir, path) | ||
354 | 407 | file = open(abspath, 'rt') | ||
355 | 408 | file_words.update(builder.iter_words(file)) | ||
356 | 409 | self.processEvents() | ||
357 | 410 | except EnvironmentError: | ||
358 | 411 | pass | ||
359 | 412 | self.file_words[path] = file_words | ||
360 | 413 | num_files_loaded += 1 | ||
361 | 414 | else: | ||
362 | 415 | file_words = self.file_words[path] | ||
363 | 416 | words.update(file_words) | ||
364 | 417 | words = list(words) | ||
365 | 418 | words.sort(lambda a, b: cmp(a.lower(), b.lower())) | ||
366 | 419 | self.completer_model.setStringList(words) | ||
367 | 414 | 420 | ||
368 | 415 | def enableBugs(self, state): | 421 | def enableBugs(self, state): |
369 | 416 | if state == QtCore.Qt.Checked: | 422 | if state == QtCore.Qt.Checked: |
370 | @@ -460,7 +466,7 @@ | |||
371 | 460 | 466 | ||
372 | 461 | def start(self): | 467 | def start(self): |
373 | 462 | args = ["commit"] | 468 | args = ["commit"] |
375 | 463 | files_to_add = ["add"] | 469 | files_to_add = ["add", "--no-recurse"] |
376 | 464 | 470 | ||
377 | 465 | message = unicode(self.message.toPlainText()).strip() | 471 | message = unicode(self.message.toPlainText()).strip() |
378 | 466 | if not message: | 472 | if not message: |
379 | @@ -479,12 +485,12 @@ | |||
380 | 479 | checkedFiles = 1 | 485 | checkedFiles = 1 |
381 | 480 | if not self.has_pending_merges: | 486 | if not self.has_pending_merges: |
382 | 481 | checkedFiles = 0 | 487 | checkedFiles = 0 |
387 | 482 | for desc in self.filelist.iter_checked(): | 488 | for item_data in self.filelist.tree_model.iter_checked(): |
388 | 483 | checkedFiles = checkedFiles+1 | 489 | path = item_data.change.path() |
389 | 484 | path = desc.path() | 490 | if not item_data.change.is_versioned(): |
386 | 485 | if not desc.is_versioned(): | ||
390 | 486 | files_to_add.append(path) | 491 | files_to_add.append(path) |
391 | 487 | args.append(path) | 492 | args.append(path) |
392 | 493 | checkedFiles = 1 | ||
393 | 488 | 494 | ||
394 | 489 | if checkedFiles == 0: # BUG: 295116 | 495 | if checkedFiles == 0: # BUG: 295116 |
395 | 490 | # check for availability of --exclude option for commit | 496 | # check for availability of --exclude option for commit |
396 | @@ -540,11 +546,8 @@ | |||
397 | 540 | 546 | ||
398 | 541 | def show_nonversioned(self, state): | 547 | def show_nonversioned(self, state): |
399 | 542 | """Show/hide non-versioned files.""" | 548 | """Show/hide non-versioned files.""" |
405 | 543 | state = not state | 549 | fmodel = self.filelist.tree_filter_model |
406 | 544 | for (tree_item, change_desc) in self.filelist.iter_treeitem_and_desc(True): | 550 | fmodel.setFilter(fmodel.UNVERSIONED, state) |
402 | 545 | if change_desc[3] == (False, False): | ||
403 | 546 | self.filelist.set_item_hidden(tree_item, state) | ||
404 | 547 | self.filelist.update_selectall_state(None, None) | ||
407 | 548 | 551 | ||
408 | 549 | def closeEvent(self, event): | 552 | def closeEvent(self, event): |
409 | 550 | if not self.process_widget.is_running(): | 553 | if not self.process_widget.is_running(): |
410 | @@ -580,9 +583,9 @@ | |||
411 | 580 | # XXX make this function universal for both qcommit and qrevert (?) | 583 | # XXX make this function universal for both qcommit and qrevert (?) |
412 | 581 | checked = [] # checked versioned | 584 | checked = [] # checked versioned |
413 | 582 | unversioned = [] # checked unversioned (supposed to be added) | 585 | unversioned = [] # checked unversioned (supposed to be added) |
417 | 583 | for desc in self.filelist.iter_checked(): | 586 | for item_data in self.filelist.tree_model.iter_checked(): |
418 | 584 | path = desc.path() | 587 | path = item_data.change.path() |
419 | 585 | if desc.is_versioned(): | 588 | if item_data.change.is_versioned(): |
420 | 586 | checked.append(path) | 589 | checked.append(path) |
421 | 587 | else: | 590 | else: |
422 | 588 | unversioned.append(path) | 591 | unversioned.append(path) |
423 | 589 | 592 | ||
424 | === modified file 'lib/diff.py' | |||
425 | --- lib/diff.py 2009-06-11 21:21:01 +0000 | |||
426 | +++ lib/diff.py 2009-07-10 05:16:40 +0000 | |||
427 | @@ -69,6 +69,7 @@ | |||
428 | 69 | window.process_widget.hide_progress() | 69 | window.process_widget.hide_progress() |
429 | 70 | if parent_window: | 70 | if parent_window: |
430 | 71 | parent_window.windows.append(window) | 71 | parent_window.windows.append(window) |
431 | 72 | window.show() | ||
432 | 72 | 73 | ||
433 | 73 | 74 | ||
434 | 74 | def has_ext_diff(): | 75 | def has_ext_diff(): |
435 | 75 | 76 | ||
436 | === modified file 'lib/diff_arg.py' | |||
437 | --- lib/diff_arg.py 2009-06-23 00:38:58 +0000 | |||
438 | +++ lib/diff_arg.py 2009-07-10 05:16:40 +0000 | |||
439 | @@ -93,7 +93,9 @@ | |||
440 | 93 | from bzrlib import urlutils | 93 | from bzrlib import urlutils |
441 | 94 | 94 | ||
442 | 95 | args = [] | 95 | args = [] |
444 | 96 | args.append(self.get_revspec()) | 96 | revspec = self.get_revspec() |
445 | 97 | if revspec: | ||
446 | 98 | args.append(revspec) | ||
447 | 97 | 99 | ||
448 | 98 | if not self.old_branch.base == self.new_branch.base: | 100 | if not self.old_branch.base == self.new_branch.base: |
449 | 99 | args.append("--old=%s" % self.old_branch.base) | 101 | args.append("--old=%s" % self.old_branch.base) |
450 | @@ -138,7 +140,10 @@ | |||
451 | 138 | self.specific_files) | 140 | self.specific_files) |
452 | 139 | 141 | ||
453 | 140 | def get_revspec(self): | 142 | def get_revspec(self): |
455 | 141 | return "-r revid:%s" % (self.old_revid,) | 143 | if self.old_revid is not None: |
456 | 144 | return "-r revid:%s" % (self.old_revid,) | ||
457 | 145 | else: | ||
458 | 146 | return None | ||
459 | 142 | 147 | ||
460 | 143 | def need_to_load_paths(self): | 148 | def need_to_load_paths(self): |
461 | 144 | return False | 149 | return False |
462 | 145 | 150 | ||
463 | === modified file 'lib/revert.py' | |||
464 | --- lib/revert.py 2009-06-11 21:21:01 +0000 | |||
465 | +++ lib/revert.py 2009-07-10 19:43:43 +0000 | |||
466 | @@ -28,11 +28,16 @@ | |||
467 | 28 | ) | 28 | ) |
468 | 29 | from bzrlib.plugins.qbzr.lib.i18n import gettext | 29 | from bzrlib.plugins.qbzr.lib.i18n import gettext |
469 | 30 | from bzrlib.plugins.qbzr.lib.subprocess import SubProcessDialog | 30 | from bzrlib.plugins.qbzr.lib.subprocess import SubProcessDialog |
475 | 31 | from bzrlib.plugins.qbzr.lib.wtlist import ( | 31 | from bzrlib.plugins.qbzr.lib.treewidget import ( |
476 | 32 | ChangeDesc, | 32 | TreeWidget, |
477 | 33 | WorkingTreeFileList, | 33 | SelectAllCheckBox, |
478 | 34 | closure_in_selected_list, | 34 | ) |
479 | 35 | ) | 35 | from bzrlib.plugins.qbzr.lib.util import ( |
480 | 36 | ThrobberWidget, | ||
481 | 37 | runs_in_loading_queue, | ||
482 | 38 | ) | ||
483 | 39 | from bzrlib.plugins.qbzr.lib.uifactory import ui_current_widget | ||
484 | 40 | from bzrlib.plugins.qbzr.lib.trace import reports_exception | ||
485 | 36 | 41 | ||
486 | 37 | 42 | ||
487 | 38 | class RevertWindow(SubProcessDialog): | 43 | class RevertWindow(SubProcessDialog): |
488 | @@ -51,26 +56,27 @@ | |||
489 | 51 | parent = parent, | 56 | parent = parent, |
490 | 52 | hide_progress=True) | 57 | hide_progress=True) |
491 | 53 | 58 | ||
492 | 59 | self.throbber = ThrobberWidget(self) | ||
493 | 60 | |||
494 | 54 | # Display the list of changed files | 61 | # Display the list of changed files |
495 | 55 | groupbox = QtGui.QGroupBox(gettext("Changes"), self) | 62 | groupbox = QtGui.QGroupBox(gettext("Changes"), self) |
496 | 56 | 63 | ||
506 | 57 | self.filelist = WorkingTreeFileList(groupbox, self.tree) | 64 | self.filelist = TreeWidget(groupbox) |
507 | 58 | 65 | self.filelist.throbber = self.throbber | |
508 | 59 | self.tree.lock_read() | 66 | self.filelist.tree_model.is_item_in_select_all = lambda item: ( |
509 | 60 | try: | 67 | item.change is not None and item.change.is_versioned()) |
510 | 61 | self.filelist.fill(self.iter_changes_and_state()) | 68 | self.filelist.setRootIsDecorated(False) |
511 | 62 | finally: | 69 | def filter_context_menu(): |
512 | 63 | self.tree.unlock() | 70 | TreeWidget.filter_context_menu(self.filelist) |
513 | 64 | 71 | self.filelist.action_add.setVisible(False) | |
514 | 65 | self.filelist.setup_actions() | 72 | self.filelist.action_revert.setVisible(False) |
515 | 73 | self.filelist.filter_context_menu = filter_context_menu | ||
516 | 66 | 74 | ||
517 | 67 | vbox = QtGui.QVBoxLayout(groupbox) | 75 | vbox = QtGui.QVBoxLayout(groupbox) |
518 | 68 | vbox.addWidget(self.filelist) | 76 | vbox.addWidget(self.filelist) |
521 | 69 | selectall_checkbox = QtGui.QCheckBox( | 77 | selectall_checkbox = SelectAllCheckBox(self.filelist, groupbox) |
520 | 70 | gettext(self.filelist.SELECTALL_MESSAGE)) | ||
522 | 71 | selectall_checkbox.setCheckState(QtCore.Qt.Checked) | 78 | selectall_checkbox.setCheckState(QtCore.Qt.Checked) |
523 | 72 | selectall_checkbox.setEnabled(True) | 79 | selectall_checkbox.setEnabled(True) |
524 | 73 | self.filelist.set_selectall_checkbox(selectall_checkbox) | ||
525 | 74 | vbox.addWidget(selectall_checkbox) | 80 | vbox.addWidget(selectall_checkbox) |
526 | 75 | 81 | ||
527 | 76 | self.no_backup_checkbox = QtGui.QCheckBox( | 82 | self.no_backup_checkbox = QtGui.QCheckBox( |
528 | @@ -80,8 +86,6 @@ | |||
529 | 80 | self.no_backup_checkbox.setEnabled(True) | 86 | self.no_backup_checkbox.setEnabled(True) |
530 | 81 | vbox.addWidget(self.no_backup_checkbox) | 87 | vbox.addWidget(self.no_backup_checkbox) |
531 | 82 | 88 | ||
532 | 83 | self.filelist.sortItems(0, QtCore.Qt.AscendingOrder) | ||
533 | 84 | |||
534 | 85 | # groupbox gets disabled as we are executing. | 89 | # groupbox gets disabled as we are executing. |
535 | 86 | QtCore.QObject.connect(self, | 90 | QtCore.QObject.connect(self, |
536 | 87 | QtCore.SIGNAL("subprocessStarted(bool)"), | 91 | QtCore.SIGNAL("subprocessStarted(bool)"), |
537 | @@ -95,6 +99,7 @@ | |||
538 | 95 | self.restoreSplitterSizes([150, 150]) | 99 | self.restoreSplitterSizes([150, 150]) |
539 | 96 | 100 | ||
540 | 97 | layout = QtGui.QVBoxLayout(self) | 101 | layout = QtGui.QVBoxLayout(self) |
541 | 102 | layout.addWidget(self.throbber) | ||
542 | 98 | layout.addWidget(self.splitter) | 103 | layout.addWidget(self.splitter) |
543 | 99 | 104 | ||
544 | 100 | # Diff button to view changes in files selected to revert | 105 | # Diff button to view changes in files selected to revert |
545 | @@ -107,27 +112,31 @@ | |||
546 | 107 | hbox.addWidget(self.diffbuttons) | 112 | hbox.addWidget(self.diffbuttons) |
547 | 108 | hbox.addWidget(self.buttonbox) | 113 | hbox.addWidget(self.buttonbox) |
548 | 109 | layout.addLayout(hbox) | 114 | layout.addLayout(hbox) |
562 | 110 | 115 | self.throbber.show() | |
563 | 111 | def iter_changes_and_state(self): | 116 | |
564 | 112 | """An iterator for the WorkingTreeFileList widget""" | 117 | |
565 | 113 | 118 | def show(self): | |
566 | 114 | in_selected_list = closure_in_selected_list(self.initial_selected_list) | 119 | SubProcessDialog.show(self) |
567 | 115 | 120 | QtCore.QTimer.singleShot(1, self.initial_load) | |
568 | 116 | for desc in self.tree.iter_changes(self.tree.basis_tree()): | 121 | |
569 | 117 | desc = ChangeDesc(desc) | 122 | @runs_in_loading_queue |
570 | 118 | if desc.is_tree_root(): | 123 | @ui_current_widget |
571 | 119 | continue | 124 | @reports_exception() |
572 | 120 | path = desc.path() | 125 | def initial_load(self): |
573 | 121 | check_state = in_selected_list(path) | 126 | self.filelist.tree_model.checkable = True |
574 | 122 | yield desc, True, check_state | 127 | fmodel = self.filelist.tree_filter_model |
575 | 128 | #fmodel.setFilter(fmodel.UNVERSIONED, False) | ||
576 | 129 | self.filelist.set_tree(self.tree, changes_mode=True, | ||
577 | 130 | want_unversioned=False) | ||
578 | 131 | self.throbber.hide() | ||
579 | 123 | 132 | ||
580 | 124 | def start(self): | 133 | def start(self): |
581 | 125 | """Revert the files.""" | 134 | """Revert the files.""" |
582 | 126 | args = ["revert"] | 135 | args = ["revert"] |
583 | 127 | if self.no_backup_checkbox.checkState(): | 136 | if self.no_backup_checkbox.checkState(): |
584 | 128 | args.append("--no-backup") | 137 | args.append("--no-backup") |
587 | 129 | for desc in self.filelist.iter_checked(): | 138 | for item_data in self.filelist.tree_model.iter_checked(): |
588 | 130 | args.append(desc.path()) | 139 | args.append(item_data.change.path()) |
589 | 131 | self.process_widget.start(self.tree.basedir, *args) | 140 | self.process_widget.start(self.tree.basedir, *args) |
590 | 132 | 141 | ||
591 | 133 | def saveSize(self): | 142 | def saveSize(self): |
592 | 134 | 143 | ||
593 | === modified file 'lib/revtreeview.py' | |||
594 | --- lib/revtreeview.py 2009-07-08 16:20:59 +0000 | |||
595 | +++ lib/revtreeview.py 2009-07-10 01:04:24 +0000 | |||
596 | @@ -102,6 +102,8 @@ | |||
597 | 102 | break | 102 | break |
598 | 103 | 103 | ||
599 | 104 | revids = list(revids) | 104 | revids = list(revids) |
600 | 105 | if len(revids) == 0: | ||
601 | 106 | return | ||
602 | 105 | 107 | ||
603 | 106 | self.load_revisions_call_count += 1 | 108 | self.load_revisions_call_count += 1 |
604 | 107 | current_call_count = self.load_revisions_call_count | 109 | current_call_count = self.load_revisions_call_count |
605 | 108 | 110 | ||
606 | === modified file 'lib/tests/modeltest.py' | |||
607 | --- lib/tests/modeltest.py 2009-07-09 01:28:49 +0000 | |||
608 | +++ lib/tests/modeltest.py 2009-07-10 01:54:38 +0000 | |||
609 | @@ -431,7 +431,8 @@ | |||
610 | 431 | 431 | ||
611 | 432 | # Check that we can get back our real parent | 432 | # Check that we can get back our real parent |
612 | 433 | p = self.model.parent( index ) | 433 | p = self.model.parent( index ) |
614 | 434 | assert( self.model.parent( index ) == parent ) | 434 | assert( p.internalId() == parent.internalId() ) |
615 | 435 | assert( p.row() == parent.row() ) | ||
616 | 435 | 436 | ||
617 | 436 | # recursively go down the children | 437 | # recursively go down the children |
618 | 437 | if self.model.hasChildren(index) and depth < 10: | 438 | if self.model.hasChildren(index) and depth < 10: |
619 | 438 | 439 | ||
620 | === modified file 'lib/treewidget.py' | |||
621 | --- lib/treewidget.py 2009-07-10 00:27:28 +0000 | |||
622 | +++ lib/treewidget.py 2009-07-12 13:46:24 +0000 | |||
623 | @@ -21,6 +21,7 @@ | |||
624 | 21 | from time import (strftime, localtime) | 21 | from time import (strftime, localtime) |
625 | 22 | from PyQt4 import QtCore, QtGui | 22 | from PyQt4 import QtCore, QtGui |
626 | 23 | from bzrlib.workingtree import WorkingTree | 23 | from bzrlib.workingtree import WorkingTree |
627 | 24 | from bzrlib.revisiontree import RevisionTree | ||
628 | 24 | 25 | ||
629 | 25 | from bzrlib.plugins.qbzr.lib.cat import QBzrCatWindow | 26 | from bzrlib.plugins.qbzr.lib.cat import QBzrCatWindow |
630 | 26 | from bzrlib.plugins.qbzr.lib.annotate import AnnotateWindow | 27 | from bzrlib.plugins.qbzr.lib.annotate import AnnotateWindow |
631 | @@ -37,7 +38,6 @@ | |||
632 | 37 | get_apparent_author_name, | 38 | get_apparent_author_name, |
633 | 38 | ) | 39 | ) |
634 | 39 | from bzrlib.plugins.qbzr.lib.uifactory import ui_current_widget | 40 | from bzrlib.plugins.qbzr.lib.uifactory import ui_current_widget |
635 | 40 | from bzrlib.plugins.qbzr.lib.wtlist import ChangeDesc | ||
636 | 41 | from bzrlib.plugins.qbzr.lib.subprocess import SimpleSubProcessDialog | 41 | from bzrlib.plugins.qbzr.lib.subprocess import SimpleSubProcessDialog |
637 | 42 | from bzrlib.plugins.qbzr.lib.diff import ( | 42 | from bzrlib.plugins.qbzr.lib.diff import ( |
638 | 43 | show_diff, | 43 | show_diff, |
639 | @@ -45,18 +45,159 @@ | |||
640 | 45 | ExtDiffMenu, | 45 | ExtDiffMenu, |
641 | 46 | InternalWTDiffArgProvider, | 46 | InternalWTDiffArgProvider, |
642 | 47 | ) | 47 | ) |
649 | 48 | 48 | from bzrlib.plugins.qbzr.lib.i18n import gettext, N_ | |
650 | 49 | 49 | ||
651 | 50 | 50 | ||
652 | 51 | class UnversionedItem(): | 51 | class InternalItem(): |
653 | 52 | __slots__ = ["name", "path", "kind"] | 52 | __slots__ = ["name", "path", "kind", "file_id"] |
654 | 53 | def __init__(self, name, path, kind): | 53 | def __init__(self, name, path, kind, file_id): |
655 | 54 | self.name = name | 54 | self.name = name |
656 | 55 | self.path = path | 55 | self.path = path |
657 | 56 | self.kind = kind | 56 | self.kind = kind |
658 | 57 | self.file_id = file_id | ||
659 | 57 | 58 | ||
660 | 58 | revision = property(lambda self:None) | 59 | revision = property(lambda self:None) |
662 | 59 | file_id = property(lambda self:None) | 60 | |
663 | 61 | class UnversionedItem(InternalItem): | ||
664 | 62 | def __init__(self, name, path, kind): | ||
665 | 63 | InternalItem.__init__(self, name, path, kind, None) | ||
666 | 64 | |||
667 | 65 | class ModelItemData(): | ||
668 | 66 | __slots__ = ["id", "item", "change", "chekced", "children_ids", | ||
669 | 67 | "parent_id", "row"] | ||
670 | 68 | |||
671 | 69 | def __init__(self, item, change): | ||
672 | 70 | self.item = item | ||
673 | 71 | self.change = change | ||
674 | 72 | if change is not None and change.is_ignored() is None: | ||
675 | 73 | self.checked = QtCore.Qt.Checked | ||
676 | 74 | else: | ||
677 | 75 | self.checked = QtCore.Qt.Unchecked | ||
678 | 76 | |||
679 | 77 | self.children_ids = None | ||
680 | 78 | self.parent_id = None | ||
681 | 79 | self.id = None | ||
682 | 80 | self.row = None | ||
683 | 81 | |||
684 | 82 | |||
685 | 83 | class ChangeDesc(tuple): | ||
686 | 84 | """Helper class that "knows" about internals of iter_changes' changed entry | ||
687 | 85 | description tuple, and provides additional helper methods. | ||
688 | 86 | |||
689 | 87 | iter_changes return tuple with info about changed entry: | ||
690 | 88 | [0]: file_id -> ascii string | ||
691 | 89 | [1]: paths -> 2-tuple (old, new) fullpaths unicode/None | ||
692 | 90 | [2]: changed_content -> bool | ||
693 | 91 | [3]: versioned -> 2-tuple (bool, bool) | ||
694 | 92 | [4]: parent -> 2-tuple | ||
695 | 93 | [5]: name -> 2-tuple (old_name, new_name) utf-8?/None | ||
696 | 94 | [6]: kind -> 2-tuple (string/None, string/None) | ||
697 | 95 | [7]: executable -> 2-tuple (bool/None, bool/None) | ||
698 | 96 | |||
699 | 97 | --optional-- | ||
700 | 98 | [8]: is_ignored -> If the file is ignored, pattern which caused it to | ||
701 | 99 | be ignored, otherwise None. | ||
702 | 100 | |||
703 | 101 | NOTE: None value used for non-existing entry in corresponding | ||
704 | 102 | tree, e.g. for added/deleted/ignored/unversioned | ||
705 | 103 | """ | ||
706 | 104 | |||
707 | 105 | # XXX We should may be try get this into bzrlib. | ||
708 | 106 | # XXX We should use this in qdiff. | ||
709 | 107 | |||
710 | 108 | def fileid(desc): | ||
711 | 109 | return desc[0] | ||
712 | 110 | |||
713 | 111 | def path(desc): | ||
714 | 112 | """Return a suitable entry for a 'specific_files' param to bzr functions.""" | ||
715 | 113 | oldpath, newpath = desc[1] | ||
716 | 114 | return newpath or oldpath | ||
717 | 115 | |||
718 | 116 | def oldpath(desc): | ||
719 | 117 | """Return oldpath for renames.""" | ||
720 | 118 | return desc[1][0] | ||
721 | 119 | |||
722 | 120 | def kind(desc): | ||
723 | 121 | oldkind, newkind = desc[6] | ||
724 | 122 | return newkind or oldkind | ||
725 | 123 | |||
726 | 124 | def is_versioned(desc): | ||
727 | 125 | return desc[3] != (False, False) | ||
728 | 126 | |||
729 | 127 | def is_modified(desc): | ||
730 | 128 | return (desc[3] != (False, False) and desc[2]) | ||
731 | 129 | |||
732 | 130 | def is_renamed(desc): | ||
733 | 131 | return (desc[3] == (True, True) | ||
734 | 132 | and (desc[4][0], desc[5][0]) != (desc[4][1], desc[5][1])) | ||
735 | 133 | |||
736 | 134 | def is_tree_root(desc): | ||
737 | 135 | """Check if entry actually tree root.""" | ||
738 | 136 | if desc[3] != (False, False) and desc[4] == (None, None): | ||
739 | 137 | # TREE_ROOT has not parents (desc[4]). | ||
740 | 138 | # But because we could want to see unversioned files | ||
741 | 139 | # we need to check for versioned flag (desc[3]) | ||
742 | 140 | return True | ||
743 | 141 | return False | ||
744 | 142 | |||
745 | 143 | def is_missing(desc): | ||
746 | 144 | """Check if file was present in previous revision but now it's gone | ||
747 | 145 | (i.e. deleted manually, without invoking `bzr remove` command) | ||
748 | 146 | """ | ||
749 | 147 | return (desc[3] == (True, True) and desc[6][1] is None) | ||
750 | 148 | |||
751 | 149 | def is_misadded(desc): | ||
752 | 150 | """Check if file was added to the working tree but then gone | ||
753 | 151 | (i.e. deleted manually, without invoking `bzr remove` command) | ||
754 | 152 | """ | ||
755 | 153 | return (desc[3] == (False, True) and desc[6][1] is None) | ||
756 | 154 | |||
757 | 155 | def is_ignored(desc): | ||
758 | 156 | if len(desc) >= 8: | ||
759 | 157 | return desc[8] | ||
760 | 158 | else: | ||
761 | 159 | return None | ||
762 | 160 | |||
763 | 161 | def status(desc): | ||
764 | 162 | if len(desc) == 8: | ||
765 | 163 | (file_id, (path_in_source, path_in_target), | ||
766 | 164 | changed_content, versioned, parent, name, kind, | ||
767 | 165 | executable) = desc | ||
768 | 166 | is_ignored = None | ||
769 | 167 | elif len(desc) == 9: | ||
770 | 168 | (file_id, (path_in_source, path_in_target), | ||
771 | 169 | changed_content, versioned, parent, name, kind, | ||
772 | 170 | executable, is_ignored) = desc | ||
773 | 171 | else: | ||
774 | 172 | raise RuntimeError, "Unkown number of items to unpack." | ||
775 | 173 | |||
776 | 174 | if versioned == (False, False): | ||
777 | 175 | if is_ignored: | ||
778 | 176 | return gettext("ignored") | ||
779 | 177 | else: | ||
780 | 178 | return gettext("non-versioned") | ||
781 | 179 | elif versioned == (False, True): | ||
782 | 180 | return gettext("added") | ||
783 | 181 | elif versioned == (True, False): | ||
784 | 182 | return gettext("removed") | ||
785 | 183 | elif kind[0] is not None and kind[1] is None: | ||
786 | 184 | return gettext("missing") | ||
787 | 185 | else: | ||
788 | 186 | # versioned = True, True - so either renamed or modified | ||
789 | 187 | # or properties changed (x-bit). | ||
790 | 188 | renamed = (parent[0], name[0]) != (parent[1], name[1]) | ||
791 | 189 | if renamed: | ||
792 | 190 | if changed_content: | ||
793 | 191 | return gettext("renamed and modified") | ||
794 | 192 | else: | ||
795 | 193 | return gettext("renamed") | ||
796 | 194 | elif changed_content: | ||
797 | 195 | return gettext("modified") | ||
798 | 196 | elif executable[0] != executable[1]: | ||
799 | 197 | return gettext("modified (x-bit)") | ||
800 | 198 | else: | ||
801 | 199 | raise RuntimeError, "what status am I missing??" | ||
802 | 200 | |||
803 | 60 | 201 | ||
804 | 61 | class TreeModel(QtCore.QAbstractItemModel): | 202 | class TreeModel(QtCore.QAbstractItemModel): |
805 | 62 | 203 | ||
806 | @@ -68,10 +209,6 @@ | |||
807 | 68 | gettext("Status")] | 209 | gettext("Status")] |
808 | 69 | NAME, DATE, REVNO, MESSAGE, AUTHOR, STATUS = range(len(HEADER_LABELS)) | 210 | NAME, DATE, REVNO, MESSAGE, AUTHOR, STATUS = range(len(HEADER_LABELS)) |
809 | 70 | 211 | ||
810 | 71 | REVID = QtCore.Qt.UserRole + 1 | ||
811 | 72 | FILEID = QtCore.Qt.UserRole + 2 | ||
812 | 73 | PATH = QtCore.Qt.UserRole + 3 | ||
813 | 74 | |||
814 | 75 | def __init__(self, file_icon, dir_icon, symlink_icon, parent=None): | 212 | def __init__(self, file_icon, dir_icon, symlink_icon, parent=None): |
815 | 76 | QtCore.QAbstractTableModel.__init__(self, parent) | 213 | QtCore.QAbstractTableModel.__init__(self, parent) |
816 | 77 | 214 | ||
817 | @@ -79,39 +216,82 @@ | |||
818 | 79 | self.dir_icon = dir_icon | 216 | self.dir_icon = dir_icon |
819 | 80 | self.symlink_icon = symlink_icon | 217 | self.symlink_icon = symlink_icon |
820 | 81 | self.tree = None | 218 | self.tree = None |
822 | 82 | self.inventory_items = [] | 219 | self.inventory_data = [] |
823 | 83 | self.dir_children_ids = {} | 220 | self.dir_children_ids = {} |
824 | 84 | self.parent_ids = [] | 221 | self.parent_ids = [] |
825 | 222 | self.checkable = False | ||
826 | 85 | 223 | ||
828 | 86 | def set_tree(self, tree, branch): | 224 | def set_tree(self, tree, branch=None, |
829 | 225 | changes_mode=False, want_unversioned=True): | ||
830 | 87 | self.tree = tree | 226 | self.tree = tree |
831 | 88 | self.branch = branch | 227 | self.branch = branch |
832 | 89 | self.revno_map = None | 228 | self.revno_map = None |
833 | 229 | self.changes_mode = changes_mode | ||
834 | 90 | 230 | ||
835 | 91 | self.changes = {} | 231 | self.changes = {} |
836 | 92 | self.unver_by_parent = {} | 232 | self.unver_by_parent = {} |
838 | 93 | 233 | ||
839 | 94 | if isinstance(self.tree, WorkingTree): | 234 | if isinstance(self.tree, WorkingTree): |
840 | 95 | tree.lock_read() | 235 | tree.lock_read() |
841 | 96 | try: | 236 | try: |
842 | 237 | root_id = self.tree.get_root_id() | ||
843 | 238 | dir_fileid_by_path = {} | ||
844 | 97 | for change in self.tree.iter_changes(self.tree.basis_tree(), | 239 | for change in self.tree.iter_changes(self.tree.basis_tree(), |
846 | 98 | want_unversioned=True): | 240 | want_unversioned=want_unversioned): |
847 | 99 | change = ChangeDesc(change) | 241 | change = ChangeDesc(change) |
848 | 100 | path = change.path() | 242 | path = change.path() |
849 | 243 | fileid = change.fileid() | ||
850 | 101 | is_ignored = self.tree.is_ignored(path) | 244 | is_ignored = self.tree.is_ignored(path) |
851 | 102 | change = ChangeDesc(change+(is_ignored,)) | 245 | change = ChangeDesc(change+(is_ignored,)) |
852 | 103 | 246 | ||
854 | 104 | if change.fileid() is not None: | 247 | if fileid is not None and not changes_mode: |
855 | 105 | self.changes[change.fileid()] = change | 248 | self.changes[change.fileid()] = change |
856 | 106 | else: | 249 | else: |
859 | 107 | (dir_path, slash, name) = path.rpartition('/') | 250 | if changes_mode: |
860 | 108 | dir_fileid = self.tree.path2id(dir_path) | 251 | if (fileid is not None and |
861 | 252 | change.kind() == "directory"): | ||
862 | 253 | dir_fileid_by_path[path] = fileid | ||
863 | 254 | dir_path = path | ||
864 | 255 | dir_fileid = None | ||
865 | 256 | relpath = "" | ||
866 | 257 | while dir_path: | ||
867 | 258 | (dir_path, slash, name) = dir_path.rpartition('/') | ||
868 | 259 | relpath = slash + name + relpath | ||
869 | 260 | if dir_path in dir_fileid_by_path: | ||
870 | 261 | dir_fileid = dir_fileid_by_path[dir_path] | ||
871 | 262 | break | ||
872 | 263 | if dir_fileid is None: | ||
873 | 264 | dir_fileid = root_id | ||
874 | 265 | dir_path = "" | ||
875 | 266 | |||
876 | 267 | name = relpath.lstrip("/") | ||
877 | 268 | if change.is_renamed(): | ||
878 | 269 | oldpath = change.oldpath() | ||
879 | 270 | if oldpath.startswith(dir_path): | ||
880 | 271 | oldpath = oldpath[len(dir_path):] | ||
881 | 272 | else: | ||
882 | 273 | # The file was mv from a difirent path. | ||
883 | 274 | oldpath = '/' + oldpath | ||
884 | 275 | name = "%s => %s" % (oldpath, name) | ||
885 | 276 | else: | ||
886 | 277 | (dir_path, slash, name) = path.rpartition('/') | ||
887 | 278 | dir_fileid = self.tree.path2id(dir_path) | ||
888 | 279 | |||
889 | 109 | 280 | ||
890 | 110 | if dir_fileid not in self.unver_by_parent: | 281 | if dir_fileid not in self.unver_by_parent: |
891 | 111 | self.unver_by_parent[dir_fileid] = [] | 282 | self.unver_by_parent[dir_fileid] = [] |
895 | 112 | self.unver_by_parent[dir_fileid].append(( | 283 | |
896 | 113 | UnversionedItem(name, path, change.kind()), | 284 | if change.is_versioned(): |
897 | 114 | change)) | 285 | if changes_mode: |
898 | 286 | item = InternalItem(name, path, change.kind(), | ||
899 | 287 | change.fileid()) | ||
900 | 288 | else: | ||
901 | 289 | item = self.tree.inventory[change.fileid()] | ||
902 | 290 | else: | ||
903 | 291 | item = UnversionedItem(name, path, change.kind()) | ||
904 | 292 | |||
905 | 293 | self.unver_by_parent[dir_fileid].append( | ||
906 | 294 | ModelItemData(item, change)) | ||
907 | 115 | 295 | ||
908 | 116 | self.process_inventory(self.working_tree_get_children) | 296 | self.process_inventory(self.working_tree_get_children) |
909 | 117 | finally: | 297 | finally: |
910 | @@ -119,11 +299,12 @@ | |||
911 | 119 | else: | 299 | else: |
912 | 120 | self.process_inventory(self.revision_tree_get_children) | 300 | self.process_inventory(self.revision_tree_get_children) |
913 | 121 | 301 | ||
917 | 122 | def revision_tree_get_children(self, item): | 302 | def revision_tree_get_children(self, item_data): |
918 | 123 | for child in item.children.itervalues(): | 303 | for child in item_data.item.children.itervalues(): |
919 | 124 | yield (child, None) | 304 | yield ModelItemData(child, None) |
920 | 125 | 305 | ||
922 | 126 | def working_tree_get_children(self, item): | 306 | def working_tree_get_children(self, item_data): |
923 | 307 | item = item_data.item | ||
924 | 127 | if isinstance(item, UnversionedItem): | 308 | if isinstance(item, UnversionedItem): |
925 | 128 | abspath = self.tree.abspath(item.path) | 309 | abspath = self.tree.abspath(item.path) |
926 | 129 | 310 | ||
927 | @@ -143,9 +324,10 @@ | |||
928 | 143 | (None, kind), | 324 | (None, kind), |
929 | 144 | (None, executable), | 325 | (None, executable), |
930 | 145 | is_ignored)) | 326 | is_ignored)) |
932 | 146 | yield (child, change) | 327 | yield ModelItemData(child, change) |
933 | 147 | 328 | ||
935 | 148 | elif item.children is not None: | 329 | if (not isinstance(item, InternalItem) and |
936 | 330 | item.children is not None and not self.changes_mode): | ||
937 | 149 | #Because we create copies, we have to get the real item. | 331 | #Because we create copies, we have to get the real item. |
938 | 150 | item = self.tree.inventory[item.file_id] | 332 | item = self.tree.inventory[item.file_id] |
939 | 151 | for child in item.children.itervalues(): | 333 | for child in item.children.itervalues(): |
940 | @@ -155,35 +337,32 @@ | |||
941 | 155 | change = self.changes[child.file_id] | 337 | change = self.changes[child.file_id] |
942 | 156 | else: | 338 | else: |
943 | 157 | change = None | 339 | change = None |
945 | 158 | yield (child, change) | 340 | yield ModelItemData(child, change) |
946 | 159 | if item.file_id in self.unver_by_parent: | 341 | if item.file_id in self.unver_by_parent: |
949 | 160 | for (child, change) in self.unver_by_parent[item.file_id]: | 342 | for item_data in self.unver_by_parent[item.file_id]: |
950 | 161 | yield (child, change) | 343 | yield item_data |
951 | 162 | 344 | ||
952 | 163 | def load_dir(self, dir_id): | 345 | def load_dir(self, dir_id): |
953 | 164 | if isinstance(self.tree, WorkingTree): | 346 | if isinstance(self.tree, WorkingTree): |
954 | 165 | self.tree.lock_read() | 347 | self.tree.lock_read() |
955 | 166 | try: | 348 | try: |
957 | 167 | if dir_id>=len(self.inventory_items): | 349 | if dir_id>=len(self.inventory_data): |
958 | 168 | return | 350 | return |
961 | 169 | dir_item, dir_change = self.inventory_items[dir_id] | 351 | dir_item = self.inventory_data[dir_id] |
962 | 170 | dir_children_ids = [] | 352 | dir_item.children_ids = [] |
963 | 171 | children = sorted(self.get_children(dir_item), | 353 | children = sorted(self.get_children(dir_item), |
964 | 172 | self.inventory_dirs_first_cmp, | 354 | self.inventory_dirs_first_cmp, |
966 | 173 | lambda x: x[0]) | 355 | lambda x: (x.item.name, x.item.kind)) |
967 | 174 | 356 | ||
968 | 175 | parent_model_index = self._index_from_id(dir_id, 0) | 357 | parent_model_index = self._index_from_id(dir_id, 0) |
969 | 176 | self.beginInsertRows(parent_model_index, 0, len(children)-1) | 358 | self.beginInsertRows(parent_model_index, 0, len(children)-1) |
970 | 177 | try: | 359 | try: |
976 | 178 | for (child, change) in children: | 360 | for child in children: |
977 | 179 | child_id = self.append_item(child, change, dir_id) | 361 | child_id = self.append_item(child, dir_id) |
978 | 180 | dir_children_ids.append(child_id) | 362 | dir_item.children_ids.append(child_id) |
974 | 181 | if child.kind == "directory": | ||
975 | 182 | self.dir_children_ids[child_id] = None | ||
979 | 183 | 363 | ||
981 | 184 | if len(dir_children_ids) % 100 == 0: | 364 | if len(dir_item.children_ids) % 100 == 0: |
982 | 185 | QtCore.QCoreApplication.processEvents() | 365 | QtCore.QCoreApplication.processEvents() |
983 | 186 | self.dir_children_ids[dir_id] = dir_children_ids | ||
984 | 187 | finally: | 366 | finally: |
985 | 188 | self.endInsertRows(); | 367 | self.endInsertRows(); |
986 | 189 | finally: | 368 | finally: |
987 | @@ -194,37 +373,64 @@ | |||
988 | 194 | self.get_children = get_children | 373 | self.get_children = get_children |
989 | 195 | 374 | ||
990 | 196 | self.emit(QtCore.SIGNAL("layoutAboutToBeChanged()")) | 375 | self.emit(QtCore.SIGNAL("layoutAboutToBeChanged()")) |
992 | 197 | self.inventory_items = [] | 376 | self.inventory_data = [] |
993 | 198 | self.dir_children_ids = {} | 377 | self.dir_children_ids = {} |
994 | 199 | self.parent_ids = [] | 378 | self.parent_ids = [] |
995 | 379 | |||
996 | 380 | root_item = ModelItemData( | ||
997 | 381 | self.tree.inventory[self.tree.get_root_id()], | ||
998 | 382 | None) | ||
999 | 383 | root_id = self.append_item(root_item, None) | ||
1000 | 384 | self.load_dir(root_id) | ||
1001 | 200 | self.emit(QtCore.SIGNAL("layoutChanged()")) | 385 | self.emit(QtCore.SIGNAL("layoutChanged()")) |
1002 | 201 | |||
1003 | 202 | root_item = self.tree.inventory[self.tree.get_root_id()] | ||
1004 | 203 | root_id = self.append_item(root_item, None, None) | ||
1005 | 204 | self.dir_children_ids[root_id] = None | ||
1006 | 205 | self.load_dir(root_id) | ||
1007 | 206 | 386 | ||
1013 | 207 | def append_item(self, item, change, parent_id): | 387 | def append_item(self, item_data, parent_id): |
1014 | 208 | id = len(self.inventory_items) | 388 | item_data.id = len(self.inventory_data) |
1015 | 209 | self.inventory_items.append((item, change)) | 389 | if parent_id is not None: |
1016 | 210 | self.parent_ids.append(parent_id) | 390 | parent_data = self.inventory_data[parent_id] |
1017 | 211 | return id | 391 | if self.is_item_in_select_all(item_data): |
1018 | 392 | item_data.checked = parent_data.checked | ||
1019 | 393 | else: | ||
1020 | 394 | item_data.checked = False | ||
1021 | 395 | item_data.row = len(parent_data.children_ids) | ||
1022 | 396 | else: | ||
1023 | 397 | item_data.checked = QtCore.Qt.Checked | ||
1024 | 398 | item_data.row = 0 | ||
1025 | 399 | item_data.parent_id = parent_id | ||
1026 | 400 | self.inventory_data.append(item_data) | ||
1027 | 401 | return item_data.id | ||
1028 | 212 | 402 | ||
1029 | 213 | def inventory_dirs_first_cmp(self, x, y): | 403 | def inventory_dirs_first_cmp(self, x, y): |
1032 | 214 | x_is_dir = x.kind =="directory" | 404 | (x_name, x_kind) = x |
1033 | 215 | y_is_dir = y.kind =="directory" | 405 | (y_name, y_kind) = y |
1034 | 406 | x_a = x_name | ||
1035 | 407 | y_a = y_name | ||
1036 | 408 | x_is_dir = x_kind =="directory" | ||
1037 | 409 | y_is_dir = y_kind =="directory" | ||
1038 | 410 | while True: | ||
1039 | 411 | x_b, sep, x_a_t = x_a.partition("/") | ||
1040 | 412 | y_b, sep, y_a_t = y_a.partition("/") | ||
1041 | 413 | if x_a_t == "" and y_a_t == "": | ||
1042 | 414 | break | ||
1043 | 415 | if (x_is_dir or not x_a_t == "") and not (y_is_dir or not y_a_t == ""): | ||
1044 | 416 | return -1 | ||
1045 | 417 | if (y_is_dir or not y_a_t == "") and not (x_is_dir or not x_a_t == ""): | ||
1046 | 418 | return 1 | ||
1047 | 419 | cmp_r = cmp(x_b, y_b) | ||
1048 | 420 | if not cmp_r == 0: | ||
1049 | 421 | return cmp_r | ||
1050 | 422 | x_a = x_a_t | ||
1051 | 423 | y_a = y_a_t | ||
1052 | 216 | if x_is_dir and not y_is_dir: | 424 | if x_is_dir and not y_is_dir: |
1053 | 217 | return -1 | 425 | return -1 |
1054 | 218 | if y_is_dir and not x_is_dir: | 426 | if y_is_dir and not x_is_dir: |
1055 | 219 | return 1 | 427 | return 1 |
1057 | 220 | return cmp(x.name, y.name) | 428 | return cmp(x_name, y_name) |
1058 | 221 | 429 | ||
1059 | 222 | def set_revno_map(self, revno_map): | 430 | def set_revno_map(self, revno_map): |
1060 | 223 | self.revno_map = revno_map | 431 | self.revno_map = revno_map |
1065 | 224 | for id in xrange(1, len(self.inventory_items)): | 432 | for item_data in self.inventory_data[1:0]: |
1066 | 225 | parent_id = self.parent_ids[id] | 433 | index = self.createIndex (item_data.row, self.REVNO, item_data.id) |
1063 | 226 | row = self.dir_children_ids[parent_id].index(id) | ||
1064 | 227 | index = self.createIndex (row, self.REVNO, id) | ||
1067 | 228 | self.emit(QtCore.SIGNAL("dataChanged(QModelIndex, QModelIndex)"), | 434 | self.emit(QtCore.SIGNAL("dataChanged(QModelIndex, QModelIndex)"), |
1068 | 229 | index,index) | 435 | index,index) |
1069 | 230 | 436 | ||
1070 | @@ -232,43 +438,48 @@ | |||
1071 | 232 | return len(self.HEADER_LABELS) | 438 | return len(self.HEADER_LABELS) |
1072 | 233 | 439 | ||
1073 | 234 | def rowCount(self, parent): | 440 | def rowCount(self, parent): |
1081 | 235 | parent_id = parent.internalId() | 441 | if parent.internalId()>=len(self.inventory_data): |
1082 | 236 | if parent_id not in self.dir_children_ids: | 442 | return 0 |
1083 | 237 | return 0 | 443 | parent_data = self.inventory_data[parent.internalId()] |
1084 | 238 | dir_children_ids = self.dir_children_ids[parent_id] | 444 | if parent_data.children_ids is None: |
1085 | 239 | if dir_children_ids is None: | 445 | return 0 |
1086 | 240 | return 0 | 446 | return len(parent_data.children_ids) |
1080 | 241 | return len(dir_children_ids) | ||
1087 | 242 | 447 | ||
1088 | 243 | def canFetchMore(self, parent): | 448 | def canFetchMore(self, parent): |
1092 | 244 | parent_id = parent.internalId() | 449 | if parent.internalId()>=len(self.inventory_data): |
1093 | 245 | return parent_id in self.dir_children_ids \ | 450 | return False |
1094 | 246 | and self.dir_children_ids[parent_id] is None | 451 | parent_data = self.inventory_data[parent.internalId()] |
1095 | 452 | return (parent_data.children_ids is None and | ||
1096 | 453 | parent_data.item.kind == "directory") | ||
1097 | 247 | 454 | ||
1098 | 248 | def fetchMore(self, parent): | 455 | def fetchMore(self, parent): |
1099 | 249 | self.load_dir(parent.internalId()) | 456 | self.load_dir(parent.internalId()) |
1100 | 250 | 457 | ||
1101 | 251 | def _index(self, row, column, parent_id): | 458 | def _index(self, row, column, parent_id): |
1110 | 252 | if parent_id not in self.dir_children_ids: | 459 | if parent_id>=len(self.inventory_data): |
1111 | 253 | return QtCore.QModelIndex() | 460 | return QtCore.QModelIndex() |
1112 | 254 | dir_children_ids = self.dir_children_ids[parent_id] | 461 | |
1113 | 255 | if dir_children_ids is None: | 462 | parent_data = self.inventory_data[parent_id] |
1114 | 256 | return QtCore.QModelIndex() | 463 | if parent_data.children_ids is None: |
1115 | 257 | if row >= len(dir_children_ids): | 464 | return QtCore.QModelIndex() |
1116 | 258 | return QtCore.QModelIndex() | 465 | |
1117 | 259 | item_id = dir_children_ids[row] | 466 | if (row < 0 or |
1118 | 467 | row >= len(parent_data.children_ids) or | ||
1119 | 468 | column < 0 or | ||
1120 | 469 | column >= len(self.HEADER_LABELS)): | ||
1121 | 470 | return QtCore.QModelIndex() | ||
1122 | 471 | item_id = parent_data.children_ids[row] | ||
1123 | 260 | return self.createIndex(row, column, item_id) | 472 | return self.createIndex(row, column, item_id) |
1124 | 261 | 473 | ||
1125 | 262 | def _index_from_id(self, item_id, column): | 474 | def _index_from_id(self, item_id, column): |
1128 | 263 | parent_id = self.parent_ids[item_id] | 475 | if item_id >= len(self.inventory_data): |
1127 | 264 | if parent_id is None: | ||
1129 | 265 | return QtCore.QModelIndex() | 476 | return QtCore.QModelIndex() |
1132 | 266 | row = self.dir_children_ids[parent_id].index(item_id) | 477 | |
1133 | 267 | return self.createIndex(row, column, item_id) | 478 | item_data = self.inventory_data[item_id] |
1134 | 479 | return self.createIndex(item_data.row, column, item_id) | ||
1135 | 268 | 480 | ||
1136 | 269 | def index(self, row, column, parent = QtCore.QModelIndex()): | 481 | def index(self, row, column, parent = QtCore.QModelIndex()): |
1139 | 270 | parent_id = parent.internalId() | 482 | return self._index(row, column, parent.internalId()) |
1138 | 271 | return self._index(row, column, parent_id) | ||
1140 | 272 | 483 | ||
1141 | 273 | def sibling(self, row, column, index): | 484 | def sibling(self, row, column, index): |
1142 | 274 | sibling_id = index.internalId() | 485 | sibling_id = index.internalId() |
1143 | @@ -281,28 +492,109 @@ | |||
1144 | 281 | child_id = child.internalId() | 492 | child_id = child.internalId() |
1145 | 282 | if child_id == 0: | 493 | if child_id == 0: |
1146 | 283 | return QtCore.QModelIndex() | 494 | return QtCore.QModelIndex() |
1151 | 284 | if child_id not in self.parent_ids: | 495 | item_data = self.inventory_data[child_id] |
1152 | 285 | return QtCore.QModelIndex() | 496 | if item_data.parent_id == 0 : |
1149 | 286 | item_id = self.parent_ids[child_id] | ||
1150 | 287 | if item_id == 0 : | ||
1153 | 288 | return QtCore.QModelIndex() | 497 | return QtCore.QModelIndex() |
1154 | 289 | 498 | ||
1156 | 290 | return self._index_from_id(item_id, 0) | 499 | return self._index_from_id(item_data.parent_id, 0) |
1157 | 291 | 500 | ||
1158 | 292 | def hasChildren(self, parent): | 501 | def hasChildren(self, parent): |
1161 | 293 | parent_id = parent.internalId() | 502 | if self.tree is None: |
1162 | 294 | return parent_id in self.dir_children_ids | 503 | return False |
1163 | 504 | if parent.internalId() == 0: | ||
1164 | 505 | return True | ||
1165 | 506 | item_data = self.inventory_data[parent.internalId()] | ||
1166 | 507 | return item_data.item.kind == "directory" | ||
1167 | 508 | |||
1168 | 509 | is_item_in_select_all = lambda self, item: True | ||
1169 | 510 | """Returns wether an item is changed when select all is clicked.""" | ||
1170 | 511 | |||
1171 | 512 | def setData(self, index, value, role): | ||
1172 | 513 | |||
1173 | 514 | |||
1174 | 515 | def set_checked(item_data, checked): | ||
1175 | 516 | old_checked = item_data.checked | ||
1176 | 517 | item_data.checked = checked | ||
1177 | 518 | if not old_checked == checked: | ||
1178 | 519 | index = self.createIndex (item_data.row, self.NAME, item_data.id) | ||
1179 | 520 | self.emit(QtCore.SIGNAL("dataChanged(QModelIndex, QModelIndex)"), | ||
1180 | 521 | index,index) | ||
1181 | 522 | |||
1182 | 523 | if index.column() == self.NAME and role == QtCore.Qt.CheckStateRole: | ||
1183 | 524 | value = value.toInt()[0] | ||
1184 | 525 | if index.internalId() >= len(self.inventory_data): | ||
1185 | 526 | return False | ||
1186 | 527 | |||
1187 | 528 | item_data = self.inventory_data[index.internalId()] | ||
1188 | 529 | set_checked(item_data, value) | ||
1189 | 530 | |||
1190 | 531 | # Recursively set all children to checked. | ||
1191 | 532 | if item_data.children_ids is not None: | ||
1192 | 533 | children_ids = list(item_data.children_ids) | ||
1193 | 534 | while children_ids: | ||
1194 | 535 | child = self.inventory_data[children_ids.pop(0)] | ||
1195 | 536 | |||
1196 | 537 | # If unchecking, uncheck everything, but if checking, | ||
1197 | 538 | # only check those in "select_all" get checked. | ||
1198 | 539 | if (self.is_item_in_select_all(child) or | ||
1199 | 540 | value == QtCore.Qt.Unchecked): | ||
1200 | 541 | |||
1201 | 542 | set_checked(child, value) | ||
1202 | 543 | if child.children_ids is not None: | ||
1203 | 544 | children_ids.extend(child.children_ids) | ||
1204 | 545 | |||
1205 | 546 | # Walk up the tree, and update every dir | ||
1206 | 547 | parent_data = item_data | ||
1207 | 548 | while parent_data.parent_id is not None: | ||
1208 | 549 | parent_data = self.inventory_data[parent_data.parent_id] | ||
1209 | 550 | has_checked = False | ||
1210 | 551 | has_unchecked = False | ||
1211 | 552 | for child_id in parent_data.children_ids: | ||
1212 | 553 | child = self.inventory_data[child_id] | ||
1213 | 554 | |||
1214 | 555 | if child.checked == QtCore.Qt.Checked: | ||
1215 | 556 | has_checked = True | ||
1216 | 557 | elif (child.checked == QtCore.Qt.Unchecked and | ||
1217 | 558 | self.is_item_in_select_all(child)): | ||
1218 | 559 | has_unchecked = True | ||
1219 | 560 | elif child.checked == QtCore.Qt.PartiallyChecked: | ||
1220 | 561 | has_checked = True | ||
1221 | 562 | if self.is_item_in_select_all(child): | ||
1222 | 563 | has_unchecked = True | ||
1223 | 564 | |||
1224 | 565 | if has_checked and has_unchecked: | ||
1225 | 566 | break | ||
1226 | 567 | |||
1227 | 568 | if has_checked and has_unchecked: | ||
1228 | 569 | set_checked(parent_data, QtCore.Qt.PartiallyChecked) | ||
1229 | 570 | elif has_checked: | ||
1230 | 571 | set_checked(parent_data, QtCore.Qt.Checked) | ||
1231 | 572 | else: | ||
1232 | 573 | set_checked(parent_data, QtCore.Qt.Unchecked) | ||
1233 | 574 | |||
1234 | 575 | return True | ||
1235 | 576 | |||
1236 | 577 | return False | ||
1237 | 578 | |||
1238 | 579 | REVID = QtCore.Qt.UserRole + 1 | ||
1239 | 580 | FILEID = QtCore.Qt.UserRole + 2 | ||
1240 | 581 | PATH = QtCore.Qt.UserRole + 3 | ||
1241 | 582 | INTERNALID = QtCore.Qt.UserRole + 4 | ||
1242 | 295 | 583 | ||
1243 | 296 | def data(self, index, role): | 584 | def data(self, index, role): |
1244 | 297 | if not index.isValid(): | 585 | if not index.isValid(): |
1245 | 298 | return QtCore.QVariant() | 586 | return QtCore.QVariant() |
1246 | 299 | 587 | ||
1248 | 300 | (item, change) = self.inventory_items[index.internalId()] | 588 | if role == self.INTERNALID: |
1249 | 589 | return QtCore.QVariant(index.internalId()) | ||
1250 | 590 | |||
1251 | 591 | item_data = self.inventory_data[index.internalId()] | ||
1252 | 592 | item = item_data.item | ||
1253 | 301 | 593 | ||
1254 | 302 | if role == self.FILEID: | 594 | if role == self.FILEID: |
1255 | 303 | return QtCore.QVariant(item.file_id) | 595 | return QtCore.QVariant(item.file_id) |
1256 | 304 | 596 | ||
1258 | 305 | revid = item.revision | 597 | revid = item_data.item.revision |
1259 | 306 | if role == self.REVID: | 598 | if role == self.REVID: |
1260 | 307 | if revid is None: | 599 | if revid is None: |
1261 | 308 | return QtCore.QVariant() | 600 | return QtCore.QVariant() |
1262 | @@ -321,13 +613,18 @@ | |||
1263 | 321 | if item.kind == "symlink": | 613 | if item.kind == "symlink": |
1264 | 322 | return QtCore.QVariant(self.symlink_icon) | 614 | return QtCore.QVariant(self.symlink_icon) |
1265 | 323 | return QtCore.QVariant() | 615 | return QtCore.QVariant() |
1266 | 616 | if role == QtCore.Qt.CheckStateRole: | ||
1267 | 617 | if not self.checkable: | ||
1268 | 618 | return QtCore.QVariant() | ||
1269 | 619 | else: | ||
1270 | 620 | return QtCore.QVariant(item_data.checked) | ||
1271 | 324 | 621 | ||
1272 | 325 | if column == self.STATUS: | 622 | if column == self.STATUS: |
1273 | 326 | if role == QtCore.Qt.DisplayRole: | 623 | if role == QtCore.Qt.DisplayRole: |
1276 | 327 | if change is not None: | 624 | if item_data.change is not None: |
1277 | 328 | return QtCore.QVariant(change.status()) | 625 | return QtCore.QVariant(item_data.change.status()) |
1278 | 329 | else: | 626 | else: |
1280 | 330 | return QtCore.QVariant() | 627 | return QtCore.QVariant("") |
1281 | 331 | 628 | ||
1282 | 332 | if column == self.REVNO: | 629 | if column == self.REVNO: |
1283 | 333 | if role == QtCore.Qt.DisplayRole: | 630 | if role == QtCore.Qt.DisplayRole: |
1284 | @@ -336,7 +633,7 @@ | |||
1285 | 336 | return QtCore.QVariant( | 633 | return QtCore.QVariant( |
1286 | 337 | ".".join(["%d" % (revno) for revno in revno_sequence])) | 634 | ".".join(["%d" % (revno) for revno in revno_sequence])) |
1287 | 338 | else: | 635 | else: |
1289 | 339 | return QtCore.QVariant() | 636 | return QtCore.QVariant("") |
1290 | 340 | 637 | ||
1291 | 341 | if role == QtCore.Qt.DisplayRole: | 638 | if role == QtCore.Qt.DisplayRole: |
1292 | 342 | if revid in cached_revisions: | 639 | if revid in cached_revisions: |
1293 | @@ -365,13 +662,21 @@ | |||
1294 | 365 | self.tree.unlock() | 662 | self.tree.unlock() |
1295 | 366 | return QtCore.QVariant(path) | 663 | return QtCore.QVariant(path) |
1296 | 367 | 664 | ||
1297 | 665 | if role == QtCore.Qt.DisplayRole: | ||
1298 | 666 | return QtCore.QVariant("") | ||
1299 | 368 | return QtCore.QVariant() | 667 | return QtCore.QVariant() |
1300 | 369 | 668 | ||
1301 | 370 | def flags(self, index): | 669 | def flags(self, index): |
1304 | 371 | if not index.isValid(): | 670 | #if not index.isValid(): |
1305 | 372 | return QtCore.Qt.ItemIsEnabled | 671 | # return QtCore.Qt.ItemIsEnabled |
1306 | 373 | 672 | ||
1308 | 374 | return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | 673 | if self.checkable and index.column() == self.NAME: |
1309 | 674 | return (QtCore.Qt.ItemIsEnabled | | ||
1310 | 675 | QtCore.Qt.ItemIsSelectable | | ||
1311 | 676 | QtCore.Qt.ItemIsUserCheckable) | ||
1312 | 677 | else: | ||
1313 | 678 | return (QtCore.Qt.ItemIsEnabled | | ||
1314 | 679 | QtCore.Qt.ItemIsSelectable) | ||
1315 | 375 | 680 | ||
1316 | 376 | def headerData(self, section, orientation, role): | 681 | def headerData(self, section, orientation, role): |
1317 | 377 | if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole: | 682 | if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole: |
1318 | @@ -380,19 +685,23 @@ | |||
1319 | 380 | 685 | ||
1320 | 381 | def on_revisions_loaded(self, revisions, last_call): | 686 | def on_revisions_loaded(self, revisions, last_call): |
1321 | 382 | inventory = self.tree.inventory | 687 | inventory = self.tree.inventory |
1324 | 383 | for id, (item, change) in enumerate(self.inventory_items): | 688 | for item_data in self.inventory_data: |
1325 | 384 | if id == 0: | 689 | if item_data.id == 0: |
1326 | 385 | continue | 690 | continue |
1327 | 386 | 691 | ||
1331 | 387 | if item.revision in revisions: | 692 | if item_data.item.revision in revisions: |
1329 | 388 | parent_id = self.parent_ids[id] | ||
1330 | 389 | row = self.dir_children_ids[parent_id].index(id) | ||
1332 | 390 | self.emit(QtCore.SIGNAL("dataChanged(QModelIndex, QModelIndex)"), | 693 | self.emit(QtCore.SIGNAL("dataChanged(QModelIndex, QModelIndex)"), |
1335 | 391 | self.createIndex (row, self.DATE, id), | 694 | self.createIndex (item_data.row, self.DATE, item_data.id), |
1336 | 392 | self.createIndex (row, self.AUTHOR,id)) | 695 | self.createIndex (item_data.row, self.AUTHOR, item_data.id)) |
1337 | 393 | 696 | ||
1338 | 394 | def get_repo(self): | 697 | def get_repo(self): |
1339 | 395 | return self.branch.repository | 698 | return self.branch.repository |
1340 | 699 | |||
1341 | 700 | def iter_checked(self): | ||
1342 | 701 | return sorted([item_data for item_data in self.inventory_data[1:] | ||
1343 | 702 | if item_data.checked == QtCore.Qt.Checked], | ||
1344 | 703 | self.inventory_dirs_first_cmp, | ||
1345 | 704 | lambda x: (x.change.path(), x.item.kind)) | ||
1346 | 396 | 705 | ||
1347 | 397 | class TreeFilterProxyModel(QtGui.QSortFilterProxyModel): | 706 | class TreeFilterProxyModel(QtGui.QSortFilterProxyModel): |
1348 | 398 | source_model = None | 707 | source_model = None |
1349 | @@ -430,7 +739,10 @@ | |||
1350 | 430 | 739 | ||
1351 | 431 | model = self.source_model | 740 | model = self.source_model |
1352 | 432 | parent_id = source_parent.internalId() | 741 | parent_id = source_parent.internalId() |
1354 | 433 | id = model.dir_children_ids[parent_id][source_row] | 742 | children_ids = model.inventory_data[parent_id].children_ids |
1355 | 743 | if len(children_ids)<=source_row: | ||
1356 | 744 | return False | ||
1357 | 745 | id = children_ids[source_row] | ||
1358 | 434 | return self.filter_id_cached(id) | 746 | return self.filter_id_cached(id) |
1359 | 435 | 747 | ||
1360 | 436 | def filter_id_cached(self, id): | 748 | def filter_id_cached(self, id): |
1361 | @@ -445,27 +757,26 @@ | |||
1362 | 445 | (unchanged, changed, unversioned, ignored) = self.filters | 757 | (unchanged, changed, unversioned, ignored) = self.filters |
1363 | 446 | 758 | ||
1364 | 447 | model = self.source_model | 759 | model = self.source_model |
1372 | 448 | item, change = model.inventory_items[id] | 760 | item_data = model.inventory_data[id] |
1373 | 449 | 761 | ||
1374 | 450 | if change is None and unchanged: return True | 762 | if item_data.change is None and unchanged: return True |
1375 | 451 | 763 | ||
1376 | 452 | is_versioned = not isinstance(item, UnversionedItem) | 764 | is_versioned = not isinstance(item_data.item, UnversionedItem) |
1377 | 453 | 765 | ||
1378 | 454 | if is_versioned and change is not None and changed: return True | 766 | if is_versioned and item_data.change is not None and changed: |
1379 | 767 | return True | ||
1380 | 455 | 768 | ||
1381 | 456 | if not is_versioned and unversioned: | 769 | if not is_versioned and unversioned: |
1383 | 457 | is_ignored = change.is_ignored() | 770 | is_ignored = item_data.change.is_ignored() |
1384 | 458 | if not is_ignored: return True | 771 | if not is_ignored: return True |
1385 | 459 | if is_ignored and ignored: return True | 772 | if is_ignored and ignored: return True |
1386 | 460 | if is_ignored and not ignored: return False | 773 | if is_ignored and not ignored: return False |
1387 | 461 | 774 | ||
1391 | 462 | if id in model.dir_children_ids: | 775 | if item_data.item.kind == "directory": |
1392 | 463 | dir_children_ids = model.dir_children_ids[id] | 776 | if item_data.children_ids is None: |
1390 | 464 | if dir_children_ids is None: | ||
1393 | 465 | model.load_dir(id) | 777 | model.load_dir(id) |
1394 | 466 | dir_children_ids = model.dir_children_ids[id] | ||
1395 | 467 | 778 | ||
1397 | 468 | for child_id in dir_children_ids: | 779 | for child_id in item_data.children_ids: |
1398 | 469 | if self.filter_id_cached(child_id): | 780 | if self.filter_id_cached(child_id): |
1399 | 470 | return True | 781 | return True |
1400 | 471 | 782 | ||
1401 | @@ -540,7 +851,6 @@ | |||
1402 | 540 | self.connect(self, | 851 | self.connect(self, |
1403 | 541 | QtCore.SIGNAL("doubleClicked(QModelIndex)"), | 852 | QtCore.SIGNAL("doubleClicked(QModelIndex)"), |
1404 | 542 | self.do_default_action) | 853 | self.do_default_action) |
1405 | 543 | self.default_action = self.show_file_content | ||
1406 | 544 | self.tree = None | 854 | self.tree = None |
1407 | 545 | self.branch = None | 855 | self.branch = None |
1408 | 546 | 856 | ||
1409 | @@ -552,7 +862,7 @@ | |||
1410 | 552 | header.setResizeMode(self.tree_model.REVNO, QtGui.QHeaderView.Interactive) | 862 | header.setResizeMode(self.tree_model.REVNO, QtGui.QHeaderView.Interactive) |
1411 | 553 | header.setResizeMode(self.tree_model.MESSAGE, QtGui.QHeaderView.Stretch) | 863 | header.setResizeMode(self.tree_model.MESSAGE, QtGui.QHeaderView.Stretch) |
1412 | 554 | header.setResizeMode(self.tree_model.AUTHOR, QtGui.QHeaderView.Interactive) | 864 | header.setResizeMode(self.tree_model.AUTHOR, QtGui.QHeaderView.Interactive) |
1414 | 555 | header.setResizeMode(self.tree_model.STATUS, QtGui.QHeaderView.ResizeToContents) | 865 | header.setResizeMode(self.tree_model.STATUS, QtGui.QHeaderView.Stretch) |
1415 | 556 | fm = self.fontMetrics() | 866 | fm = self.fontMetrics() |
1416 | 557 | # XXX Make this dynamic. | 867 | # XXX Make this dynamic. |
1417 | 558 | col_margin = 6 | 868 | col_margin = 6 |
1418 | @@ -596,10 +906,34 @@ | |||
1419 | 596 | gettext("&Revert"), | 906 | gettext("&Revert"), |
1420 | 597 | self.revert) | 907 | self.revert) |
1421 | 598 | 908 | ||
1423 | 599 | def set_tree(self, tree, branch): | 909 | def set_tree(self, tree, branch=None, |
1424 | 910 | changes_mode=False, want_unversioned=True): | ||
1425 | 911 | """Causes a tree to be loaded, and displayed in the widget. | ||
1426 | 912 | |||
1427 | 913 | @param changes_mode: If in changes mode, a list of changes, and | ||
1428 | 914 | unversioned items, rather than a tree, is diplayed. | ||
1429 | 915 | e.g., when not in changes mode, one will get: | ||
1430 | 916 | |||
1431 | 917 | dir1 | ||
1432 | 918 | file1 changed | ||
1433 | 919 | file2 changed | ||
1434 | 920 | |||
1435 | 921 | but when in changes mode, one will get: | ||
1436 | 922 | |||
1437 | 923 | dir1/file1 changed | ||
1438 | 924 | file2 changed | ||
1439 | 925 | |||
1440 | 926 | When in changes mode, no unchanged items are shown. | ||
1441 | 927 | """ | ||
1442 | 600 | self.tree = tree | 928 | self.tree = tree |
1443 | 929 | if isinstance(tree, RevisionTree) and branch is None: | ||
1444 | 930 | raise AttributeError("A branch must be provided if the tree is a \ | ||
1445 | 931 | RevisionTree") | ||
1446 | 601 | self.branch = branch | 932 | self.branch = branch |
1448 | 602 | self.tree_model.set_tree(self.tree, self.branch) | 933 | self.changes_mode = changes_mode |
1449 | 934 | self.want_unversioned = want_unversioned | ||
1450 | 935 | self.tree_model.set_tree(self.tree, self.branch, | ||
1451 | 936 | changes_mode, want_unversioned) | ||
1452 | 603 | self.tree_filter_model.invalidateFilter() | 937 | self.tree_filter_model.invalidateFilter() |
1453 | 604 | 938 | ||
1454 | 605 | if str(QtCore.QT_VERSION_STR).startswith("4.4"): | 939 | if str(QtCore.QT_VERSION_STR).startswith("4.4"): |
1455 | @@ -623,7 +957,6 @@ | |||
1456 | 623 | header.showSection(self.tree_model.STATUS) | 957 | header.showSection(self.tree_model.STATUS) |
1457 | 624 | 958 | ||
1458 | 625 | self.context_menu.setDefaultAction(self.action_open_file) | 959 | self.context_menu.setDefaultAction(self.action_open_file) |
1459 | 626 | self.default_action = self.open_file | ||
1460 | 627 | else: | 960 | else: |
1461 | 628 | header.showSection(self.tree_model.DATE) | 961 | header.showSection(self.tree_model.DATE) |
1462 | 629 | header.showSection(self.tree_model.REVNO) | 962 | header.showSection(self.tree_model.REVNO) |
1463 | @@ -632,10 +965,10 @@ | |||
1464 | 632 | header.hideSection(self.tree_model.STATUS) | 965 | header.hideSection(self.tree_model.STATUS) |
1465 | 633 | 966 | ||
1466 | 634 | self.context_menu.setDefaultAction(self.action_show_file) | 967 | self.context_menu.setDefaultAction(self.action_show_file) |
1467 | 635 | self.default_action = self.show_file_content | ||
1468 | 636 | 968 | ||
1469 | 637 | def refresh(self): | 969 | def refresh(self): |
1471 | 638 | self.tree_model.set_tree(self.tree, self.branch) | 970 | self.tree_model.set_tree(self.tree, self.branch, |
1472 | 971 | self.changes_mode, self.want_unversioned) | ||
1473 | 639 | self.tree_filter_model.invalidateFilter() | 972 | self.tree_filter_model.invalidateFilter() |
1474 | 640 | 973 | ||
1475 | 641 | def contextMenuEvent(self, event): | 974 | def contextMenuEvent(self, event): |
1476 | @@ -647,32 +980,35 @@ | |||
1477 | 647 | if indexes == None: | 980 | if indexes == None: |
1478 | 648 | indexes = self.selectedIndexes() | 981 | indexes = self.selectedIndexes() |
1479 | 649 | rows = {} | 982 | rows = {} |
1483 | 650 | for index in self.selectedIndexes(): | 983 | for index in indexes: |
1484 | 651 | if index.row() not in rows: | 984 | internal_id = index.data(self.tree_model.INTERNALID).toInt()[0] |
1485 | 652 | rows[index.internalId()] = index | 985 | if internal_id not in rows: |
1486 | 986 | rows[internal_id] = index | ||
1487 | 653 | return rows.values() | 987 | return rows.values() |
1488 | 654 | 988 | ||
1489 | 655 | def get_selection_items(self, indexes=None): | 989 | def get_selection_items(self, indexes=None): |
1490 | 656 | items = [] | 990 | items = [] |
1492 | 657 | if indexes is None: | 991 | if indexes is None or len(indexes)==1 and indexes[0] is None: |
1493 | 658 | indexes = self.get_selection_indexes(indexes) | 992 | indexes = self.get_selection_indexes(indexes) |
1494 | 659 | for index in indexes: | 993 | for index in indexes: |
1495 | 660 | source_index = self.tree_filter_model.mapToSource(index) | 994 | source_index = self.tree_filter_model.mapToSource(index) |
1497 | 661 | items.append(self.tree_model.inventory_items[ | 995 | items.append(self.tree_model.inventory_data[ |
1498 | 662 | source_index.internalId()]) | 996 | source_index.internalId()]) |
1499 | 663 | return items | 997 | return items |
1500 | 664 | 998 | ||
1501 | 665 | def filter_context_menu(self): | 999 | def filter_context_menu(self): |
1502 | 666 | is_working_tree = isinstance(self.tree, WorkingTree) | 1000 | is_working_tree = isinstance(self.tree, WorkingTree) |
1503 | 667 | items = self.get_selection_items() | 1001 | items = self.get_selection_items() |
1505 | 668 | versioned = [not isinstance(item[0], UnversionedItem) | 1002 | versioned = [not isinstance(item.item, UnversionedItem) |
1506 | 669 | for item in items] | 1003 | for item in items] |
1508 | 670 | changed = [item[1] is not None | 1004 | changed = [item.change is not None |
1509 | 671 | for item in items] | 1005 | for item in items] |
1510 | 1006 | versioned_changed = [ver and ch for ver,ch in zip(versioned, changed)] | ||
1511 | 1007 | |||
1512 | 672 | selection_len = len(items) | 1008 | selection_len = len(items) |
1513 | 673 | 1009 | ||
1514 | 674 | single_versioned_file = (selection_len == 1 and versioned[0] and | 1010 | single_versioned_file = (selection_len == 1 and versioned[0] and |
1516 | 675 | items[0][0].kind == "file") | 1011 | items[0].item.kind == "file") |
1517 | 676 | 1012 | ||
1518 | 677 | self.action_open_file.setEnabled(is_working_tree) | 1013 | self.action_open_file.setEnabled(is_working_tree) |
1519 | 678 | self.action_open_file.setVisible(is_working_tree) | 1014 | self.action_open_file.setVisible(is_working_tree) |
1520 | @@ -680,24 +1016,37 @@ | |||
1521 | 680 | self.action_show_annotate.setEnabled(single_versioned_file) | 1016 | self.action_show_annotate.setEnabled(single_versioned_file) |
1522 | 681 | self.action_show_log.setEnabled(any(versioned)) | 1017 | self.action_show_log.setEnabled(any(versioned)) |
1523 | 682 | self.action_show_diff.setVisible(is_working_tree) | 1018 | self.action_show_diff.setVisible(is_working_tree) |
1525 | 683 | self.action_show_diff.setEnabled(any(changed)) | 1019 | self.action_show_diff.setEnabled(any(versioned_changed)) |
1526 | 684 | 1020 | ||
1527 | 685 | self.action_add.setVisible(is_working_tree) | 1021 | self.action_add.setVisible(is_working_tree) |
1528 | 686 | self.action_add.setDisabled(all(versioned)) | 1022 | self.action_add.setDisabled(all(versioned)) |
1529 | 687 | self.action_revert.setVisible(is_working_tree) | 1023 | self.action_revert.setVisible(is_working_tree) |
1531 | 688 | self.action_revert.setEnabled(any(changed) and any(versioned)) | 1024 | self.action_revert.setEnabled(any(versioned_changed)) |
1532 | 1025 | |||
1533 | 1026 | if is_working_tree: | ||
1534 | 1027 | if any(versioned_changed): | ||
1535 | 1028 | self.context_menu.setDefaultAction(self.action_show_diff) | ||
1536 | 1029 | else: | ||
1537 | 1030 | self.context_menu.setDefaultAction(self.action_open_file) | ||
1538 | 1031 | |||
1539 | 689 | 1032 | ||
1541 | 690 | def do_default_action(self, index=None): | 1033 | def do_default_action(self, index): |
1542 | 691 | indexes = self.get_selection_indexes([index]) | 1034 | indexes = self.get_selection_indexes([index]) |
1543 | 692 | if not len(indexes) == 1: | 1035 | if not len(indexes) == 1: |
1544 | 693 | return | 1036 | return |
1545 | 694 | 1037 | ||
1548 | 695 | item = self.get_selection_items(indexes)[0] | 1038 | item_data = self.get_selection_items(indexes)[0] |
1549 | 696 | if item[0].kind == "directory": | 1039 | if item_data.item.kind == "directory": |
1550 | 697 | # Don't do anything, so that the directory can be expanded. | 1040 | # Don't do anything, so that the directory can be expanded. |
1551 | 698 | return | 1041 | return |
1552 | 699 | 1042 | ||
1554 | 700 | self.default_action(index) | 1043 | if isinstance(self.tree, WorkingTree): |
1555 | 1044 | if item_data.change is not None and item_data.change.is_versioned(): | ||
1556 | 1045 | self.show_differences(index=index) | ||
1557 | 1046 | else: | ||
1558 | 1047 | self.open_file(index) | ||
1559 | 1048 | else: | ||
1560 | 1049 | self.show_file_content(index) | ||
1561 | 701 | 1050 | ||
1562 | 702 | @ui_current_widget | 1051 | @ui_current_widget |
1563 | 703 | def show_file_content(self, index=None): | 1052 | def show_file_content(self, index=None): |
1564 | @@ -708,7 +1057,7 @@ | |||
1565 | 708 | return | 1057 | return |
1566 | 709 | 1058 | ||
1567 | 710 | item = self.get_selection_items(indexes)[0] | 1059 | item = self.get_selection_items(indexes)[0] |
1569 | 711 | if isinstance(item[0], UnversionedItem): | 1060 | if isinstance(item.item, UnversionedItem): |
1570 | 712 | return | 1061 | return |
1571 | 713 | 1062 | ||
1572 | 714 | index = indexes[0] | 1063 | index = indexes[0] |
1573 | @@ -748,8 +1097,8 @@ | |||
1574 | 748 | """Show qlog for one selected file(s).""" | 1097 | """Show qlog for one selected file(s).""" |
1575 | 749 | 1098 | ||
1576 | 750 | items = self.get_selection_items() | 1099 | items = self.get_selection_items() |
1579 | 751 | fileids = [item[0].file_id for item in items | 1100 | fileids = [item.item.file_id for item in items |
1580 | 752 | if item[0].file_id is not None] | 1101 | if item.item.file_id is not None] |
1581 | 753 | 1102 | ||
1582 | 754 | window = LogWindow(None, self.branch, fileids) | 1103 | window = LogWindow(None, self.branch, fileids) |
1583 | 755 | window.show() | 1104 | window.show() |
1584 | @@ -767,17 +1116,17 @@ | |||
1585 | 767 | self.window().windows.append(window) | 1116 | self.window().windows.append(window) |
1586 | 768 | 1117 | ||
1587 | 769 | @ui_current_widget | 1118 | @ui_current_widget |
1589 | 770 | def show_differences(self, ext_diff=None): | 1119 | def show_differences(self, ext_diff=None, index=None): |
1590 | 771 | """Show differences for selected file(s).""" | 1120 | """Show differences for selected file(s).""" |
1591 | 772 | 1121 | ||
1593 | 773 | items = self.get_selection_items() | 1122 | items = self.get_selection_items([index]) |
1594 | 774 | if len(items) > 0: | 1123 | if len(items) > 0: |
1595 | 775 | self.tree.lock_read() | 1124 | self.tree.lock_read() |
1596 | 776 | try: | 1125 | try: |
1597 | 777 | # Only paths that have changes. | 1126 | # Only paths that have changes. |
1599 | 778 | paths = [self.tree.id2path(item[0].file_id) | 1127 | paths = [self.tree.id2path(item.item.file_id) |
1600 | 779 | for item in items | 1128 | for item in items |
1602 | 780 | if item[1] is not None] | 1129 | if item.change is not None] |
1603 | 781 | finally: | 1130 | finally: |
1604 | 782 | self.tree.unlock() | 1131 | self.tree.unlock() |
1605 | 783 | else: | 1132 | else: |
1606 | @@ -800,9 +1149,9 @@ | |||
1607 | 800 | items = self.get_selection_items() | 1149 | items = self.get_selection_items() |
1608 | 801 | 1150 | ||
1609 | 802 | # Only paths that are not versioned. | 1151 | # Only paths that are not versioned. |
1611 | 803 | paths = [item[0].path | 1152 | paths = [item.item.path |
1612 | 804 | for item in items | 1153 | for item in items |
1614 | 805 | if isinstance(item[0], UnversionedItem)] | 1154 | if isinstance(item.item, UnversionedItem)] |
1615 | 806 | 1155 | ||
1616 | 807 | if len(paths) == 0: | 1156 | if len(paths) == 0: |
1617 | 808 | return | 1157 | return |
1618 | @@ -829,10 +1178,10 @@ | |||
1619 | 829 | self.tree.lock_read() | 1178 | self.tree.lock_read() |
1620 | 830 | try: | 1179 | try: |
1621 | 831 | # Only paths that have changes. | 1180 | # Only paths that have changes. |
1623 | 832 | paths = [self.tree.id2path(item[0].file_id) | 1181 | paths = [self.tree.id2path(item.item.file_id) |
1624 | 833 | for item in items | 1182 | for item in items |
1627 | 834 | if item[1] is not None and | 1183 | if item.change is not None and |
1628 | 835 | not isinstance(item[0], UnversionedItem)] | 1184 | not isinstance(item.item, UnversionedItem)] |
1629 | 836 | finally: | 1185 | finally: |
1630 | 837 | self.tree.unlock() | 1186 | self.tree.unlock() |
1631 | 838 | 1187 | ||
1632 | @@ -851,4 +1200,39 @@ | |||
1633 | 851 | res = revert_dialog.exec_() | 1200 | res = revert_dialog.exec_() |
1634 | 852 | if res == QtGui.QDialog.Accepted: | 1201 | if res == QtGui.QDialog.Accepted: |
1635 | 853 | self.refresh() | 1202 | self.refresh() |
1636 | 854 | |||
1637 | 855 | \ No newline at end of file | 1203 | \ No newline at end of file |
1638 | 1204 | |||
1639 | 1205 | class SelectAllCheckBox(QtGui.QCheckBox): | ||
1640 | 1206 | |||
1641 | 1207 | def __init__(self, tree_widget, parent=None): | ||
1642 | 1208 | QtGui.QCheckBox.__init__(self, gettext("Select / deselect all"), parent) | ||
1643 | 1209 | |||
1644 | 1210 | self.tree_widget = tree_widget | ||
1645 | 1211 | #self.setTristate(True) | ||
1646 | 1212 | |||
1647 | 1213 | self.connect(tree_widget.tree_model, | ||
1648 | 1214 | QtCore.SIGNAL("dataChanged(QModelIndex, QModelIndex)"), | ||
1649 | 1215 | self.on_data_changed) | ||
1650 | 1216 | |||
1651 | 1217 | self.connect(self, QtCore.SIGNAL("clicked(bool)"), | ||
1652 | 1218 | self.clicked) | ||
1653 | 1219 | |||
1654 | 1220 | def on_data_changed(self, start_index, end_index): | ||
1655 | 1221 | self.update_state() | ||
1656 | 1222 | |||
1657 | 1223 | def update_state(self): | ||
1658 | 1224 | model = self.tree_widget.tree_model | ||
1659 | 1225 | root_index = model._index_from_id(0, model.NAME) | ||
1660 | 1226 | |||
1661 | 1227 | state = model.data(root_index, QtCore.Qt.CheckStateRole) | ||
1662 | 1228 | self.setCheckState(state.toInt()[0]) | ||
1663 | 1229 | |||
1664 | 1230 | def clicked(self, state): | ||
1665 | 1231 | model = self.tree_widget.tree_model | ||
1666 | 1232 | root_index = model._index_from_id(0, model.NAME) | ||
1667 | 1233 | if state: | ||
1668 | 1234 | state = QtCore.QVariant(QtCore.Qt.Checked) | ||
1669 | 1235 | else: | ||
1670 | 1236 | state = QtCore.QVariant(QtCore.Qt.Unchecked) | ||
1671 | 1237 | |||
1672 | 1238 | model.setData(root_index, QtCore.QVariant(state), | ||
1673 | 1239 | QtCore.Qt.CheckStateRole) | ||
1674 | 856 | 1240 | ||
1675 | === removed file 'lib/wtlist.py' | |||
1676 | --- lib/wtlist.py 2009-06-18 03:44:48 +0000 | |||
1677 | +++ lib/wtlist.py 1970-01-01 00:00:00 +0000 | |||
1678 | @@ -1,449 +0,0 @@ | |||
1679 | 1 | # -*- coding: utf-8 -*- | ||
1680 | 2 | # | ||
1681 | 3 | # QBzr - Qt frontend to Bazaar commands | ||
1682 | 4 | # Copyright (C) 2006-2007 Lukáš Lalinský <lalinsky@gmail.com> | ||
1683 | 5 | # Copyright (C) 2006 Trolltech ASA | ||
1684 | 6 | # Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org> | ||
1685 | 7 | # | ||
1686 | 8 | # This program is free software; you can redistribute it and/or | ||
1687 | 9 | # modify it under the terms of the GNU General Public License | ||
1688 | 10 | # as published by the Free Software Foundation; either version 2 | ||
1689 | 11 | # of the License, or (at your option) any later version. | ||
1690 | 12 | # | ||
1691 | 13 | # This program is distributed in the hope that it will be useful, | ||
1692 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1693 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1694 | 16 | # GNU General Public License for more details. | ||
1695 | 17 | # | ||
1696 | 18 | # You should have received a copy of the GNU General Public License | ||
1697 | 19 | # along with this program; if not, write to the Free Software | ||
1698 | 20 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
1699 | 21 | |||
1700 | 22 | """A QTreeWidget that shows the items in a working tree, and includes a common | ||
1701 | 23 | context menu.""" | ||
1702 | 24 | |||
1703 | 25 | from PyQt4 import QtCore, QtGui | ||
1704 | 26 | |||
1705 | 27 | from bzrlib import osutils | ||
1706 | 28 | |||
1707 | 29 | from bzrlib.plugins.qbzr.lib.diff import ( | ||
1708 | 30 | show_diff, | ||
1709 | 31 | has_ext_diff, | ||
1710 | 32 | ExtDiffMenu, | ||
1711 | 33 | InternalWTDiffArgProvider, | ||
1712 | 34 | ) | ||
1713 | 35 | from bzrlib.plugins.qbzr.lib.i18n import gettext, N_ | ||
1714 | 36 | from bzrlib.plugins.qbzr.lib.subprocess import SimpleSubProcessDialog | ||
1715 | 37 | from bzrlib.plugins.qbzr.lib.util import ( | ||
1716 | 38 | file_extension, | ||
1717 | 39 | runs_in_loading_queue, | ||
1718 | 40 | ) | ||
1719 | 41 | |||
1720 | 42 | |||
1721 | 43 | class WorkingTreeFileList(QtGui.QTreeWidget): | ||
1722 | 44 | |||
1723 | 45 | SELECTALL_MESSAGE = N_("Select / deselect all") | ||
1724 | 46 | |||
1725 | 47 | def __init__(self, parent, tree): | ||
1726 | 48 | QtGui.QTreeWidget.__init__(self, parent) | ||
1727 | 49 | self._ignore_select_all_changes = False | ||
1728 | 50 | self.selectall_checkbox = None # added by client. | ||
1729 | 51 | self.tree = tree | ||
1730 | 52 | |||
1731 | 53 | def setup_actions(self): | ||
1732 | 54 | """Setup double-click and context menu""" | ||
1733 | 55 | parent = self.parentWidget() | ||
1734 | 56 | parent.connect(self, | ||
1735 | 57 | QtCore.SIGNAL("itemDoubleClicked(QTreeWidgetItem *, int)"), | ||
1736 | 58 | self.itemDoubleClicked) | ||
1737 | 59 | |||
1738 | 60 | self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) | ||
1739 | 61 | parent.connect(self, | ||
1740 | 62 | QtCore.SIGNAL("itemSelectionChanged()"), | ||
1741 | 63 | self.update_context_menu_actions) | ||
1742 | 64 | parent.connect(self, | ||
1743 | 65 | QtCore.SIGNAL("customContextMenuRequested(QPoint)"), | ||
1744 | 66 | self.show_context_menu) | ||
1745 | 67 | |||
1746 | 68 | self.context_menu = QtGui.QMenu(self) | ||
1747 | 69 | if has_ext_diff(): | ||
1748 | 70 | self.diff_menu = ExtDiffMenu(self) | ||
1749 | 71 | self.context_menu.addMenu(self.diff_menu) | ||
1750 | 72 | self.connect(self.diff_menu, QtCore.SIGNAL("triggered(QString)"), | ||
1751 | 73 | self.show_differences) | ||
1752 | 74 | self.show_diff_ui = self.diff_menu | ||
1753 | 75 | else: | ||
1754 | 76 | self.show_diff_action = self.context_menu.addAction( | ||
1755 | 77 | gettext("Show &differences..."), self.show_differences) | ||
1756 | 78 | self.context_menu.setDefaultAction(self.show_diff_action) | ||
1757 | 79 | self.show_diff_ui = self.show_diff_action | ||
1758 | 80 | |||
1759 | 81 | self.revert_action = self.context_menu.addAction( | ||
1760 | 82 | gettext("&Revert..."), self.revert_selected) | ||
1761 | 83 | # set all actions to disabled so it does the right thing with an empty | ||
1762 | 84 | # list (our itemSelectionChanged() will fire as soon as we select one) | ||
1763 | 85 | self.revert_action.setEnabled(False) | ||
1764 | 86 | self.show_diff_ui.setEnabled(False) | ||
1765 | 87 | |||
1766 | 88 | @runs_in_loading_queue | ||
1767 | 89 | def fill(self, items_iter): | ||
1768 | 90 | self.setTextElideMode(QtCore.Qt.ElideMiddle) | ||
1769 | 91 | self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) | ||
1770 | 92 | self.setSortingEnabled(True) | ||
1771 | 93 | self.setHeaderLabels([gettext("File"), gettext("Extension"), gettext("Status")]) | ||
1772 | 94 | header = self.header() | ||
1773 | 95 | header.setStretchLastSection(False) | ||
1774 | 96 | header.setResizeMode(0, QtGui.QHeaderView.Stretch) | ||
1775 | 97 | header.setResizeMode(1, QtGui.QHeaderView.ResizeToContents) | ||
1776 | 98 | header.setResizeMode(2, QtGui.QHeaderView.ResizeToContents) | ||
1777 | 99 | self.setRootIsDecorated(False) | ||
1778 | 100 | self._ignore_select_all_changes = True # don't update as we add items! | ||
1779 | 101 | |||
1780 | 102 | # Each items_iter returns a tuple of (changes_tuple, is_checked) | ||
1781 | 103 | # Where changes_tuple is a single item from iter_changes(): | ||
1782 | 104 | # (file_id, (path_in_source, path_in_target), | ||
1783 | 105 | # changed_content, versioned, parent, name, kind, | ||
1784 | 106 | # executable) | ||
1785 | 107 | # Note that the current filter is used to determine if the items are | ||
1786 | 108 | # shown or not | ||
1787 | 109 | self.item_to_data = {} | ||
1788 | 110 | items = [] | ||
1789 | 111 | for change_desc, visible, checked in items_iter: | ||
1790 | 112 | (file_id, (path_in_source, path_in_target), | ||
1791 | 113 | changed_content, versioned, parent, name, kind, | ||
1792 | 114 | executable) = change_desc | ||
1793 | 115 | |||
1794 | 116 | if versioned == (False, False): | ||
1795 | 117 | if self.tree.is_ignored(path_in_target): | ||
1796 | 118 | status = gettext("ignored") | ||
1797 | 119 | else: | ||
1798 | 120 | status = gettext("non-versioned") | ||
1799 | 121 | ext = file_extension(path_in_target) | ||
1800 | 122 | name = path_in_target | ||
1801 | 123 | elif versioned == (False, True): | ||
1802 | 124 | status = gettext("added") | ||
1803 | 125 | ext = file_extension(path_in_target) | ||
1804 | 126 | name = path_in_target + osutils.kind_marker(kind[1]) | ||
1805 | 127 | elif versioned == (True, False): | ||
1806 | 128 | status = gettext("removed") | ||
1807 | 129 | ext = file_extension(path_in_source) | ||
1808 | 130 | name = path_in_source + osutils.kind_marker(kind[0]) | ||
1809 | 131 | elif kind[0] is not None and kind[1] is None: | ||
1810 | 132 | status = gettext("missing") | ||
1811 | 133 | ext = file_extension(path_in_source) | ||
1812 | 134 | name = path_in_source + osutils.kind_marker(kind[0]) | ||
1813 | 135 | else: | ||
1814 | 136 | # versioned = True, True - so either renamed or modified | ||
1815 | 137 | # or properties changed (x-bit). | ||
1816 | 138 | renamed = (parent[0], name[0]) != (parent[1], name[1]) | ||
1817 | 139 | if renamed: | ||
1818 | 140 | if changed_content: | ||
1819 | 141 | status = gettext("renamed and modified") | ||
1820 | 142 | else: | ||
1821 | 143 | status = gettext("renamed") | ||
1822 | 144 | name = "%s%s => %s%s" % (path_in_source, | ||
1823 | 145 | osutils.kind_marker(kind[0]), | ||
1824 | 146 | path_in_target, | ||
1825 | 147 | osutils.kind_marker(kind[0])) | ||
1826 | 148 | ext = file_extension(path_in_target) | ||
1827 | 149 | elif changed_content: | ||
1828 | 150 | status = gettext("modified") | ||
1829 | 151 | name = path_in_target + osutils.kind_marker(kind[1]) | ||
1830 | 152 | ext = file_extension(path_in_target) | ||
1831 | 153 | elif executable[0] != executable[1]: | ||
1832 | 154 | status = gettext("modified (x-bit)") | ||
1833 | 155 | name = path_in_target + osutils.kind_marker(kind[1]) | ||
1834 | 156 | ext = file_extension(path_in_target) | ||
1835 | 157 | else: | ||
1836 | 158 | raise RuntimeError, "what status am I missing??" | ||
1837 | 159 | |||
1838 | 160 | item = QtGui.QTreeWidgetItem() | ||
1839 | 161 | item.setText(0, name) | ||
1840 | 162 | item.setText(1, ext) | ||
1841 | 163 | item.setText(2, status) | ||
1842 | 164 | if visible: | ||
1843 | 165 | items.append(item) | ||
1844 | 166 | |||
1845 | 167 | if checked is None: | ||
1846 | 168 | item.setCheckState(0, QtCore.Qt.PartiallyChecked) | ||
1847 | 169 | item.setFlags(item.flags() & ~QtCore.Qt.ItemIsUserCheckable) | ||
1848 | 170 | elif checked: | ||
1849 | 171 | item.setCheckState(0, QtCore.Qt.Checked) | ||
1850 | 172 | else: | ||
1851 | 173 | item.setCheckState(0, QtCore.Qt.Unchecked) | ||
1852 | 174 | self.item_to_data[item] = change_desc | ||
1853 | 175 | # add them all to the tree in one hit. | ||
1854 | 176 | self.insertTopLevelItems(0, items) | ||
1855 | 177 | self._ignore_select_all_changes = False | ||
1856 | 178 | if self.selectall_checkbox is not None: | ||
1857 | 179 | self.update_selectall_state(None, None) | ||
1858 | 180 | |||
1859 | 181 | def set_item_hidden(self, item, hide): | ||
1860 | 182 | # Due to what seems a bug in Qt "hiding" an item isn't always | ||
1861 | 183 | # reliable - so a "hidden" item is simply not added to the tree! | ||
1862 | 184 | # See https://bugs.launchpad.net/qbzr/+bug/274295 | ||
1863 | 185 | index = self.indexOfTopLevelItem(item) | ||
1864 | 186 | if index == -1 and not hide: | ||
1865 | 187 | self.addTopLevelItem(item) | ||
1866 | 188 | elif index != -1 and hide: | ||
1867 | 189 | self.takeTopLevelItem(index) | ||
1868 | 190 | |||
1869 | 191 | def is_item_hidden(self, item): | ||
1870 | 192 | return self.indexOfTopLevelItem(item) == -1 | ||
1871 | 193 | |||
1872 | 194 | def iter_treeitem_and_desc(self, include_hidden=False): | ||
1873 | 195 | """iterators to help work with the selection, checked items, etc""" | ||
1874 | 196 | for ti, desc in self.item_to_data.iteritems(): | ||
1875 | 197 | if include_hidden or not self.is_item_hidden(ti): | ||
1876 | 198 | yield ti, desc | ||
1877 | 199 | |||
1878 | 200 | def iter_selection(self): | ||
1879 | 201 | for i in self.selectedItems(): | ||
1880 | 202 | yield self.item_to_data[i] | ||
1881 | 203 | |||
1882 | 204 | def iter_checked(self): | ||
1883 | 205 | # XXX - just use self.iter_treeitem_and_desc() - no need to hit the | ||
1884 | 206 | # XXX tree object at all!? | ||
1885 | 207 | for i in range(self.topLevelItemCount()): | ||
1886 | 208 | item = self.topLevelItem(i) | ||
1887 | 209 | if item.checkState(0) == QtCore.Qt.Checked: | ||
1888 | 210 | yield self.item_to_data[item] | ||
1889 | 211 | |||
1890 | 212 | def show_context_menu(self, pos): | ||
1891 | 213 | """Context menu and double-click related functions...""" | ||
1892 | 214 | self.context_menu.popup(self.viewport().mapToGlobal(pos)) | ||
1893 | 215 | |||
1894 | 216 | def update_context_menu_actions(self): | ||
1895 | 217 | contains_non_versioned = False | ||
1896 | 218 | for desc in self.iter_selection(): | ||
1897 | 219 | if desc[3] == (False, False): | ||
1898 | 220 | contains_non_versioned = True | ||
1899 | 221 | break | ||
1900 | 222 | self.revert_action.setEnabled(not contains_non_versioned) | ||
1901 | 223 | self.show_diff_ui.setEnabled(not contains_non_versioned) | ||
1902 | 224 | |||
1903 | 225 | def revert_selected(self): | ||
1904 | 226 | """Revert the selected file.""" | ||
1905 | 227 | items = self.selectedItems() | ||
1906 | 228 | if not items: | ||
1907 | 229 | return | ||
1908 | 230 | |||
1909 | 231 | paths = [self.item_to_data[item][1][1] for item in items] | ||
1910 | 232 | |||
1911 | 233 | args = ["revert"] | ||
1912 | 234 | args.extend(paths) | ||
1913 | 235 | desc = (gettext("Revert %s to latest revision.") % ", ".join(paths)) | ||
1914 | 236 | revert_dialog = SimpleSubProcessDialog(gettext("Revert"), | ||
1915 | 237 | desc=desc, | ||
1916 | 238 | args=args, | ||
1917 | 239 | dir=self.tree.basedir, | ||
1918 | 240 | parent=self, | ||
1919 | 241 | hide_progress=True, | ||
1920 | 242 | ) | ||
1921 | 243 | res = revert_dialog.exec_() | ||
1922 | 244 | if res == QtGui.QDialog.Accepted: | ||
1923 | 245 | for item in items: | ||
1924 | 246 | index = self.indexOfTopLevelItem(item) | ||
1925 | 247 | self.takeTopLevelItem(index) | ||
1926 | 248 | |||
1927 | 249 | def itemDoubleClicked(self, items=None, column=None): | ||
1928 | 250 | self.show_differences() | ||
1929 | 251 | |||
1930 | 252 | def show_differences(self, ext_diff=None): | ||
1931 | 253 | """Show differences between the working copy and the last revision.""" | ||
1932 | 254 | if not self.show_diff_ui.isEnabled(): | ||
1933 | 255 | return | ||
1934 | 256 | |||
1935 | 257 | entries = [desc.path() for desc in self.iter_selection()] | ||
1936 | 258 | if entries: | ||
1937 | 259 | arg_provider = InternalWTDiffArgProvider( | ||
1938 | 260 | self.tree.basis_tree().get_revision_id(), self.tree, | ||
1939 | 261 | self.tree.branch, self.tree.branch, | ||
1940 | 262 | specific_files=entries) | ||
1941 | 263 | |||
1942 | 264 | show_diff(arg_provider, ext_diff=ext_diff, | ||
1943 | 265 | parent_window=self.topLevelWidget()) | ||
1944 | 266 | |||
1945 | 267 | def set_selectall_checkbox(self, checkbox): | ||
1946 | 268 | """Helpers for a 'show all' checkbox. Parent widgets must create the | ||
1947 | 269 | widget and pass it to us. | ||
1948 | 270 | """ | ||
1949 | 271 | checkbox.setTristate(True) | ||
1950 | 272 | self.selectall_checkbox = checkbox | ||
1951 | 273 | parent = self.parentWidget() | ||
1952 | 274 | parent.connect(self, | ||
1953 | 275 | QtCore.SIGNAL("itemChanged(QTreeWidgetItem *, int)"), | ||
1954 | 276 | self.update_selectall_state) | ||
1955 | 277 | |||
1956 | 278 | parent.connect(checkbox, QtCore.SIGNAL("stateChanged(int)"), | ||
1957 | 279 | self.selectall_changed) | ||
1958 | 280 | |||
1959 | 281 | def update_selectall_state(self, item, column): | ||
1960 | 282 | """Update the state of the 'select all' checkbox to reflect the state | ||
1961 | 283 | of the items in the list. | ||
1962 | 284 | """ | ||
1963 | 285 | if self._ignore_select_all_changes: | ||
1964 | 286 | return | ||
1965 | 287 | checked = 0 | ||
1966 | 288 | num_items = 0 | ||
1967 | 289 | |||
1968 | 290 | for (tree_item, change_desc) in self.iter_treeitem_and_desc(): | ||
1969 | 291 | if tree_item.checkState(0) == QtCore.Qt.Checked: | ||
1970 | 292 | checked += 1 | ||
1971 | 293 | num_items += 1 | ||
1972 | 294 | self._ignore_select_all_changes = True | ||
1973 | 295 | if checked == 0: | ||
1974 | 296 | self.selectall_checkbox.setCheckState(QtCore.Qt.Unchecked) | ||
1975 | 297 | elif checked == num_items: | ||
1976 | 298 | self.selectall_checkbox.setCheckState(QtCore.Qt.Checked) | ||
1977 | 299 | else: | ||
1978 | 300 | self.selectall_checkbox.setCheckState(QtCore.Qt.PartiallyChecked) | ||
1979 | 301 | self._ignore_select_all_changes = False | ||
1980 | 302 | |||
1981 | 303 | def selectall_changed(self, state): | ||
1982 | 304 | if self._ignore_select_all_changes or not self.selectall_checkbox.isEnabled(): | ||
1983 | 305 | return | ||
1984 | 306 | if state == QtCore.Qt.PartiallyChecked: | ||
1985 | 307 | self.selectall_checkbox.setCheckState(QtCore.Qt.Checked) | ||
1986 | 308 | return | ||
1987 | 309 | |||
1988 | 310 | self._ignore_select_all_changes = True | ||
1989 | 311 | for (tree_item, change_desc) in self.iter_treeitem_and_desc(): | ||
1990 | 312 | tree_item.setCheckState(0, QtCore.Qt.CheckState(state)) | ||
1991 | 313 | self._ignore_select_all_changes = False | ||
1992 | 314 | |||
1993 | 315 | |||
1994 | 316 | class ChangeDesc(tuple): | ||
1995 | 317 | """Helper class that "knows" about internals of iter_changes' changed entry | ||
1996 | 318 | description tuple, and provides additional helper methods. | ||
1997 | 319 | |||
1998 | 320 | iter_changes return tuple with info about changed entry: | ||
1999 | 321 | [0]: file_id -> ascii string | ||
2000 | 322 | [1]: paths -> 2-tuple (old, new) fullpaths unicode/None | ||
2001 | 323 | [2]: changed_content -> bool | ||
2002 | 324 | [3]: versioned -> 2-tuple (bool, bool) | ||
2003 | 325 | [4]: parent -> 2-tuple | ||
2004 | 326 | [5]: name -> 2-tuple (old_name, new_name) utf-8?/None | ||
2005 | 327 | [6]: kind -> 2-tuple (string/None, string/None) | ||
2006 | 328 | [7]: executable -> 2-tuple (bool/None, bool/None) | ||
2007 | 329 | |||
2008 | 330 | --optional-- | ||
2009 | 331 | [8]: is_ignored -> If the file is ignored, pattern which caused it to | ||
2010 | 332 | be ignored, otherwise None. | ||
2011 | 333 | |||
2012 | 334 | NOTE: None value used for non-existing entry in corresponding | ||
2013 | 335 | tree, e.g. for added/deleted/ignored/unversioned | ||
2014 | 336 | """ | ||
2015 | 337 | |||
2016 | 338 | # XXX We should may be try get this into bzrlib. | ||
2017 | 339 | # XXX We should use this in qdiff. | ||
2018 | 340 | |||
2019 | 341 | def fileid(desc): | ||
2020 | 342 | return desc[0] | ||
2021 | 343 | |||
2022 | 344 | def path(desc): | ||
2023 | 345 | """Return a suitable entry for a 'specific_files' param to bzr functions.""" | ||
2024 | 346 | oldpath, newpath = desc[1] | ||
2025 | 347 | return newpath or oldpath | ||
2026 | 348 | |||
2027 | 349 | def oldpath(desc): | ||
2028 | 350 | """Return oldpath for renames.""" | ||
2029 | 351 | return desc[1][0] | ||
2030 | 352 | |||
2031 | 353 | def kind(desc): | ||
2032 | 354 | oldkind, newkind = desc[6] | ||
2033 | 355 | return newkind or oldkind | ||
2034 | 356 | |||
2035 | 357 | def is_versioned(desc): | ||
2036 | 358 | return desc[3] != (False, False) | ||
2037 | 359 | |||
2038 | 360 | def is_modified(desc): | ||
2039 | 361 | return (desc[3] != (False, False) and desc[2]) | ||
2040 | 362 | |||
2041 | 363 | def is_renamed(desc): | ||
2042 | 364 | return (desc[3] == (True, True) | ||
2043 | 365 | and (desc[4][0], desc[5][0]) != (desc[4][1], desc[5][1])) | ||
2044 | 366 | |||
2045 | 367 | def is_tree_root(desc): | ||
2046 | 368 | """Check if entry actually tree root.""" | ||
2047 | 369 | if desc[3] != (False, False) and desc[4] == (None, None): | ||
2048 | 370 | # TREE_ROOT has not parents (desc[4]). | ||
2049 | 371 | # But because we could want to see unversioned files | ||
2050 | 372 | # we need to check for versioned flag (desc[3]) | ||
2051 | 373 | return True | ||
2052 | 374 | return False | ||
2053 | 375 | |||
2054 | 376 | def is_missing(desc): | ||
2055 | 377 | """Check if file was present in previous revision but now it's gone | ||
2056 | 378 | (i.e. deleted manually, without invoking `bzr remove` command) | ||
2057 | 379 | """ | ||
2058 | 380 | return (desc[3] == (True, True) and desc[6][1] is None) | ||
2059 | 381 | |||
2060 | 382 | def is_misadded(desc): | ||
2061 | 383 | """Check if file was added to the working tree but then gone | ||
2062 | 384 | (i.e. deleted manually, without invoking `bzr remove` command) | ||
2063 | 385 | """ | ||
2064 | 386 | return (desc[3] == (False, True) and desc[6][1] is None) | ||
2065 | 387 | |||
2066 | 388 | def is_ignored(desc): | ||
2067 | 389 | if len(desc) >= 8: | ||
2068 | 390 | return desc[8] | ||
2069 | 391 | else: | ||
2070 | 392 | return None | ||
2071 | 393 | |||
2072 | 394 | def status(desc): | ||
2073 | 395 | if len(desc) == 8: | ||
2074 | 396 | (file_id, (path_in_source, path_in_target), | ||
2075 | 397 | changed_content, versioned, parent, name, kind, | ||
2076 | 398 | executable) = desc | ||
2077 | 399 | is_ignored = None | ||
2078 | 400 | elif len(desc) == 9: | ||
2079 | 401 | (file_id, (path_in_source, path_in_target), | ||
2080 | 402 | changed_content, versioned, parent, name, kind, | ||
2081 | 403 | executable, is_ignored) = desc | ||
2082 | 404 | else: | ||
2083 | 405 | raise RuntimeError, "Unkown number of items to unpack." | ||
2084 | 406 | |||
2085 | 407 | if versioned == (False, False): | ||
2086 | 408 | if is_ignored: | ||
2087 | 409 | return gettext("ignored") | ||
2088 | 410 | else: | ||
2089 | 411 | return gettext("non-versioned") | ||
2090 | 412 | elif versioned == (False, True): | ||
2091 | 413 | return gettext("added") | ||
2092 | 414 | elif versioned == (True, False): | ||
2093 | 415 | return gettext("removed") | ||
2094 | 416 | elif kind[0] is not None and kind[1] is None: | ||
2095 | 417 | return gettext("missing") | ||
2096 | 418 | else: | ||
2097 | 419 | # versioned = True, True - so either renamed or modified | ||
2098 | 420 | # or properties changed (x-bit). | ||
2099 | 421 | renamed = (parent[0], name[0]) != (parent[1], name[1]) | ||
2100 | 422 | if renamed: | ||
2101 | 423 | if changed_content: | ||
2102 | 424 | return gettext("renamed and modified") | ||
2103 | 425 | else: | ||
2104 | 426 | return gettext("renamed") | ||
2105 | 427 | elif changed_content: | ||
2106 | 428 | return gettext("modified") | ||
2107 | 429 | elif executable[0] != executable[1]: | ||
2108 | 430 | return gettext("modified (x-bit)") | ||
2109 | 431 | else: | ||
2110 | 432 | raise RuntimeError, "what status am I missing??" | ||
2111 | 433 | |||
2112 | 434 | |||
2113 | 435 | def closure_in_selected_list(selected_list): | ||
2114 | 436 | """Return in_selected_list(path) function for given selected_list.""" | ||
2115 | 437 | |||
2116 | 438 | def in_selected_list(path): | ||
2117 | 439 | """Check: is path belongs to some selected_list.""" | ||
2118 | 440 | if path in selected_list: | ||
2119 | 441 | return True | ||
2120 | 442 | for p in selected_list: | ||
2121 | 443 | if path.startswith(p): | ||
2122 | 444 | return True | ||
2123 | 445 | return False | ||
2124 | 446 | |||
2125 | 447 | if not selected_list: | ||
2126 | 448 | return lambda path: True | ||
2127 | 449 | return in_selected_list |
This replaces wtlist with treewidget in qcommit, qadd, and qrevert.
To do this I have added a mode to the treewidget called changes_mode. This only show changes, and not the whole tree. Like the old wtlist, changes from subdirs are shown in the root, e.g.
In normal mode, you would see:
Dir1
File1 Modified
but in changes_mode, you will see:
Dir1/File1 Modified
Unless a parent directory is shown, which may happen if you have a unversioned, added, or renamed directory.
e.g. in changes_mode, you will get:
dir1 => dir2 Renamed
File3 Modified
dir4 Added
file5 Added
Drive by improvement: qcommit now adjusts the auto complete word list if you change which files are selected in the file list.