~qbzr-dev/qbzr/threadless-throbber

230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
1
# -*- coding: utf-8 -*-
2
#
3
# QBzr - Qt frontend to Bazaar commands
4
# Copyright (C) 2006-2007 Gary van der Merwe <garyvdm@gmail.com> 
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
20
from PyQt4 import QtCore, QtGui
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
21
from time import (strftime, localtime, clock)
516.1.8 by Gary van der Merwe
qlog: Correctly handle missing revisions.
22
from bzrlib import (lazy_regex, errors)
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
23
from bzrlib.revision import NULL_REVISION
230.1.5 by Gary van der Merwe
Move linegraph.py code to logmodel.py.
24
from bzrlib.tsort import merge_sort
516.1.1 by Gary van der Merwe
qlog: Make it possible to load log for branches in different repositories.
25
from bzrlib.graph import (Graph, _StackedParentsProvider)
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
26
from bzrlib.plugins.qbzr.lib.i18n import gettext
27
from bzrlib.plugins.qbzr.lib.util import (
28
    extract_name,
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
29
    BackgroundJob,
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
30
    )
31
349.3.4 by Gary van der Merwe
Merge Log
32
have_search = True 
33
try: 
34
    from bzrlib.plugins.search import errors as search_errors 
35
    from bzrlib.plugins.search import index as search_index 
36
except ImportError: 
37
    have_search = False 
38
 
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
39
TagsRole = QtCore.Qt.UserRole + 1
40
BugIdsRole = QtCore.Qt.UserRole + 2
349.1.41 by Gary van der Merwe
qlog: show tags for branch tips.
41
BranchTagsRole = QtCore.Qt.UserRole + 3
42
GraphNodeRole = QtCore.Qt.UserRole + 4
43
GraphLinesRole = QtCore.Qt.UserRole + 5
44
GraphTwistyStateRole = QtCore.Qt.UserRole + 6
45
RevIdRole = QtCore.Qt.UserRole + 7
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
46
47
FilterIdRole = QtCore.Qt.UserRole + 100
48
FilterMessageRole = QtCore.Qt.UserRole + 101
49
FilterAuthorRole = QtCore.Qt.UserRole + 102
50
FilterRevnoRole = QtCore.Qt.UserRole + 103
349.3.1 by Gary van der Merwe
qlog: Intergrate with bzr-search for indexed searching.
51
FilterSearchRole = QtCore.Qt.UserRole + 104
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
52
53
COL_REV = 0
230.1.26 by Gary van der Merwe
qlog: Make the Graph and Message columns one.
54
COL_MESSAGE = 1
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
55
COL_DATE = 2
56
COL_AUTHOR = 3
57
344 by Alexander Belchenko
support for Redmine bugs URLs (to show bug numbers in qlog).
58
_bug_id_re = lazy_regex.lazy_compile(r'(?:'
59
    r'bugs/'                    # Launchpad bugs URL
60
    r'|ticket/'                 # Trac bugs URL
61
    r'|show_bug\.cgi\?id='      # Bugzilla bugs URL
62
    r'|issues/show/'            # Redmine bugs URL
63
    r')(\d+)(?:\b|$)')
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
64
373 by Lukáš Lalinský
Fix qlog on PyQt 4.4.3
65
349.1.33 by Gary van der Merwe
qlog: Make it possible to specify multiple branches, or multiple file. Make pending merges are show.
66
def get_bug_id(bug_url):
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
67
    match = _bug_id_re.search(bug_url)
68
    if match:
69
        return match.group(1)
70
    return None
71
318 by Alexander Belchenko
Restore showing tags in revision info (bottom left window) in qlog.
72
373 by Lukáš Lalinský
Fix qlog on PyQt 4.4.3
73
74
try:
75
    QVariant_fromList = QtCore.QVariant.fromList
76
except AttributeError:
77
    QVariant_fromList = QtCore.QVariant
78
79
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
80
class GraphModel(QtCore.QAbstractTableModel):
318 by Alexander Belchenko
Restore showing tags in revision info (bottom left window) in qlog.
81
557 by Gary van der Merwe
qlog: Make all methods that are run from a timer use the new report_exception.
82
    def __init__(self, process_events_ptr, report_exception_ptr, parent=None):
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
83
        QtCore.QAbstractTableModel.__init__(self, parent)
84
        
85
        self.horizontalHeaderLabels = [gettext("Rev"),
230.1.26 by Gary van der Merwe
qlog: Make the Graph and Message columns one.
86
                                       gettext("Message"),
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
87
                                       gettext("Date"),
88
                                       gettext("Author"),
89
                                       ]
90
        
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
91
        self.merge_sorted_revisions = []
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
92
        self.columns_len = 0
93
        self.revisions = {}
94
        self.tags = {}
230.1.24 by Gary van der Merwe
qlog: Get search working again.
95
        self.searchMode = False
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
96
        self.touches_file_msri = None
97
        self.msri_index = {}
442 by Gary van der Merwe
qlog: Refresh should not cause revisions to be reloaded. And don't cause 2 revisions loading timers to be started.
98
        self.stop_revision_loading = False
555 by Gary van der Merwe
qlog: Stop loading methods stright away if we press close by raising an execption.
99
        self.processEvents = process_events_ptr
557 by Gary van der Merwe
qlog: Make all methods that are run from a timer use the new report_exception.
100
        self.report_exception = report_exception_ptr
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
101
        self.queue = []
102
        
103
        self.load_queued_revisions = LoadQueuedRevisions(self)
104
        self.load_all_revisions = LoadAllRevisions(self)
105
   
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
106
    def setGraphFilterProxyModel(self, graphFilterProxyModel):
107
        self.graphFilterProxyModel = graphFilterProxyModel
108
    
516.1.3 by Gary van der Merwe
qlog: better way to work out for a revision, what branch it can be found in.
109
    def loadBranch(self, branches, heads, specific_fileids = []):
110
        self.heads = heads
516.1.1 by Gary van der Merwe
qlog: Make it possible to load log for branches in different repositories.
111
        self.branches = branches
112
        self.repos = {}
113
        for branch in self.branches:
114
            if branch.repository.base not in self.repos:
516.1.3 by Gary van der Merwe
qlog: better way to work out for a revision, what branch it can be found in.
115
                self.repos[branch.repository.base] = branch.repository
516.1.1 by Gary van der Merwe
qlog: Make it possible to load log for branches in different repositories.
116
        
117
        for branch in self.branches:
118
            branch.lock_read()
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
119
        try:
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
120
            self.emit(QtCore.SIGNAL("layoutAboutToBeChanged()"))
516.1.1 by Gary van der Merwe
qlog: Make it possible to load log for branches in different repositories.
121
            self.tags = {}
122
            for branch in self.branches:
123
                branch_tags = branch.tags.get_reverse_tag_dict()  # revid to tags map
124
                for revid, tags in branch_tags.iteritems():
125
                    if revid in self.tags:
516.1.4 by Gary van der Merwe
qlog: Don't show tags more than once.
126
                        self.tags[revid].update(set(tags))
516.1.1 by Gary van der Merwe
qlog: Make it possible to load log for branches in different repositories.
127
                    else:
516.1.4 by Gary van der Merwe
qlog: Don't show tags more than once.
128
                        self.tags[revid] = set(tags)
516.1.1 by Gary van der Merwe
qlog: Make it possible to load log for branches in different repositories.
129
            
516.1.3 by Gary van der Merwe
qlog: better way to work out for a revision, what branch it can be found in.
130
            self.start_revs = [rev for rev in self.heads if not rev == NULL_REVISION]
131
            self.start_revs.sort(lambda x, y:cmp(self.heads[x][0], self.heads[y][0]))
349.1.43 by Gary van der Merwe
qlog: Maintain the order of the start revisions.
132
            
516.1.1 by Gary van der Merwe
qlog: Make it possible to load log for branches in different repositories.
133
            parents_providers = [repo._make_parents_provider() for repo in self.repos.itervalues()]
134
            graph = Graph(_StackedParentsProvider(parents_providers))
135
            
230.1.6 by Gary van der Merwe
Split the graph loading and sorting and the line computing into 2 methods.
136
            self.graph_parents = {}
230.1.5 by Gary van der Merwe
Move linegraph.py code to logmodel.py.
137
            ghosts = set()
230.1.6 by Gary van der Merwe
Split the graph loading and sorting and the line computing into 2 methods.
138
            self.graph_children = {}
516.1.3 by Gary van der Merwe
qlog: better way to work out for a revision, what branch it can be found in.
139
            for (revid, parent_revids) in graph.iter_ancestry(self.start_revs):
230.1.5 by Gary van der Merwe
Move linegraph.py code to logmodel.py.
140
                if parent_revids is None:
141
                    ghosts.add(revid)
142
                    continue
143
                if parent_revids == (NULL_REVISION,):
230.1.6 by Gary van der Merwe
Split the graph loading and sorting and the line computing into 2 methods.
144
                    self.graph_parents[revid] = ()
230.1.5 by Gary van der Merwe
Move linegraph.py code to logmodel.py.
145
                else:
230.1.6 by Gary van der Merwe
Split the graph loading and sorting and the line computing into 2 methods.
146
                    self.graph_parents[revid] = parent_revids
230.1.5 by Gary van der Merwe
Move linegraph.py code to logmodel.py.
147
                for parent in parent_revids:
230.1.6 by Gary van der Merwe
Split the graph loading and sorting and the line computing into 2 methods.
148
                    self.graph_children.setdefault(parent, []).append(revid)
149
                self.graph_children.setdefault(revid, [])
