~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
            
230.1.20 by Gary van der Merwe
qlog: Get revisions from logmodel.
888
            self.revisions[revid] = revision
889
            revno_sequence = self.merge_sorted_revisions[self.revid_msri[revid]][3]
890
            revision.revno = ".".join(["%d" % (revno)
891
                                      for revno in revno_sequence])
318 by Alexander Belchenko
Restore showing tags in revision info (bottom left window) in qlog.
892
            revision.tags = sorted(self.tags.get(revision.revision_id, []))
230.1.20 by Gary van der Merwe
qlog: Get revisions from logmodel.
893
        else:
894
            revision = self.revisions[revid]
895
        return revision
896
    
897
    def revision(self, revid):
898
        revision = self._revision(revid)
899
        if not hasattr(revision, 'parents'):
900
            revision.parents = [self._revision(i) for i in self.graph_parents[revid]]
901
        if not hasattr(revision, 'children'):
902
            revision.children = [self._revision(i) for i in self.graph_children[revid]]
903
        return revision
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
904
905
    def revision_if_availible_else_queue(self, revid):
906
        if revid not in self.revisions:
907
            if revid not in self.queue:
908
                self.queue.append(revid)
909
                self.load_queued_revisions.start()
910
            return None
911
        return self._revision(revid)
912
913
    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.
914
        msri = self.revid_msri[revid]
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
915
        if columns:
916
            return [self.createIndex (msri, column, QtCore.QModelIndex())\
917
                    for column in columns]
349.1.2 by Gary van der Merwe
qlog: Continining refactor.
918
        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.
919
    
230.1.34 by Gary van der Merwe
qlog: Add keyboard navigation for graph.
920
    def findChildBranchMergeRevision (self, revid):
349.1.8 by Gary van der Merwe
qlog: Tweeks to background loading.
921
        msri = self.revid_msri[revid]
519 by Gary van der Merwe
qlog: Fix keyboard navigation.
922
        merged_by_msri = self.merge_info[msri][1]
923
        if merged_by_msri:
924
            return self.merge_sorted_revisions[merged_by_msri][1]
925
        else:
926
            return None
230.1.34 by Gary van der Merwe
qlog: Add keyboard navigation for graph.
927
    
516.1.3 by Gary van der Merwe
qlog: better way to work out for a revision, what branch it can be found in.
928
    def revisionHeadInfo(self, revid):
929
        if revid in self.revid_head:
930
            head_revid = self.revid_head[revid]
931
        else:
932
            head_revid = self.start_revs[0]
933
        return self.heads[head_revid][1]
934
    
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
935
class LoadRevisionsBase(BackgroundJob):
936
    def run(self):
937
        update_time = self.update_time_initial
938
        start_time = clock()
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
939
        revisionsChanged = []
560 by Gary van der Merwe
qlog: Fix some bugs introduced by previous commit.
940
        for repo in self.parent.repos.itervalues():
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
941
            repo.lock_read()
942
        try:
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
943
            for revid in self.revision_generator():
560 by Gary van der Merwe
qlog: Fix some bugs introduced by previous commit.
944
                if revid not in self.parent.revisions:
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
945
                    self.parent._revision(revid)
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
946
                    revisionsChanged.append(revid)
947
                    self.processEvents()
948
                
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
949
                current_time = clock()
950
                if update_time < current_time - start_time:
951
                    self.notifyChanges(revisionsChanged)
952
                    update_time = max(update_time + self.update_time_increment, self.update_time_max)
953
                    self.processEvents()
954
                    start_time = clock()
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
955
            
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
956
            self.notifyChanges(revisionsChanged)
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
957
        finally:
560 by Gary van der Merwe
qlog: Fix some bugs introduced by previous commit.
958
            for repo in self.parent.repos.itervalues():
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
959
                repo.unlock()
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
960
    
961
    def notifyChanges(self, revisionsChanged):
962
        for revid in revisionsChanged:
963
            indexes = self.parent.indexFromRevId(revid, (COL_MESSAGE, COL_AUTHOR))
