1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2003-2008 - TortoiseSVN
\r
5 // This program is free software; you can redistribute it and/or
\r
6 // modify it under the terms of the GNU General Public License
\r
7 // as published by the Free Software Foundation; either version 2
\r
8 // of the License, or (at your option) any later version.
\r
10 // This program is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with this program; if not, write to the Free Software Foundation,
\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
20 #include "StandardLayout.h"
\r
22 #include "VisibleGraph.h"
\r
24 #include "StandardLayoutNodeList.h"
\r
25 #include "StandardLayoutConnectionList.h"
\r
26 #include "StandardLayoutTextList.h"
\r
30 CStandardLayoutNodeInfo::CStandardLayoutNodeInfo()
\r
32 , firstSubBranch (NULL)
\r
33 , nextInBranch (NULL)
\r
34 , previousInBranch (NULL)
\r
35 , lastInBranch (NULL)
\r
37 , previousBranch (NULL)
\r
43 , requiresRevision (true)
\r
44 , requiresPath (true)
\r
45 , requiresGap (true)
\r
46 , requiredSize (0, 0)
\r
47 , subTreeShift (0, 0)
\r
50 , parentBranch(NULL)
\r
54 // sort nodes: make "heaviest" branch the first one
\r
56 void CStandardLayout::SortNodes()
\r
58 for (size_t i = 0, count = nodes.size(); i < count; ++i)
\r
59 if (nodes[i].firstSubBranch != NULL)
\r
61 // find first deepest sub-branch
\r
63 CStandardLayoutNodeInfo* firstBranch = nodes[i].firstSubBranch;
\r
64 CStandardLayoutNodeInfo* heaviestBranch = firstBranch;
\r
65 index_t maxWeight = heaviestBranch->subTreeWeight;
\r
67 for ( CStandardLayoutNodeInfo* node = heaviestBranch->nextBranch
\r
69 ; node = node->nextBranch)
\r
71 if (node->subTreeWeight > maxWeight)
\r
73 maxWeight = node->subTreeWeight;
\r
74 heaviestBranch = node;
\r
78 // already the first one?
\r
80 if (firstBranch == heaviestBranch)
\r
83 // update "last" pointers, if necessary
\r
85 CStandardLayoutNodeInfo* nextBranch = heaviestBranch->nextBranch;
\r
86 if (nextBranch == NULL)
\r
88 CStandardLayoutNodeInfo* newLast = heaviestBranch->previousBranch;
\r
89 for ( CStandardLayoutNodeInfo* node = firstBranch
\r
91 ; node = node->nextBranch)
\r
93 node->lastBranch = newLast;
\r
97 // mode branch to front
\r
99 heaviestBranch->previousBranch->nextBranch = nextBranch;
\r
100 if (nextBranch != NULL)
\r
101 nextBranch->previousBranch = heaviestBranch->previousBranch;
\r
103 heaviestBranch->previousBranch = NULL;
\r
104 heaviestBranch->nextBranch = firstBranch;
\r
105 firstBranch->previousBranch = heaviestBranch;
\r
107 nodes[i].firstSubBranch = heaviestBranch;
\r
111 // layout creation:
\r
112 // * create a node info object for every node
\r
113 // * calculate branch and tree sizes (in nodes)
\r
115 void CStandardLayout::InitializeNodes ( const CVisibleGraphNode* start
\r
116 , CStandardLayoutNodeInfo* parentBranch)
\r
118 CStandardLayoutNodeInfo* previousInBranch = NULL;
\r
119 CStandardLayoutNodeInfo* lastInBranch = NULL;
\r
121 // measure subtree size, calculate branch length and back-link it
\r
123 index_t branchLength = 0;
\r
124 for ( const CVisibleGraphNode* node = start
\r
126 ; node = node->GetNext())
\r
128 // get the node, initialize it and update pointers
\r
130 CStandardLayoutNodeInfo& nodeInfo = nodes[node->GetIndex()];
\r
133 assert (nodeInfo.node == NULL);
\r
134 nodeInfo.node = node;
\r
135 nodeInfo.previousInBranch = previousInBranch;
\r
136 nodeInfo.parentBranch = parentBranch;
\r
137 if (previousInBranch != NULL)
\r
138 previousInBranch->nextInBranch = &nodeInfo;
\r
140 previousInBranch = &nodeInfo;
\r
141 lastInBranch = &nodeInfo;
\r
143 if (node->GetFirstCopyTarget())
\r
145 CStandardLayoutNodeInfo* previousBranch = NULL;
\r
146 CStandardLayoutNodeInfo* lastBranch = NULL;
\r
148 // measure sub-branches and back-link them
\r
150 for ( const CVisibleGraphNode::CCopyTarget*
\r
151 target = node->GetFirstCopyTarget()
\r
153 ; target = target->next())
\r
155 // get sub-branch node, initialize it and update pointers
\r
157 const CVisibleGraphNode* subNode = target->value();
\r
158 CStandardLayoutNodeInfo& subNodeInfo = nodes[subNode->GetIndex()];
\r
160 if (previousBranch != NULL)
\r
162 previousBranch->nextBranch = &subNodeInfo;
\r
163 subNodeInfo.previousBranch = previousBranch;
\r
167 // mark the first branch
\r
169 nodeInfo.firstSubBranch = &subNodeInfo;
\r
172 previousBranch = &subNodeInfo;
\r
173 lastBranch = &subNodeInfo;
\r
177 InitializeNodes (subNode, &nodeInfo);
\r
179 // accumulate branch into sub-tree
\r
181 nodeInfo.subTreeWidth += subNodeInfo.subTreeWidth;
\r
182 nodeInfo.subTreeWeight += subNodeInfo.subTreeWeight;
\r
183 if (nodeInfo.subTreeHeight <= subNodeInfo.subTreeHeight)
\r
184 nodeInfo.subTreeHeight = subNodeInfo.subTreeHeight+1;
\r
187 // link sub-branches forward
\r
189 for ( const CVisibleGraphNode::CCopyTarget*
\r
190 target = node->GetFirstCopyTarget()
\r
192 ; target = target->next())
\r
194 nodes[target->value()->GetIndex()].lastBranch = lastBranch;
\r
199 // write branch lengths, adjust sub-tree heights and link forward
\r
201 for ( const CVisibleGraphNode* node = start
\r
203 ; node = node->GetNext())
\r
205 CStandardLayoutNodeInfo& nodeInfo = nodes[node->GetIndex()];
\r
206 nodeInfo.branchLength = branchLength;
\r
207 nodeInfo.lastInBranch = lastInBranch;
\r
209 if (nodeInfo.subTreeHeight < branchLength)
\r
210 nodeInfo.subTreeHeight = branchLength;
\r
214 void CStandardLayout::InitializeNodes (const CVisibleGraph* graph)
\r
216 nodes.resize (graph->GetNodeCount());
\r
218 for (size_t i = 0, count = graph->GetRootCount(); i < count; ++i)
\r
219 InitializeNodes (graph->GetRoot (i), NULL);
\r
221 // every node info must actually refer to a node
\r
223 for (size_t i = 0, count = nodes.size(); i < count; ++i)
\r
224 assert (nodes[i].node != NULL);
\r
226 // adjust sub-tree weights
\r
228 for (size_t i = nodes.size(); i > 0; --i)
\r
230 CStandardLayoutNodeInfo& node = nodes[i-1];
\r
232 index_t weight = node.nextInBranch != NULL
\r
233 ? node.nextInBranch->subTreeWeight+1
\r
236 for ( CStandardLayoutNodeInfo* branch = node.firstSubBranch
\r
238 ; branch = branch->nextBranch)
\r
239 weight += branch->subTreeWeight;
\r
241 node.subTreeWeight = weight;
\r
244 // prevent degenerated branches from growing too far to the right
\r
249 // scan tree for connections between non-overlapping nodes
\r
251 void CStandardLayout::CreateConnections()
\r
253 // there can't be more connections than nodes
\r
255 connections.reserve (nodes.size());
\r
256 for (index_t i = 0, count = (index_t)nodes.size(); i < count; ++i)
\r
258 const CVisibleGraphNode* node = nodes[i].node;
\r
259 const CVisibleGraphNode* previousNode = node->GetCopySource()
\r
260 ? node->GetCopySource()
\r
261 : node->GetPrevious();
\r
265 if (previousNode == NULL)
\r
270 const CRect& previousRect = nodes[previousNode->GetIndex()].rect;
\r
271 CRect rect = nodes[i].rect;
\r
273 // no line because nodes touch or overlap?
\r
275 rect.InflateRect (1, 1, 1, 1);
\r
276 if (TRUE == CRect().IntersectRect (rect, previousRect))
\r
279 // an actual connection
\r
281 connections.push_back (std::make_pair (previousNode->GetIndex(), i));
\r
285 // scan tree for connections longer than 0
\r
287 void CStandardLayout::CreateTexts()
\r
289 // there can be at most two texts per node
\r
291 texts.reserve (2*nodes.size());
\r
293 // cover all node rects
\r
294 // (connections and texts will lie within these bounds)
\r
296 for (index_t i = 0, count = (index_t)nodes.size(); i < count; ++i)
\r
298 const CStandardLayoutNodeInfo& info = nodes[i];
\r
300 if (info.requiresRevision)
\r
301 texts.push_back (STextInfo (i, 0));
\r
303 if (info.requiresPath)
\r
304 for (index_t k = info.node->GetPath().GetDepth(); k > 0; --k)
\r
305 texts.push_back (STextInfo (i, k));
\r
309 // just iterate over all nodes
\r
311 void CStandardLayout::CalculateBoundingRect()
\r
313 // special case: empty graph
\r
317 boundingRect = CRect();
\r
321 // cover all node rects
\r
322 // (connections and texts will lie within these bounds)
\r
324 boundingRect = nodes[0].rect;
\r
325 for (size_t i = 1, count = nodes.size(); i < count; ++i)
\r
326 boundingRect |= nodes[i].rect;
\r
329 /// construction / destruction
\r
331 CStandardLayout::CStandardLayout ( const CCachedLogInfo* cache
\r
332 , const CVisibleGraph* graph)
\r
335 InitializeNodes (graph);
\r
338 CStandardLayout::~CStandardLayout(void)
\r
342 // call this after executing the format options
\r
344 void CStandardLayout::Finalize()
\r
346 CreateConnections();
\r
348 CalculateBoundingRect();
\r
351 /// implement IRevisionGraphLayout
\r
353 CRect CStandardLayout::GetRect() const
\r
355 return boundingRect;
\r
358 const ILayoutNodeList* CStandardLayout::GetNodes() const
\r
360 return new CStandardLayoutNodeList (nodes, cache);
\r
363 const ILayoutConnectionList* CStandardLayout::GetConnections() const
\r
365 return new CStandardLayoutConnectionList (nodes, connections);
\r
368 const ILayoutTextList* CStandardLayout::GetTexts() const
\r
370 return new CStandardLayoutTextList (nodes, texts);
\r
373 /// implement IStandardLayoutNodeAccess
\r
375 index_t CStandardLayout::GetNodeCount() const
\r
377 return static_cast<index_t>(nodes.size());
\r
380 CStandardLayoutNodeInfo* CStandardLayout::GetNode (index_t index)
\r
382 return &nodes[index];
\r