230.1.42 by Gary van der Merwe
qlog: Intelligent background revision loading.
150
                if len(self.graph_parents) % 100 == 0 :
555 by Gary van der Merwe
qlog: Stop loading methods stright away if we press close by raising an execption.
151
                    self.processEvents()
230.1.5 by Gary van der Merwe
Move linegraph.py code to logmodel.py.
152
            for ghost in ghosts:
230.1.6 by Gary van der Merwe
Split the graph loading and sorting and the line computing into 2 methods.
153
                for ghost_child in self.graph_children[ghost]:
154
                    self.graph_parents[ghost_child] = [p for p in self.graph_parents[ghost_child]
230.1.5 by Gary van der Merwe
Move linegraph.py code to logmodel.py.
155
                                                  if p not in ghosts]
516.1.3 by Gary van der Merwe
qlog: better way to work out for a revision, what branch it can be found in.
156
            self.graph_parents["top:"] = self.start_revs
230.1.5 by Gary van der Merwe
Move linegraph.py code to logmodel.py.
157
        
230.1.6 by Gary van der Merwe
Split the graph loading and sorting and the line computing into 2 methods.
158
            if len(self.graph_parents)>0:
159
                self.merge_sorted_revisions = merge_sort(
160
                    self.graph_parents,
230.1.5 by Gary van der Merwe
Move linegraph.py code to logmodel.py.
161
                    "top:",
162
                    generate_revno=True)
163
            else:
230.1.6 by Gary van der Merwe
Split the graph loading and sorting and the line computing into 2 methods.
164
                self.merge_sorted_revisions = ()
165
            
166
            assert self.merge_sorted_revisions[0][1] == "top:"
167
            self.merge_sorted_revisions = self.merge_sorted_revisions[1:]
230.1.5 by Gary van der Merwe
Move linegraph.py code to logmodel.py.
168
            
516.1.3 by Gary van der Merwe
qlog: better way to work out for a revision, what branch it can be found in.
169
            self.revid_head = {}
170
            for i in xrange(1, len(self.start_revs)):
171
                head_revid = self.start_revs[i]
172
                for ancestor_revid in graph.find_unique_ancestors(head_revid, self.start_revs[:i-1]):
173
                    self.revid_head[ancestor_revid] = head_revid
174
            
349.1.28 by Gary van der Merwe
qlog: Comments
175
            # This will hold, for each "branch":
176
            # [a list of revision indexes in the branch,
177
            #  is the branch visible,
349.1.29 by Gary van der Merwe
qlog: Fix bug where some branches are not colapased when the branch that they are merged by is colapased.
178
            #  merges,
179
            #  merged_by].
230.1.5 by Gary van der Merwe
Move linegraph.py code to logmodel.py.
180
            #
181
            # For a revisions, the revsion number less the least significant
182
            # digit is the branch_id, and used as the key for the dict. Hence
183
            # revision with the same revsion number less the least significant
184
            # digit are considered to be in the same branch line. e.g.: for
185
            # revisions 290.12.1 and 290.12.2, the branch_id would be 290.12,
186
            # and these two revisions will be in the same branch line. 
230.1.6 by Gary van der Merwe
Split the graph loading and sorting and the line computing into 2 methods.
187
            self.branch_lines = {}
230.1.11 by Gary van der Merwe
Calculate the parent color if it is hidden.
188
            self.revid_msri = {}
230.1.14 by Gary van der Merwe
qlog: Refactor linegraphdata structure to remove dup data.
189
            self.revno_msri = {}
349.1.33 by Gary van der Merwe
qlog: Make it possible to specify multiple branches, or multiple file. Make pending merges are show.
190
            self.start_branch_ids = []
230.1.6 by Gary van der Merwe
Split the graph loading and sorting and the line computing into 2 methods.
191
            
192
            for (rev_index, (sequence_number,
193
                             revid,
194
                             merge_depth,
195
                             revno_sequence,
196
                             end_of_merge)) in enumerate(self.merge_sorted_revisions):
197
                branch_id = revno_sequence[0:-1]
198
                
230.1.11 by Gary van der Merwe
Calculate the parent color if it is hidden.
199
                self.revid_msri[revid] = rev_index
230.1.14 by Gary van der Merwe
qlog: Refactor linegraphdata structure to remove dup data.
200
                self.revno_msri[revno_sequence] = rev_index
230.1.38 by Gary van der Merwe
qlog: Fix specific_fileid.
201
                
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
202
                branch_line = None
203
                if branch_id not in self.branch_lines:
516.1.3 by Gary van der Merwe
qlog: better way to work out for a revision, what branch it can be found in.
204
                    start_branch = revid in self.start_revs
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
205
                    branch_line = [[],
349.1.33 by Gary van der Merwe
qlog: Make it possible to specify multiple branches, or multiple file. Make pending merges are show.
206
                                   start_branch,
349.1.29 by Gary van der Merwe
qlog: Fix bug where some branches are not colapased when the branch that they are merged by is colapased.
207
                                   [],
208
                                   []]
349.1.33 by Gary van der Merwe
qlog: Make it possible to specify multiple branches, or multiple file. Make pending merges are show.
209
                    if start_branch:
210
                        self.start_branch_ids.append(branch_id)
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
211
                    self.branch_lines[branch_id] = branch_line
212
                else:
213
                    branch_line = self.branch_lines[branch_id]
214
                
215
                branch_line[0].append(rev_index)
230.1.29 by Gary van der Merwe
qlog: Colapse parent branchs that are widdowed.
216
            
230.1.6 by Gary van der Merwe
Split the graph loading and sorting and the line computing into 2 methods.
217
            self.branch_ids = self.branch_lines.keys()
349.1.16 by Gary van der Merwe
qlog: Avoid overlaping lines if the merge depth of the parent is greater by allocating the line from bottom to top, and by changing the branch sort order.
218
            
230.1.6 by Gary van der Merwe
Split the graph loading and sorting and the line computing into 2 methods.
219
            def branch_id_cmp(x, y):
349.1.33 by Gary van der Merwe
qlog: Make it possible to specify multiple branches, or multiple file. Make pending merges are show.
220
                is_start_x = x in self.start_branch_ids
221
                is_start_y = y in self.start_branch_ids
222
                if not is_start_x == is_start_y:
223
                    return - cmp(is_start_x, is_start_y)
349.1.16 by Gary van der Merwe
qlog: Avoid overlaping lines if the merge depth of the parent is greater by allocating the line from bottom to top, and by changing the branch sort order.
224
                merge_depth_x = self.merge_sorted_revisions[self.branch_lines[x][0][0]][2]
225
                merge_depth_y = self.merge_sorted_revisions[self.branch_lines[y][0][0]][2]
226
                if not merge_depth_x == merge_depth_y:
227
                    return cmp(merge_depth_x, merge_depth_y)
349.1.27 by Gary van der Merwe
qlog: Revet back to processing he branchs in reverse. It results in a more stable layout, and in case, and more understandable layout.
228
                return -cmp(x, y)
230.1.6 by Gary van der Merwe
Split the graph loading and sorting and the line computing into 2 methods.
229
            
230
            self.branch_ids.sort(branch_id_cmp)
349.1.2 by Gary van der Merwe
qlog: Continining refactor.
231
            
515 by Gary van der Merwe
qlog: Simplfy the code to work out merged_by and merges.
232
            # Work out for each revision, which revisions it merges, and what
233
            # revision it is merged by.
234
            self.merge_info = []
235
            current_merge_stack = [None]
349.1.5 by Gary van der Merwe
qlog: Work out for each revision, which revisions it merges when we load the branch, and store it in mem.
236
            for (msri, (sequence_number,
237
                        revid,
238
                        merge_depth,
239
                        revno_sequence,
240
                        end_of_merge)) in enumerate(self.merge_sorted_revisions):
515 by Gary van der Merwe
qlog: Simplfy the code to work out merged_by and merges.
241
                
242
                if merge_depth == len(current_merge_stack):
243
                    current_merge_stack.append(msri)
244
                else:
245
                    del current_merge_stack[merge_depth + 1:]
246
                    current_merge_stack[-1] = msri
247
                
248
                merged_by = None
249
                if merge_depth>0:
250
                    merged_by = current_merge_stack[-2]
520 by Gary van der Merwe
qlog: Fix bug in merges for revision with index 0.
251
                    if merged_by is not None:
516.1.1 by Gary van der Merwe
qlog: Make it possible to load log for branches in different repositories.
252
                        self.merge_info[merged_by][0].append(msri)
518 by Gary van der Merwe
qlog: Restore missing code to work out which branches are merged by which branches.
253
                        branch_id = revno_sequence[0:-1]
254
                        merged_by_branch_id = self.merge_sorted_revisions[merged_by][3][0:-1]
255
                        
256
                        if not branch_id in self.branch_lines[merged_by_branch_id][3]: 
257
                            self.branch_lines[merged_by_branch_id][2].append(branch_id) 
258
                        if not merged_by_branch_id in self.branch_lines[branch_id][2]: 
259
                            self.branch_lines[branch_id][3].append(merged_by_branch_id) 
260
                        
515 by Gary van der Merwe
qlog: Simplfy the code to work out merged_by and merges.
261
                self.merge_info.append(([],merged_by))
349.1.5 by Gary van der Merwe
qlog: Work out for each revision, which revisions it merges when we load the branch, and store it in mem.
262
            
403 by Gary van der Merwe
qlog: Don't show all revisions before filtering on specific_fileids.
263
            if specific_fileids:
403.1.3 by Gary van der Merwe
qlog: Make touches_file_msri a dict for improved speed.
264
                self.touches_file_msri = {}
403 by Gary van der Merwe
qlog: Don't show all revisions before filtering on specific_fileids.
265
            
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
266
            self.emit(QtCore.SIGNAL("layoutChanged()"))
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
267
            
349.1.33 by Gary van der Merwe
qlog: Make it possible to specify multiple branches, or multiple file. Make pending merges are show.
268
            if specific_fileids:
283 by Gary van der Merwe
qlog: Make qlog FILE compatable with bzr < 3509
269
                try:
516.1.1 by Gary van der Merwe
qlog: Make it possible to load log for branches in different repositories.
270
                    self.branches[0].repository.texts.get_parent_map([])
283 by Gary van der Merwe
qlog: Make qlog FILE compatable with bzr < 3509
271
                    use_texts = True
272
                except AttributeError:
273
                    use_texts = False
516 by Gary van der Merwe
qlog: Make qlog FILE the same as log FILE which changed in bzr 1.8
274
                
275
                if use_texts:
276
                    chunk_size = 500
277
                    for start in xrange(0, len(self.merge_sorted_revisions), chunk_size):
278
                        text_keys = [(specific_fileid, revid) \
279
                            for sequence_number,
280
                                revid,
281
                                merge_depth,
282
                                revno_sequence,
283
                                end_of_merge in self.merge_sorted_revisions[start:start + chunk_size] \
284
                            for specific_fileid in specific_fileids]
285
                        
516.1.1 by Gary van der Merwe
qlog: Make it possible to load log for branches in different repositories.
286
                        for fileid, revid in self.branches[0].repository.texts.get_parent_map(text_keys):
516 by Gary van der Merwe
qlog: Make qlog FILE the same as log FILE which changed in bzr 1.8
287
                            rev_msri = self.revid_msri[revid]
288
                            self.touches_file_msri[rev_msri] = True
289
                            
290
                            self.graphFilterProxyModel.invalidateCacheRow(rev_msri)
291
                            index = self.createIndex (rev_msri, 0, QtCore.QModelIndex())
292
                            self.emit(QtCore.SIGNAL("dataChanged(QModelIndex, QModelIndex)"),
293
                                      index,index)
294
                        
555 by Gary van der Merwe
qlog: Stop loading methods stright away if we press close by raising an execption.
295
                        self.processEvents()
516 by Gary van der Merwe
qlog: Make qlog FILE the same as log FILE which changed in bzr 1.8
296
                    
297
                else:
349.1.33 by Gary van der Merwe
qlog: Make it possible to specify multiple branches, or multiple file. Make pending merges are show.
298
                    weave_modifed_revisions = set()
299
                    for specific_fileid in specific_fileids:
516.1.1 by Gary van der Merwe
qlog: Make it possible to load log for branches in different repositories.
300
                        file_weave = self.branches[0].repository.weave_store.get_weave(specific_fileid,
301
                                            self.branches[0].repository.get_transaction())
516 by Gary van der Merwe
qlog: Make qlog FILE the same as log FILE which changed in bzr 1.8
302
                        for revid in file_weave.versions():
303
                            rev_msri = self.revid_msri[revid]
304
                            self.touches_file_msri[rev_msri] = True
305
                            self.graphFilterProxyModel.invalidateCacheRow(rev_msri)
284 by Gary van der Merwe
qlog; Enable graph when running qlog FILE. Indicate that there are hidden revision between parent with dotted line.
306
            
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
307
            self.compute_lines()
230.1.6 by Gary van der Merwe
Split the graph loading and sorting and the line computing into 2 methods.
308
        finally:
516.1.1 by Gary van der Merwe
qlog: Make it possible to load log for branches in different repositories.
309
            for branch in self.branches:
310
                branch.unlock
230.1.6 by Gary van der Merwe
Split the graph loading and sorting and the line computing into 2 methods.
311
        
349.1.3 by Gary van der Merwe
qlog: Futher refactoring including removeing broken_lines, better way to find parent for finding a free column, and a better solution for sprouts (show none direct line to visible children).
312
    def compute_lines(self):
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
313
        
314
        # This will hold for each revision, a list of (msri,
315
        #                                              node,
316
        #                                              lines,
317
        #                                              twisty_state,
318
        #                                              twisty_branch_ids).
319
        #
320
        # Node is a tuple of (column, color) with column being a
321
        # zero-indexed column number of the graph that this revision
322
        # represents and color being a zero-indexed color (which doesn't
323
        # specify any actual color in particular) to draw the node in.
324
        #
325
        # Lines is a list of tuples which represent lines you should draw
326
        # away from the revision, if you also need to draw lines into the
327
        # revision you should use the lines list from the previous
328
        # iteration. Each tuples in the list is in the form (start, end,
329
        # color, direct) with start and end being zero-indexed column
330
        # numbers and color as in node.
331
        #
332
        # twisties are +- buttons to show/hide branches. list branch_ids
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
333
        linegraphdata = []
334
        msri_index = {}
335
        
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
336
        source_parent = QtCore.QModelIndex()
337
        for msri in xrange(0,len(self.merge_sorted_revisions)):
338
            if self.graphFilterProxyModel.filterAcceptsRow(msri, source_parent):
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
339
                index = len(linegraphdata)
340
                msri_index[msri] = index
341
                linegraphdata.append([msri,
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
342
                                           None,
343
                                           [],
344
                                           None,
345
                                           [],
346
                                          ])
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
347
        
284 by Gary van der Merwe
qlog; Enable graph when running qlog FILE. Indicate that there are hidden revision between parent with dotted line.
348
        # This will hold a tuple of (child_index, parent_index, col_index,
349
        # direct) for each line that needs to be drawn. If col_index is not
350
        # none, then the line is drawn along that column, else the the line can
351
        # be drawn directly between the child and parent because either the
352
        # child and parent are in the same branch line, or the child and parent
353
        # are 1 row apart.
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
354
        lines = []
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
355
        empty_column = [False for i in range(len(linegraphdata))]
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
356
        # This will hold a bit map for each cell. If the cell is true, then
357
        # the cell allready contains a node or line. This use when deciding
358
        # what column to place a branch line or line in, without it
359
        # overlaping something else.
360
        columns = [list(empty_column)]
361
        
349.1.15 by Gary van der Merwe
qlog: Refactor - move these methods to where thay are used.
362
        def _branch_line_col_search_order(parent_col_index):
363
            for col_index in range(parent_col_index, len(columns)):
364
                yield col_index
349.1.16 by Gary van der Merwe
qlog: Avoid overlaping lines if the merge depth of the parent is greater by allocating the line from bottom to top, and by changing the branch sort order.
365
            #for col_index in range(parent_col_index-1, -1, -1):
366
            #    yield col_index
349.1.15 by Gary van der Merwe
qlog: Refactor - move these methods to where thay are used.
367
        
368
        def _line_col_search_order(parent_col_index, child_col_index):
369
            if parent_col_index is not None and child_col_index is not None:
370
                max_index = max(parent_col_index, child_col_index)
371
                min_index = min(parent_col_index, child_col_index)
349.1.31 by Gary van der Merwe
qlog: Comments
372
                # First yield the columns between the child and parent.
349.1.15 by Gary van der Merwe
qlog: Refactor - move these methods to where thay are used.
373
                for col_index in range(max_index, min_index -1, -1):
374
                    yield col_index
375
            elif child_col_index is not None:
376
                max_index = child_col_index
377
                min_index = child_col_index
378
                yield child_col_index
379
            elif parent_col_index is not None:
380
                max_index = parent_col_index
381
                min_index = parent_col_index
382
                yield parent_col_index
349.1.17 by Gary van der Merwe
qlog: More graph tweeking
383
            else:
384
                max_index = 0
385
                min_index = 0
386
                yield 0
349.1.15 by Gary van der Merwe
qlog: Refactor - move these methods to where thay are used.
387
            i = 1
349.1.31 by Gary van der Merwe
qlog: Comments
388
            # then yield the columns on either side.
349.1.15 by Gary van der Merwe
qlog: Refactor - move these methods to where thay are used.
389
            while max_index + i < len(columns) or \
390
                  min_index - i > -1:
391
                if max_index + i < len(columns):
392
                    yield max_index + i
349.1.16 by Gary van der Merwe
qlog: Avoid overlaping lines if the merge depth of the parent is greater by allocating the line from bottom to top, and by changing the branch sort order.
393
                #if min_index - i > -1:
394
                #    yield min_index - i
349.1.15 by Gary van der Merwe
qlog: Refactor - move these methods to where thay are used.
395
                i += 1
396
        
397
        def _find_free_column(col_search_order, line_range):
398
            for col_index in col_search_order:
399
                column = columns[col_index]
400
                has_overlaping_line = False
401
                for row_index in line_range:
402
                    if column[row_index]:
403
                        has_overlaping_line = True
404
                        break
405
                if not has_overlaping_line:
406
                    break
407
            else:
349.1.31 by Gary van der Merwe
qlog: Comments
408
                # No free columns found. Add an empty one on the end.
349.1.15 by Gary van der Merwe
qlog: Refactor - move these methods to where thay are used.
409
                col_index = len(columns)
410
                column = list(empty_column)
411
                columns.append(column)
412
            return col_index
413
        
414
        def _mark_column_as_used(col_index, line_range):
415
            column = columns[col_index]
416
            for row_index in line_range:
417
                column[row_index] = True
418
        
349.1.14 by Gary van der Merwe
qlog: Re-add and improve code for sprouts.
419
        def append_line (child_index, parent_index, direct):