964
            self.parent.graphFilterProxyModel.invalidateCacheRow(indexes[0].row())
965
            self.parent.emit(QtCore.SIGNAL("dataChanged(QModelIndex, QModelIndex)"),
966
                      indexes[0], indexes[1])
967
        revisionsChanged = []
968
969
class LoadQueuedRevisions(LoadRevisionsBase):
970
    update_time_initial = 0.05
971
    update_time_increment = 0
972
    update_time_max = 0.05
973
    
974
    def revision_generator(self):
975
        while len(self.parent.queue):
976
            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.
977
978
    def notifyChanges(self, revisionsChanged):
979
        # Clear the queue
980
        self.parent.queue = self.parent.queue[0:1]
981
        LoadRevisionsBase.notifyChanges(self, revisionsChanged)
982
561 by Gary van der Merwe
qlog: For background revision loading, update on time, not on number of revisions.
983
    
984
class LoadAllRevisions(LoadRevisionsBase):
985
    update_time_initial = 1
986
    update_time_increment = 1
987
    update_time_max = 10
988
989
    def revision_generator(self):
990
        for (sequence_number,
991
             revid,
992
             merge_depth,
993
             revno_sequence,
994
             end_of_merge) in self.parent.merge_sorted_revisions:
995
            yield revid
996
997
    def notifyChanges(self, revisionsChanged):
998
        LoadRevisionsBase.notifyChanges(self, revisionsChanged)
999
        self.parent.compute_lines()
1000
    
559 by Gary van der Merwe
qlog: Don't block the ui when loading revisions.
1001
    
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
1002
class GraphFilterProxyModel(QtGui.QSortFilterProxyModel):
1003
    def __init__(self, parent = None):
349.3.1 by Gary van der Merwe
qlog: Intergrate with bzr-search for indexed searching.
1004
        self.old_filter_str = ""
1005
        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.
1006
        QtGui.QSortFilterProxyModel.__init__(self, parent)
349.1.2 by Gary van der Merwe
qlog: Continining refactor.
1007
        self.cache = {}
349.3.1 by Gary van der Merwe
qlog: Intergrate with bzr-search for indexed searching.
1008
        self.search_matching_revid = None
516.1.7 by Gary van der Merwe
qlog: Load and use search indexes from all branches.
1009
        self.search_indexes = []
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1010
        self.filter_str = u""
1011
        self.filter_role = FilterMessageRole
1012
        self._sourceModel = None
1013
    
1014
    def setFilter(self, str, role):
1015
        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.
1016
            if role == FilterSearchRole:
349.3.4 by Gary van der Merwe
Merge Log
1017
                self.setFilterSearch(str)
349.3.1 by Gary van der Merwe
qlog: Intergrate with bzr-search for indexed searching.
1018
                self.setFilterRegExp("")
1019
            else:
1020
                self.setFilterRegExp(str)
1021
                self.setFilterRole(role)
349.3.4 by Gary van der Merwe
Merge Log
1022
                self.setFilterSearch("")
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1023
            self.invalidateCache()
1024
            self.sm().compute_lines()
1025
            
1026
            self.filter_str = unicode(str)
1027
            self.filter_role = role
1028
    
516.1.7 by Gary van der Merwe
qlog: Load and use search indexes from all branches.
1029
    def setSearchIndexes(self, indexes):
1030
        self.search_indexes = indexes
349.3.4 by Gary van der Merwe
Merge Log
1031
1032
    def setFilterSearch(self, s):
516.1.7 by Gary van der Merwe
qlog: Load and use search indexes from all branches.
1033
        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.
1034
            self.search_matching_revid = None
1035
        else:
1036
            s = str(s).strip()
1037
            query = [(query_item,) for query_item in s.split(" ")]
1038
            self.search_matching_revid = {}
516.1.7 by Gary van der Merwe
qlog: Load and use search indexes from all branches.
1039
            for index in self.search_indexes:
1040
                for result in index.search(query):
1041
                    if isinstance(result, search_index.RevisionHit):