349.1.13 by Gary van der Merwe
qlog: Refactor graph code to abstract appending a line.
420
            parent_node = linegraphdata[parent_index][1]
421
            if parent_node:
422
                parent_col_index = parent_node[0]
423
            else:
424
                parent_col_index = None
425
            
426
            child_node = linegraphdata[child_index][1]
427
            if child_node:
428
                child_col_index = child_node[0]
429
            else:
430
                child_col_index = None
431
                
432
            line_col_index = child_col_index
433
            if parent_index - child_index >1:
434
                line_range = range(child_index + 1, parent_index)
435
                col_search_order = \
349.1.15 by Gary van der Merwe
qlog: Refactor - move these methods to where thay are used.
436
                        _line_col_search_order(parent_col_index,
349.1.13 by Gary van der Merwe
qlog: Refactor graph code to abstract appending a line.
437
                                               child_col_index)
438
                line_col_index = \
349.1.15 by Gary van der Merwe
qlog: Refactor - move these methods to where thay are used.
439
                    _find_free_column(col_search_order,
349.1.13 by Gary van der Merwe
qlog: Refactor graph code to abstract appending a line.
440
                                      line_range)
349.1.15 by Gary van der Merwe
qlog: Refactor - move these methods to where thay are used.
441
                _mark_column_as_used(line_col_index,
349.1.13 by Gary van der Merwe
qlog: Refactor graph code to abstract appending a line.
442
                                     line_range)
443
            lines.append((child_index,
444
                          parent_index,
445
                          line_col_index,
446
                          direct,
447
                          ))            
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
448
        
449
        for branch_id in self.branch_ids:
450
            (branch_rev_msri,
451
             branch_visible,
349.1.29 by Gary van der Merwe
qlog: Fix bug where some branches are not colapased when the branch that they are merged by is colapased.
452
             branch_merges,
453
             branch_merged_by) = self.branch_lines[branch_id]
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
454
            
455
            if branch_visible:
284 by Gary van der Merwe
qlog; Enable graph when running qlog FILE. Indicate that there are hidden revision between parent with dotted line.
456
                branch_rev_msri = [rev_msri for rev_msri in branch_rev_msri
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
457
                                   if rev_msri in msri_index]
349.1.3 by Gary van der Merwe
qlog: Futher refactoring including removeing broken_lines, better way to find parent for finding a free column, and a better solution for sprouts (show none direct line to visible children).
458
            else:
459
                branch_rev_msri = []
460
                
461
            if branch_rev_msri:
462
                color = reduce(lambda x, y: x+y, branch_id, 0)
230.1.6 by Gary van der Merwe
Split the graph loading and sorting and the line computing into 2 methods.
463
                
349.1.24 by Gary van der Merwe
qlog: Impove comments.
464
                # In this loop:
465
                # * Find visible parents.
466
                # * Populate twisty_branch_ids and twisty_state
349.1.20 by Gary van der Merwe
qlog: Graph lines to a parent with a lower merge depth must only be drawn if their index is less than the last parent index to avoid overlaping lines
467
                branch_rev_visible_parents = {}
349.1.10 by Gary van der Merwe
qlog: Fix bug in graph: When the last rev in a branch is not visible, make the line not zig zap.
468
                
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
469
                for rev_msri in branch_rev_msri:
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
470
                    rev_index = msri_index[rev_msri]
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
471
                    (sequence_number,
472
                         revid,
473
                         merge_depth,
474
                         revno_sequence,
475
                         end_of_merge) = self.merge_sorted_revisions[rev_msri]
476
                    
349.1.11 by Gary van der Merwe
qlog: Improvements to graph.
477
                    # Find parents that are currently visible
478
                    rev_visible_parents = []
435 by Gary van der Merwe
qlog: Revert rev 419.1.2 and 403.1.6. - Go back to searching for all non-direct parents.
479
                    for parent_revid in self.graph_parents[revid]:
349.1.11 by Gary van der Merwe
qlog: Improvements to graph.
480
                        (parent_msri,
481
                         parent_branch_id,
482
                         parent_merge_depth) = self._msri_branch_id_merge_depth(parent_revid)
483
                        if parent_msri in msri_index:
484
                            rev_visible_parents.append((parent_revid,
485
                                                        parent_msri,
486
                                                        parent_branch_id,
487
                                                        parent_merge_depth,
488
                                                        True))
435 by Gary van der Merwe
qlog: Revert rev 419.1.2 and 403.1.6. - Go back to searching for all non-direct parents.
489
                        else:
349.1.31 by Gary van der Merwe
qlog: Comments
490
                            # The parent was not visible. Search for a ansestor
435 by Gary van der Merwe
qlog: Revert rev 419.1.2 and 403.1.6. - Go back to searching for all non-direct parents.
491
                            # that is. Stop searching if we make a hop, i.e. we
492
                            # go away for our branch, and we come back to it
493
                            has_seen_different_branch = False
494
                            if not parent_branch_id == branch_id:
495
                                has_seen_different_branch = True
349.1.11 by Gary van der Merwe
qlog: Improvements to graph.
496
                            while parent_revid and parent_msri not in msri_index:
497
                                parents = self.graph_parents[parent_revid]
498
                                if len(parents) == 0:
499
                                    parent_revid = None
500
                                else:
501
                                    parent_revid = parents[0]
502
                                    (parent_msri,
503
                                     parent_branch_id,
504
                                     parent_merge_depth) = self._msri_branch_id_merge_depth(parent_revid)
435 by Gary van der Merwe
qlog: Revert rev 419.1.2 and 403.1.6. - Go back to searching for all non-direct parents.
505
                                if not parent_branch_id == branch_id:
506
                                    has_seen_different_branch = True
507
                                if has_seen_different_branch and parent_branch_id == branch_id:
508
                                    parent_revid = None
509
                                    break
349.1.11 by Gary van der Merwe
qlog: Improvements to graph.
510
                            if parent_revid:
511
                                rev_visible_parents.append((parent_revid,
512
                                                            parent_msri,
513
                                                            parent_branch_id,
514
                                                            parent_merge_depth,
515
                                                            False))
349.1.20 by Gary van der Merwe
qlog: Graph lines to a parent with a lower merge depth must only be drawn if their index is less than the last parent index to avoid overlaping lines
516
                    branch_rev_visible_parents[rev_msri]=rev_visible_parents
230.1.9 by Gary van der Merwe
Make the code able to hide a branch.
517
                    
349.1.13 by Gary van der Merwe
qlog: Refactor graph code to abstract appending a line.
518
                    # Find and add nessery twisties
515 by Gary van der Merwe
qlog: Simplfy the code to work out merged_by and merges.
519
                    for parent_msri in self.merge_info[rev_msri][0]:
349.1.30 by Gary van der Merwe
qlog: A branch can't merge the branch that merges it. Work out what twisties for the merges data.
520
                        parent_branch_id = self.merge_sorted_revisions[parent_msri][3][0:-1]
521
                        parent_merge_depth = self.merge_sorted_revisions[parent_msri][2]
522
                        
523
                        # Does this branch have any visible revisions
524
                        parent_branch_rev_msri = self.branch_lines[parent_branch_id][0]
525
                        for pb_rev_msri in parent_branch_rev_msri:
526
                            visible = pb_rev_msri in msri_index or\
527
                                      self.graphFilterProxyModel.filterAcceptsRowIfBranchVisible(pb_rev_msri, source_parent)
528
                            if visible:
529
                                linegraphdata[rev_index][4].append (parent_branch_id)
530
                                break
349.1.13 by Gary van der Merwe
qlog: Refactor graph code to abstract appending a line.
531
                    
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
532
                    # Work out if the twisty needs to show a + or -. If all
533
                    # twisty_branch_ids are visible, show - else +.
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
534
                    if len (linegraphdata[rev_index][4])>0:
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
535
                        twisty_state = True
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
536
                        for twisty_branch_id in linegraphdata[rev_index][4]:
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
537
                            if not self.branch_lines[twisty_branch_id][1]:
538
                                twisty_state = False
539
                                break
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
540
                        linegraphdata[rev_index][3] = twisty_state
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
541
                
349.1.20 by Gary van der Merwe
qlog: Graph lines to a parent with a lower merge depth must only be drawn if their index is less than the last parent index to avoid overlaping lines
542
                last_parent_msri = None
543
                if branch_rev_visible_parents[branch_rev_msri[-1]]: 
544
                    last_parent_msri = branch_rev_visible_parents[branch_rev_msri[-1]][0][1]
545
                
349.1.25 by Gary van der Merwe
qlog: Avoid haveing lots of sprout lines.
546
                children_with_sprout_lines = {}
349.1.24 by Gary van der Merwe
qlog: Impove comments.
547
                # In this loop:
548
                # * Append lines that need to go to parents before the branch
349.1.31 by Gary van der Merwe
qlog: Comments
549
                #   (say inbetween the main line and the branch). Remove the
550
                #   ones we append from rev_visible_parents so they don't get
551
                #   added again later on.
349.1.25 by Gary van der Merwe
qlog: Avoid haveing lots of sprout lines.
552
                # * Append lines to chilren for sprouts.
349.1.20 by Gary van der Merwe
qlog: Graph lines to a parent with a lower merge depth must only be drawn if their index is less than the last parent index to avoid overlaping lines
553
                for rev_msri in branch_rev_msri:
554
                    rev_index = msri_index[rev_msri]
555
                    (sequence_number,
556
                         revid,
557
                         merge_depth,
558
                         revno_sequence,
559
                         end_of_merge) = self.merge_sorted_revisions[rev_msri]
349.1.25 by Gary van der Merwe
qlog: Avoid haveing lots of sprout lines.
560
                    
349.1.20 by Gary van der Merwe
qlog: Graph lines to a parent with a lower merge depth must only be drawn if their index is less than the last parent index to avoid overlaping lines
561
                    rev_visible_parents = branch_rev_visible_parents[rev_msri]
562
                    i = 0
563
                    while i < len(rev_visible_parents):
564
                        (parent_revid,
565
                         parent_msri,
566
                         parent_branch_id,
567
                         parent_merge_depth,
568
                         direct) = rev_visible_parents[i]
569
                        
570
                        parent_index = msri_index[parent_msri]
349.1.26 by Gary van der Merwe
qlog: On the last revision of a branch, the lines for parents other then [0] can go before the branch.
571
                        if (rev_msri <> branch_rev_msri[-1] or i > 0 )and \
349.1.20 by Gary van der Merwe
qlog: Graph lines to a parent with a lower merge depth must only be drawn if their index is less than the last parent index to avoid overlaping lines
572
                           parent_branch_id <> branch_id and\
573
                           branch_id <> () and \
574
                           parent_merge_depth <= merge_depth and\
349.1.27 by Gary van der Merwe
qlog: Revet back to processing he branchs in reverse. It results in a more stable layout, and in case, and more understandable layout.
575
                           (last_parent_msri and not direct and last_parent_msri >= parent_msri or not last_parent_msri or direct):
349.1.20 by Gary van der Merwe
qlog: Graph lines to a parent with a lower merge depth must only be drawn if their index is less than the last parent index to avoid overlaping lines
576
                            
577
                            if parent_index - rev_index >1:
578
                                rev_visible_parents.pop(i)
579
                                i -= 1
580
                                append_line(rev_index, parent_index, direct)
581
                        i += 1
349.1.25 by Gary van der Merwe
qlog: Avoid haveing lots of sprout lines.
582
                    
583
                    # This may be a sprout. Add line to first visible child
515 by Gary van der Merwe
qlog: Simplfy the code to work out merged_by and merges.
584
                    merged_by_msri = self.merge_info[rev_msri][1]
585
                    if merged_by_msri and\
586
                       not merged_by_msri in msri_index and\
587
                       rev_msri == self.merge_info[merged_by_msri][0][0]:
588
                        # The revision that merges this revision is not
589
                        # visible, and it is the first revision that is
590
                        # merged by that revision. This is a sprout.
591
                        #
592
                        # XXX What if multiple merges with --force,
593
                        # aka ocutpus merge?
594
                        #
595
                        # Search until we find a decendent that is visible.
596
                        child_msri = self.merge_info[rev_msri][1]
597
                        while not child_msri is None and \
598
                              not child_msri in msri_index:
599
                            child_msri = self.merge_info[child_msri][1]
600
                        # Ensure only one line to a decendent.
601
                        if child_msri not in children_with_sprout_lines:
602
                            children_with_sprout_lines[child_msri] = True
603
                            if child_msri in msri_index:
604
                                child_index = msri_index[child_msri]
605
                                append_line(child_index, rev_index, False)
349.1.20 by Gary van der Merwe
qlog: Graph lines to a parent with a lower merge depth must only be drawn if their index is less than the last parent index to avoid overlaping lines
606
                
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
607
                # Find a column for this branch.
608
                #
609
                # Find the col_index for the direct parent branch. This will
610
                # be the starting point when looking for a free column.
349.1.3 by Gary van der Merwe
qlog: Futher refactoring including removeing broken_lines, better way to find parent for finding a free column, and a better solution for sprouts (show none direct line to visible children).
611
                
403.1.4 by Gary van der Merwe
qlog: Only allow the mainline to occupy the first column.
612
                if branch_id == ():
613
                    parent_col_index = 0
614
                else:
615
                    parent_col_index = 1
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
616
                parent_index = None
349.1.3 by Gary van der Merwe
qlog: Futher refactoring including removeing broken_lines, better way to find parent for finding a free column, and a better solution for sprouts (show none direct line to visible children).
617
                
349.1.20 by Gary van der Merwe
qlog: Graph lines to a parent with a lower merge depth must only be drawn if their index is less than the last parent index to avoid overlaping lines
618
                if last_parent_msri:
619
                    parent_index = msri_index[last_parent_msri]
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
620
                    parent_node = linegraphdata[parent_index][1]
349.1.3 by Gary van der Merwe
qlog: Futher refactoring including removeing broken_lines, better way to find parent for finding a free column, and a better solution for sprouts (show none direct line to visible children).
621
                    if parent_node:
622
                        parent_col_index = parent_node[0]
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
623
                
349.1.20 by Gary van der Merwe
qlog: Graph lines to a parent with a lower merge depth must only be drawn if their index is less than the last parent index to avoid overlaping lines
624
                col_search_order = _branch_line_col_search_order(parent_col_index) 
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
625
                cur_cont_line = []
626
                
627
                # Work out what rows this branch spans
628
                line_range = []
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
629
                first_rev_index = msri_index[branch_rev_msri[0]]
630
                last_rev_index = msri_index[branch_rev_msri[-1]]
349.1.3 by Gary van der Merwe
qlog: Futher refactoring including removeing broken_lines, better way to find parent for finding a free column, and a better solution for sprouts (show none direct line to visible children).
631
                line_range = range(first_rev_index, last_rev_index+1)
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
632
                
633
                if parent_index:
349.1.3 by Gary van der Merwe
qlog: Futher refactoring including removeing broken_lines, better way to find parent for finding a free column, and a better solution for sprouts (show none direct line to visible children).
634
                    line_range.extend(range(last_rev_index+1, parent_index))
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
635
                
349.1.15 by Gary van der Merwe
qlog: Refactor - move these methods to where thay are used.
636
                col_index = _find_free_column(col_search_order,
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
637
                                              line_range)
638
                node = (col_index, color)
639
                # Free column for this branch found. Set node for all
640
                # revision in this branch.
641
                for rev_msri in branch_rev_msri:
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
642
                    rev_index = msri_index[rev_msri]
643
                    linegraphdata[rev_index][1] = node
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
644
                    columns[col_index][rev_index] = True
645
                
349.1.24 by Gary van der Merwe
qlog: Impove comments.
646
                # In this loop:
647
                # * Append the remaining lines to parents.
349.1.21 by Gary van der Merwe
qlog: Go back to using reversed order for remaining lines.
648
                for rev_msri in reversed(branch_rev_msri):
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
649
                    rev_index = msri_index[rev_msri]
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
650
                    (sequence_number,
651
                         revid,
652
                         merge_depth,
653
                         revno_sequence,
654
                         end_of_merge) = self.merge_sorted_revisions[rev_msri]
349.1.11 by Gary van der Merwe
qlog: Improvements to graph.
655
                    for (parent_revid,
656
                         parent_msri,
657
                         parent_branch_id,
658
                         parent_merge_depth,
349.1.20 by Gary van der Merwe
qlog: Graph lines to a parent with a lower merge depth must only be drawn if their index is less than the last parent index to avoid overlaping lines
659
                         direct) in branch_rev_visible_parents[rev_msri]:
349.1.11 by Gary van der Merwe
qlog: Improvements to graph.
660
                        
349.1.13 by Gary van der Merwe
qlog: Refactor graph code to abstract appending a line.
661
                        parent_index = msri_index[parent_msri]
349.1.14 by Gary van der Merwe
qlog: Re-add and improve code for sprouts.
662
                        append_line(rev_index, parent_index, direct)
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
663
        
664
        # It has now been calculated which column a line must go into. Now
665
        # copy the lines in to linegraphdata.
666
        for (child_index,
667
             parent_index,
349.1.12 by Gary van der Merwe
qlog: Remove more unnessery broken lines code.
668
             line_col_index,
284 by Gary van der Merwe
qlog; Enable graph when running qlog FILE. Indicate that there are hidden revision between parent with dotted line.
669
             direct,
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
670
             ) in lines:
671
            
349.1.12 by Gary van der Merwe
qlog: Remove more unnessery broken lines code.
672
            (child_col_index, child_color) = linegraphdata[child_index][1]
673
            (parent_col_index, parent_color) = linegraphdata[parent_index][1]
674
            
675
            if parent_index - child_index == 1:
676
                linegraphdata[child_index][2].append(
677
                    (child_col_index,
678
                     parent_col_index,
679
                     parent_color,
680
                     direct))
681
            else:
682
                # line from the child's column to the lines column
683
                linegraphdata[child_index][2].append(
684
                    (child_col_index,
685
                     line_col_index,
686
                     parent_color,
687
                     direct))
688
                # lines down the line's column
689
                for line_part_index in range(child_index+1, parent_index-1):
690
                    linegraphdata[line_part_index][2].append(
691
                        (line_col_index,   
692
                         line_col_index,
693
                         parent_color,
694
                         direct))
695
                # line from the line's column to the parent's column
696
                linegraphdata[parent_index-1][2].append(
697
                    (line_col_index,
698
                     parent_col_index,
699
                     parent_color,
700
                     direct))
349.1.3 by Gary van der Merwe
qlog: Futher refactoring including removeing broken_lines, better way to find parent for finding a free column, and a better solution for sprouts (show none direct line to visible children).
701
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
702
        self.linegraphdata = linegraphdata
703
        self.msri_index = msri_index