1042
                        self.search_matching_revid[result.revision_key[0]] = True
1043
                    if isinstance(result, search_index.FileTextHit):
1044
                        self.search_matching_revid[result.text_key[1]] = True
1045
                    if isinstance(result, search_index.PathHit):
1046
                        pass
349.3.1 by Gary van der Merwe
qlog: Intergrate with bzr-search for indexed searching.
1047
    
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1048
    def sm(self):
1049
        if not self._sourceModel:
1050
            self._sourceModel = self.sourceModel()
1051
        return self._sourceModel
349.1.2 by Gary van der Merwe
qlog: Continining refactor.
1052
    
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
1053
    def invalidateCache (self):
349.1.2 by Gary van der Merwe
qlog: Continining refactor.
1054
        self.cache = {}
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1055
        self._sourceModel = None
1056
    
349.1.4 by Gary van der Merwe
qlog: Performance tweeking.
1057
    def invalidateCacheRow (self, source_row):
1058
        if source_row in self.cache:
1059
            del self.cache[source_row]
515 by Gary van der Merwe
qlog: Simplfy the code to work out merged_by and merges.
1060
        merged_by = self.sm().merge_info[source_row][1]
1061
        if merged_by:
1062
            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.
1063
    
1064
    def filterAcceptsRow(self, source_row, source_parent):
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1065
        sm = self.sm()
349.1.8 by Gary van der Merwe
qlog: Tweeks to background loading.
1066
        
1067
        (sequence_number,
1068
         revid,
1069
         merge_depth,
1070
         revno_sequence,
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1071
         end_of_merge) = sm.merge_sorted_revisions[source_row]
349.1.8 by Gary van der Merwe
qlog: Tweeks to background loading.
1072
        
1073
        branch_id = revno_sequence[0:-1]
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1074
        if not sm.branch_lines[branch_id][1]: # branch colapased
349.1.8 by Gary van der Merwe
qlog: Tweeks to background loading.
1075
            return False
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
1076
        
1077
        return self.filterAcceptsRowIfBranchVisible(source_row, source_parent)
349.1.2 by Gary van der Merwe
qlog: Continining refactor.
1078
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
1079
    def filterAcceptsRowIfBranchVisible(self, source_row, source_parent):
349.1.2 by Gary van der Merwe
qlog: Continining refactor.
1080
        if source_row not in self.cache:
1081
            self.cache[source_row] = self._filterAcceptsRowIfBranchVisible(source_row, source_parent)
1082
        return self.cache[source_row]
1083
        
1084
    def _filterAcceptsRowIfBranchVisible(self, source_row, source_parent):
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1085
        sm = self.sm()
349.1.1 by Gary van der Merwe
qlog: Use QSortFilterProxyModel to do filtering of for specific_fileid and colapsed branchs.
1086
        
516 by Gary van der Merwe
qlog: Make qlog FILE the same as log FILE which changed in bzr 1.8
1087
        for parent_msri in sm.merge_info[source_row][0]:
1088
            if self.filterAcceptsRowIfBranchVisible(parent_msri, source_parent):
1089
                return True
1090
        
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1091
        if sm.touches_file_msri is not None:
1092
            if source_row not in sm.touches_file_msri:
349.1.8 by Gary van der Merwe
qlog: Tweeks to background loading.
1093
                return False
1094
        
349.3.1 by Gary van der Merwe
qlog: Intergrate with bzr-search for indexed searching.
1095
        if self.search_matching_revid is not None:
1096
            (sequence_number,
1097
             revid,
1098
             merge_depth,
1099
             revno_sequence,
349.3.4 by Gary van der Merwe
Merge Log
1100
             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.
1101
            return revid in self.search_matching_revid
1102
        
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1103
        if self.filter_str:
1104
            return QtGui.QSortFilterProxyModel.filterAcceptsRow(self, source_row, source_parent)
349.1.8 by Gary van der Merwe
qlog: Tweeks to background loading.
1105
        
349.1.18 by Gary van der Merwe
qlog: Optimize filterAcceptsRow.
1106
        return True