230.1.41 by Gary van der Merwe
qlog: Display revisions before calculating graph.
704
        self.emit(QtCore.SIGNAL("dataChanged(QModelIndex, QModelIndex)"),
705
                  self.createIndex (0, COL_MESSAGE, QtCore.QModelIndex()),
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
706
                  self.createIndex (len(self.merge_sorted_revisions), COL_MESSAGE, QtCore.QModelIndex()))
230.1.24 by Gary van der Merwe
qlog: Get search working again.
707
    
349.1.11 by Gary van der Merwe
qlog: Improvements to graph.
708
    def _msri_branch_id_merge_depth (self, revid):
709
        msri = self.revid_msri[revid]
710
        branch_id = self.merge_sorted_revisions[msri][3][0:-1]
711
        merge_depth = self.merge_sorted_revisions[msri][2]
712
        return (msri, branch_id, merge_depth)
284 by Gary van der Merwe
qlog; Enable graph when running qlog FILE. Indicate that there are hidden revision between parent with dotted line.
713
    
230.1.31 by Gary van der Merwe
qlog: When a revison link is clicked, make sure it is visible.
714
    def _set_branch_visible(self, branch_id, visible, has_change):
715
        if not self.branch_lines[branch_id][1] == visible:
716
            has_change = True
717
        self.branch_lines[branch_id][1] = visible
718
        return has_change
719
    
720
    def _has_visible_child(self, branch_id):
721
        for child_branch_id in self.branch_lines[branch_id][3]:
230.1.35 by Gary van der Merwe
qlog: Fix bug in code colapseing child branches.
722
            if self.branch_lines[child_branch_id][1]:
230.1.31 by Gary van der Merwe
qlog: When a revison link is clicked, make sure it is visible.
723
                return True
724
        return False
725
    
349.1.2 by Gary van der Merwe
qlog: Continining refactor.
726
    def colapse_expand_rev(self, revid, visible):
727
        msri = self.revid_msri[revid]
728
        if msri not in self.msri_index: return
729
        index = self.msri_index[msri]
730
        twisty_branch_ids = self.linegraphdata[index][4]
230.1.28 by Gary van der Merwe
qlog: Only colapse_expand_rev if clicking on the node, and only if somthing is changing.
731
        has_change = False
230.1.27 by Gary van der Merwe
qlog: Simplfy twisties
732
        for branch_id in twisty_branch_ids:
230.1.34 by Gary van der Merwe
qlog: Add keyboard navigation for graph.
733
            has_change = self._set_branch_visible(branch_id, visible, has_change)
734
            if not visible:
230.1.29 by Gary van der Merwe
qlog: Colapse parent branchs that are widdowed.
735
                for parent_branch_id in self.branch_lines[branch_id][2]:
349.1.33 by Gary van der Merwe
qlog: Make it possible to specify multiple branches, or multiple file. Make pending merges are show.
736
                    if not parent_branch_id in self.start_branch_ids and not self._has_visible_child(parent_branch_id):
230.1.34 by Gary van der Merwe
qlog: Add keyboard navigation for graph.
737
                        has_change = self._set_branch_visible(parent_branch_id, visible, has_change)
230.1.31 by Gary van der Merwe
qlog: When a revison link is clicked, make sure it is visible.
738
        if has_change:
739
            self.compute_lines()
740
    
332 by Gary van der Merwe
qlog: Fix bug in searching for revid.
741
    def has_rev_id(self, revid):
287.1.1 by Gary van der Merwe
qlog: When searching for revision id or revison no, just select it.
742
        return revid in self.revid_msri
743
    
744
    def revid_from_revno(self, revno):
745
        if revno not in self.revno_msri:
746
            return None
747
        msri = self.revno_msri[revno]
748
        return self.merge_sorted_revisions[msri][1]
749
        
230.1.31 by Gary van der Merwe
qlog: When a revison link is clicked, make sure it is visible.
750
    def ensure_rev_visible(self, revid):
751
        if self.searchMode:
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
752
            return # Why??
230.1.31 by Gary van der Merwe
qlog: When a revison link is clicked, make sure it is visible.
753
        rev_msri = self.revid_msri[revid]
754
        branch_id = self.merge_sorted_revisions[rev_msri][3][0:-1]
755
        has_change = self._set_branch_visible(branch_id, True, False)
349.1.33 by Gary van der Merwe
qlog: Make it possible to specify multiple branches, or multiple file. Make pending merges are show.
756
        while not branch_id in self.start_branch_ids and self.branch_lines[branch_id][3]:
349.1.29 by Gary van der Merwe
qlog: Fix bug where some branches are not colapased when the branch that they are merged by is colapased.
757
            branch_id = self.branch_lines[branch_id][3][0]
288 by Gary van der Merwe
qlog: In ensure_rev_visible, convert child_branch to list before trying to get index, as it is unindexable as a set. Also stop looping when we get to the mainline.
758
            has_change = self._set_branch_visible(branch_id, True, has_change)
230.1.28 by Gary van der Merwe
qlog: Only colapse_expand_rev if clicking on the node, and only if somthing is changing.
759
        if has_change:
760
            self.compute_lines()
230.1.27 by Gary van der Merwe
qlog: Simplfy twisties
761
    
230.1.24 by Gary van der Merwe
qlog: Get search working again.
762
    def set_search_mode(self, searchMode):
763
        if not searchMode == self.searchMode:
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
764
            self.searchMode = searchMode
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
765
            if self.searchMode:
766
                self.load_all_revisions.start()
767
            else:
768
                self.load_all_revisions.stop()
230.1.24 by Gary van der Merwe
qlog: Get search working again.
769
    
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
770
    def columnCount(self, parent):
771
        if parent.isValid():
772
            return 0
773
        return len(self.horizontalHeaderLabels)
774
775
    def rowCount(self, parent):
776
        if parent.isValid():
777
            return 0
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
778
        return len(self.merge_sorted_revisions)
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
779
    
780
    def data(self, index, role):
781
        if not index.isValid():
782
            return QtCore.QVariant()
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
783
        
784
        if index.row() in self.msri_index:
785
            (msri, node, lines, twisty_state, twisty_branch_ids) = self.linegraphdata[self.msri_index[index.row()]]
786
        else:
787
            (msri, node, lines, twisty_state, twisty_branch_ids) = (index.row(), None, [], None, [])
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
788
        
789
        if role == GraphNodeRole:
230.1.24 by Gary van der Merwe
qlog: Get search working again.
790
            if node is None:
791
                return QtCore.QVariant()
373 by Lukáš Lalinský
Fix qlog on PyQt 4.4.3
792
            return QVariant_fromList([QtCore.QVariant(nodei) for nodei in node])
230.1.12 by Gary van der Merwe
qLog: Expandable / Colapseable branchs
793
        if role == GraphLinesRole:
794
            qlines = []
284 by Gary van der Merwe
qlog; Enable graph when running qlog FILE. Indicate that there are hidden revision between parent with dotted line.
795
            for start, end, color, direct in lines:
373 by Lukáš Lalinský
Fix qlog on PyQt 4.4.3
796
                qlines.append(QVariant_fromList(
797
                    [QtCore.QVariant(start),
798
                     QtCore.QVariant(end),
799
                     QtCore.QVariant(color),
800
                     QtCore.QVariant(direct)]))
801
            return QVariant_fromList(qlines)
230.1.27 by Gary van der Merwe
qlog: Simplfy twisties
802
        if role == GraphTwistyStateRole:
803
            if twisty_state is None:
804
                return QtCore.QVariant()
805
            return QtCore.QVariant(twisty_state)
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
806
        
230.1.14 by Gary van der Merwe
qlog: Refactor linegraphdata structure to remove dup data.
807
        (sequence_number, revid, merge_depth, revno_sequence, end_of_merge) = \
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
808
            self.merge_sorted_revisions[index.row()]
230.1.14 by Gary van der Merwe
qlog: Refactor linegraphdata structure to remove dup data.
809
        
230.1.24 by Gary van der Merwe
qlog: Get search working again.
810
        if (role == QtCore.Qt.DisplayRole and index.column() == COL_REV) or \
811
                role == FilterRevnoRole:
230.1.14 by Gary van der Merwe
qlog: Refactor linegraphdata structure to remove dup data.
812
            revnos = ".".join(["%d" % (revno)
813
                                      for revno in revno_sequence])
814
            return QtCore.QVariant(revnos)
815
        
816
        if role == TagsRole:
817
            tags = []
818
            if revid in self.tags:
516.1.4 by Gary van der Merwe
qlog: Don't show tags more than once.
819
                tags = list(self.tags[revid])
375 by Lukáš Lalinský
More qlog fixes on PyQt 4.4.3
820
            return QtCore.QVariant(QtCore.QStringList(tags))
349.1.41 by Gary van der Merwe
qlog: show tags for branch tips.
821
        
822
        if role == BranchTagsRole:
823
            tags = []
516.1.3 by Gary van der Merwe
qlog: better way to work out for a revision, what branch it can be found in.
824
            if revid in self.heads:
516.1.10 by Gary van der Merwe
qlog: Allways append head info. Filter tags later.
825
                tags = [tag for (branch, tag, blr) in self.heads[revid][1] if tag]
349.1.41 by Gary van der Merwe
qlog: show tags for branch tips.
826
            return QtCore.QVariant(QtCore.QStringList(tags))
827
        
230.1.24 by Gary van der Merwe
qlog: Get search working again.
828
        if role == RevIdRole or role == FilterIdRole:
230.1.20 by Gary van der Merwe
qlog: Get revisions from logmodel.
829
            return QtCore.QVariant(revid)
230.1.14 by Gary van der Merwe
qlog: Refactor linegraphdata structure to remove dup data.
830
        
230.1.12 by Gary van der Merwe
qLog: Expandable / Colapseable branchs
831
        #Everything from here foward will need to have the revision loaded.
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
832
        if not revid or revid == NULL_REVISION:
833
            return QtCore.QVariant()
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
834
        
835
        revision = self.revision_if_availible_else_queue(revid)
836
        
837
        if not revision:
838
            return QtCore.QVariant()
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
839
        
840
        if role == QtCore.Qt.DisplayRole and index.column() == COL_DATE:
841
            return QtCore.QVariant(strftime("%Y-%m-%d %H:%M",
842
                                            localtime(revision.timestamp)))
843
        if role == QtCore.Qt.DisplayRole and index.column() == COL_AUTHOR:
230.1.49 by Lukáš Lalinský
qlog: Use revision.get_apparent_author() instead of rev.committer
844
            return QtCore.QVariant(extract_name(revision.get_apparent_author()))
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
845
        if role == QtCore.Qt.DisplayRole and index.column() == COL_MESSAGE:
846
            return QtCore.QVariant(revision.get_summary())
847
        if role == BugIdsRole:
848
            bugtext = gettext("bug #%s")
849
            bugs = []
850
            for bug in revision.properties.get('bugs', '').split('\n'):
851
                if bug:
852
                    url, status = bug.split(' ')
349.1.33 by Gary van der Merwe
qlog: Make it possible to specify multiple branches, or multiple file. Make pending merges are show.
853
                    bug_id = get_bug_id(url)
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
854
                    if bug_id:
855
                        bugs.append(bugtext % bug_id)
375 by Lukáš Lalinský
More qlog fixes on PyQt 4.4.3
856
            return QtCore.QVariant(QtCore.QStringList(bugs))
230.1.24 by Gary van der Merwe
qlog: Get search working again.
857
        
858
        if role == FilterMessageRole:
859
            return QtCore.QVariant(revision.message)
860
        if role == FilterAuthorRole:
230.1.49 by Lukáš Lalinský
qlog: Use revision.get_apparent_author() instead of rev.committer
861
            return QtCore.QVariant(revision.get_apparent_author())
230.1.4 by Gary van der Merwe
qlog: Move Model to separate file. I'm going to intergrate linegraph into the model, so it' going to get big.
862
        
863
        #return QtCore.QVariant(item.data(index.column()))
864
        return QtCore.QVariant()
865
866
    def flags(self, index):
867
        if not index.isValid():
868
            return QtCore.Qt.ItemIsEnabled
869
870
        return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
871
872
    def headerData(self, section, orientation, role):
873
        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
874
            return QtCore.QVariant(self.horizontalHeaderLabels[section])
230.1.5 by Gary van der Merwe
Move linegraph.py code to logmodel.py.
875
        return QtCore.QVariant()
230.1.20 by Gary van der Merwe
qlog: Get revisions from logmodel.
876
    
877
    def _revision(self, revid):
878
        if revid not in self.revisions:
516.1.1 by Gary van der Merwe
qlog: Make it possible to load log for branches in different repositories.
879
            revision = None
880
            for repo in self.repos.itervalues():
516.1.8 by Gary van der Merwe
qlog: Correctly handle missing revisions.
881
                try:
882
                    revision = repo.get_revisions([revid])[0]
516.1.1 by Gary van der Merwe
qlog: Make it possible to load log for branches in different repositories.
883
                    revision.repository = repo
884
                    break
516.1.8 by Gary van der Merwe
qlog: Correctly handle missing revisions.
885
                except errors.NoSuchRevision:
886
                    pass
516.1.1 by Gary van der Merwe
qlog: Make it possible to load log for branches in different repositories.
887
            
565 by Gary van der Merwe
qlog: Write our own revision loading code which allows us to batch the loading for better performance.
888
            self.post_revision_load(revision)
230.1.20 by Gary van der Merwe
qlog: Get revisions from logmodel.
889
        else:
890
            revision = self.revisions[revid]
891
        return revision
892
    
565 by Gary van der Merwe
qlog: Write our own revision loading code which allows us to batch the loading for better performance.
893
    def post_revision_load(self, revision):
894
        self.revisions[revision.revision_id] = revision
895
        revno_sequence = self.merge_sorted_revisions[self.revid_msri[revision.revision_id]][3]
896
        revision.revno = ".".join(["%d" % (revno)
897
                                  for revno in revno_sequence])
898
        revision.tags = sorted(self.tags.get(revision.revision_id, []))
899
    
230.1.20 by Gary van der Merwe
qlog: Get revisions from logmodel.
900
    def revision(self, revid):
901
        revision = self._revision(revid)
902
        if not hasattr(revision, 'parents'):
903
            revision.parents = [self._revision(i) for i in self.graph_parents[revid]]
904
        if not hasattr(revision, 'children'):
905
            revision.children = [self._revision(i) for i in self.graph_children[revid]]
906
        return revision
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
907
908
    def revision_if_availible_else_queue(self, revid):
909
        if revid not in self.revisions:
910
            if revid not in self.queue:
911
                self.queue.append(revid)
912
                self.load_queued_revisions.start()
913
            return None
914
        return self._revision(revid)
915
916
    def indexFromRevId(self, revid, columns=None):
308 by Gary van der Merwe
qlog: Don't try and select a revision if it is not visible.
917
        msri = self.revid_msri[revid]
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
918
        if columns:
919
            return [self.createIndex (msri, column, QtCore.QModelIndex())\
920
                    for column in columns]
349.1.2 by Gary van der Merwe
qlog: Continining refactor.
921
        return self.createIndex (msri, 0, QtCore.QModelIndex())
230.1.21 by Gary van der Merwe
qlog: Fix regression where you get an error when clicking on a qlog-revid link.
922
    
230.1.34 by Gary van der Merwe
qlog: Add keyboard navigation for graph.
923
    def findChildBranchMergeRevision (self, revid):
349.1.8 by Gary van der Merwe
qlog: Tweeks to background loading.
924
        msri = self.revid_msri[revid]
519 by Gary van der Merwe
qlog: Fix keyboard navigation.
925
        merged_by_msri = self.merge_info[msri][1]
926
        if merged_by_msri:
927
            return self.merge_sorted_revisions[merged_by_msri][1]
928
        else:
929
            return None
230.1.34 by Gary van der Merwe
qlog: Add keyboard navigation for graph.
930
    
516.1.3 by Gary van der Merwe
qlog: better way to work out for a revision, what branch it can be found in.
931
    def revisionHeadInfo(self, revid):
932
        if revid in self.revid_head:
933
            head_revid = self.revid_head[revid]
934
        else:
935
            head_revid = self.start_revs[0]
936
        return self.heads[head_revid][1]
937
    
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
938
class LoadRevisionsBase(BackgroundJob):
939
    def run(self):
567 by Gary van der Merwe
qlog: Tweeking revision loading code.
940
        self.update_time = self.update_time_initial
941
        self.start_time = clock()
942
        self.revisions_loaded = []
943
        
560 by Gary van der Merwe
qlog: Fix some bugs introduced by previous commit.
944
        for repo in self.parent.repos.itervalues():
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
945
            repo.lock_read()
946
        try:
565 by Gary van der Merwe
qlog: Write our own revision loading code which allows us to batch the loading for better performance.
947
            revision_ids = []
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
948
            for revid in self.revision_generator():
560 by Gary van der Merwe
qlog: Fix some bugs introduced by previous commit.
949
                if revid not in self.parent.revisions:
565 by Gary van der Merwe
qlog: Write our own revision loading code which allows us to batch the loading for better performance.
950
                    revision_ids.append(revid)
951
                if len(revision_ids)>50:
952
                    self.load_revisions(revision_ids)
953
            self.load_revisions(revision_ids)
567 by Gary van der Merwe
qlog: Tweeking revision loading code.
954
            self.notifyChanges()
565 by Gary van der Merwe
qlog: Write our own revision loading code which allows us to batch the loading for better performance.
955
        finally:
956
            for repo in self.parent.repos.itervalues():
957
                repo.unlock()
958
    
959
    def load_revisions(self, revision_ids):
960
        
961
        keys = [(key,) for key in revision_ids]
962
        
963
        for repo in self.parent.repos.itervalues():
964
            stream = repo.revisions.get_record_stream(keys, 'unordered', True)
965
            self.processEvents()
966
            for record in stream:
967
                if not record.storage_kind == 'absent':
968
                    revision_ids.remove(record.key[0])
567 by Gary van der Merwe
qlog: Tweeking revision loading code.
969
                    self.revisions_loaded.append(record.key[0])
565 by Gary van der Merwe
qlog: Write our own revision loading code which allows us to batch the loading for better performance.
970
                    text = record.get_bytes_as('fulltext')
971
                    rev = repo._serializer.read_revision_from_string(text)
972
                    rev.repository = repo
973
                    self.parent.post_revision_load(rev)
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
974
                    self.processEvents()
975
                
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
976
                current_time = clock()
567 by Gary van der Merwe
qlog: Tweeking revision loading code.
977
                if self.update_time < current_time - self.start_time:
978
                    self.notifyChanges()
979
                    self.update_time = max(self.update_time + self.update_time_increment,
980
                                           self.update_time_max)
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
981
                    self.processEvents()
567 by Gary van der Merwe
qlog: Tweeking revision loading code.
982
                    self.start_time = clock()
565 by Gary van der Merwe
qlog: Write our own revision loading code which allows us to batch the loading for better performance.
983
        
984
        
985
        # This should never happen
986
        if len(revision_ids) > 0 :
987
            raise errors.NoSuchRevision(self, revision_ids[0])
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
988
    
567 by Gary van der Merwe
qlog: Tweeking revision loading code.
989
    def notifyChanges(self):
990
        for revid in self.revisions_loaded:
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
991
            indexes = self.parent.indexFromRevId(revid, (COL_MESSAGE, COL_AUTHOR))
992
            self.parent.graphFilterProxyModel.invalidateCacheRow(indexes[0].row())
993
            self.parent.emit(QtCore.SIGNAL("dataChanged(QModelIndex, QModelIndex)"),
994
                      indexes[0], indexes[1])
995
        revisionsChanged = []
996
997
class LoadQueuedRevisions(LoadRevisionsBase):
998
    update_time_initial = 0.05
999
    update_time_increment = 0
1000
    update_time_max = 0.05
1001
    
1002
    def revision_generator(self):
1003
        while len(self.parent.queue):
1004
            yield self.parent.queue.pop(0)
563 by Gary van der Merwe
qlog: Don't load revisions that are not visible by discarding the queue when update the view. QT tries to reaccess the revisions that we haven't loaded anyway.
1005
567 by Gary van der Merwe
qlog: Tweeking revision loading code.
1006
    def notifyChanges(self):
563 by Gary van der Merwe
qlog: Don't load revisions that are not visible by discarding the queue when update the view. QT tries to reaccess the revisions that we haven't loaded anyway.
1007
        # Clear the queue
1008
        self.parent.queue = self.parent.queue[0:1]
567 by Gary van der Merwe
qlog: Tweeking revision loading code.
1009
        LoadRevisionsBase.notifyChanges(self)
563 by Gary van der Merwe
qlog: Don't load revisions that are not visible by discarding the queue when update the view. QT tries to reaccess the revisions that we haven't loaded anyway.
1010
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
1011
    
1012
class LoadAllRevisions(LoadRevisionsBase):
1013
    update_time_initial = 1
565 by Gary van der Merwe
qlog: Write our own revision loading code which allows us to batch the loading for better performance.
1014
    update_time_increment = 5
1015
    update_time_max = 20
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
1016
1017
    def revision_generator(self):
1018
        for (sequence_number,
1019
             revid,
1020
             merge_depth,
1021
             revno_sequence,
1022
             end_of_merge) in self.parent.merge_sorted_revisions:
1023
            yield revid
1024
567 by Gary van der Merwe
qlog: Tweeking revision loading code.
1025
    def notifyChanges(self):
1026
        LoadRevisionsBase.notifyChanges(self)
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
1027
        self.parent.compute_lines()
1028
    
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
1029
    
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
1030
class GraphFilterProxyModel(QtGui.QSortFilterProxyModel):
1031
    def __init__(self, parent = None):
349.3.1 by Gary van der Merwe
qlog: Intergrate with bzr-search for indexed searching.
1032
        self.old_filter_str = ""
1033
        self.old_filter_role = 0
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
1034
        QtGui.QSortFilterProxyModel.__init__(self, parent)
349.1.2 by Gary van der Merwe
qlog: Continining refactor.
1035
        self.cache = {}
349.3.1 by Gary van der Merwe
qlog: Intergrate with bzr-search for indexed searching.
1036
        self.search_matching_revid = None
516.1.7 by Gary van der Merwe
qlog: Load and use search indexes from all branches.
1037
        self.search_indexes = []
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1038
        self.filter_str = u""
1039
        self.filter_role = FilterMessageRole
1040
        self._sourceModel = None
1041
    
1042
    def setFilter(self, str, role):
1043
        if not unicode(str) == self.filter_str or not role == self.filter_role:
349.3.1 by Gary van der Merwe
qlog: Intergrate with bzr-search for indexed searching.
1044
            if role == FilterSearchRole:
349.3.4 by Gary van der Merwe
Merge Log
1045
                self.setFilterSearch(str)
349.3.1 by Gary van der Merwe
qlog: Intergrate with bzr-search for indexed searching.
1046
                self.setFilterRegExp("")
1047
            else:
1048
                self.setFilterRegExp(str)
1049
                self.setFilterRole(role)
349.3.4 by Gary van der Merwe
Merge Log
1050
                self.setFilterSearch("")
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1051
            self.invalidateCache()
1052
            self.sm().compute_lines()
1053
            
1054
            self.filter_str = unicode(str)
1055
            self.filter_role = role
1056
    
516.1.7 by Gary van der Merwe
qlog: Load and use search indexes from all branches.
1057
    def setSearchIndexes(self, indexes):
1058
        self.search_indexes = indexes
349.3.4 by Gary van der Merwe
Merge Log
1059
1060
    def setFilterSearch(self, s):
516.1.7 by Gary van der Merwe
qlog: Load and use search indexes from all branches.
1061
        if s == "" or not self.search_indexes or not have_search:
349.3.1 by Gary van der Merwe
qlog: Intergrate with bzr-search for indexed searching.
1062
            self.search_matching_revid = None
1063
        else:
1064
            s = str(s).strip()
1065
            query = [(query_item,) for query_item in s.split(" ")]
1066
            self.search_matching_revid = {}
516.1.7 by Gary van der Merwe
qlog: Load and use search indexes from all branches.
1067
            for index in self.search_indexes:
1068
                for result in index.search(query):
1069
                    if isinstance(result, search_index.RevisionHit):
1070
                        self.search_matching_revid[result.revision_key[0]] = True
1071
                    if isinstance(result, search_index.FileTextHit):
1072
                        self.search_matching_revid[result.text_key[1]] = True
1073
                    if isinstance(result, search_index.PathHit):
1074
                        pass
349.3.1 by Gary van der Merwe
qlog: Intergrate with bzr-search for indexed searching.
1075
    
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1076
    def sm(self):
1077
        if not self._sourceModel:
1078
            self._sourceModel = self.sourceModel()
1079
        return self._sourceModel
349.1.2 by Gary van der Merwe
qlog: Continining refactor.
1080
    
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
1081
    def invalidateCache (self):
349.1.2 by Gary van der Merwe
qlog: Continining refactor.
1082
        self.cache = {}
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1083
        self._sourceModel = None
1084
    
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
1085
    def invalidateCacheRow (self, source_row):
1086
        if source_row in self.cache:
1087
            del self.cache[source_row]
515 by Gary van der Merwe
qlog: Simplfy the code to work out merged_by and merges.
1088
        merged_by = self.sm().merge_info[source_row][1]
1089
        if merged_by:
1090
            self.invalidateCacheRow(merged_by)
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
1091
    
1092
    def filterAcceptsRow(self, source_row, source_parent):
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1093
        sm = self.sm()
349.1.8 by Gary van der Merwe
qlog: Tweeks to background loading.
1094
        
1095
        (sequence_number,
1096
         revid,
1097
         merge_depth,
1098
         revno_sequence,
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1099
         end_of_merge) = sm.merge_sorted_revisions[source_row]
349.1.8 by Gary van der Merwe
qlog: Tweeks to background loading.
1100
        
1101
        branch_id = revno_sequence[0:-1]
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1102
        if not sm.branch_lines[branch_id][1]: # branch colapased
349.1.8 by Gary van der Merwe
qlog: Tweeks to background loading.
1103
            return False
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
1104
        
1105
        return self.filterAcceptsRowIfBranchVisible(source_row, source_parent)
349.1.2 by Gary van der Merwe
qlog: Continining refactor.
1106
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
1107
    def filterAcceptsRowIfBranchVisible(self, source_row, source_parent):
349.1.2 by Gary van der Merwe
qlog: Continining refactor.
1108
        if source_row not in self.cache:
1109
            self.cache[source_row] = self._filterAcceptsRowIfBranchVisible(source_row, source_parent)
1110
        return self.cache[source_row]
1111
        
1112
    def _filterAcceptsRowIfBranchVisible(self, source_row, source_parent):
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1113
        sm = self.sm()
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
1114
        
516 by Gary van der Merwe
qlog: Make qlog FILE the same as log FILE which changed in bzr 1.8
1115
        for parent_msri in sm.merge_info[source_row][0]:
1116
            if self.filterAcceptsRowIfBranchVisible(parent_msri, source_parent):
1117
                return True
1118
        
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1119
        if sm.touches_file_msri is not None:
1120
            if source_row not in sm.touches_file_msri:
349.1.8 by Gary van der Merwe
qlog: Tweeks to background loading.
1121
                return False
1122
        
349.3.1 by Gary van der Merwe
qlog: Intergrate with bzr-search for indexed searching.
1123
        if self.search_matching_revid is not None:
1124
            (sequence_number,
1125
             revid,
1126
             merge_depth,
1127
             revno_sequence,
349.3.4 by Gary van der Merwe
Merge Log
1128
             end_of_merge) = sm.merge_sorted_revisions[source_row]
349.3.1 by Gary van der Merwe
qlog: Intergrate with bzr-search for indexed searching.
1129
            return revid in self.search_matching_revid
1130
        
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1131
        if self.filter_str:
566 by Gary van der Merwe
qlog: Don't try run the search filter on revisions that have not been loaded yet.
1132
            (sequence_number,
1133
             revid,
1134
             merge_depth,
1135
             revno_sequence,
1136
             end_of_merge) = sm.merge_sorted_revisions[source_row]
1137
            if revid in sm.revisions:
1138
                return QtGui.QSortFilterProxyModel.filterAcceptsRow(self, source_row, source_parent)
1139
            else:
1140
                return False
349.1.8 by Gary van der Merwe
qlog: Tweeks to background loading.
1141
        
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1142
        return True