Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 33caabd2a667728a59d84e23d6c1fb0e15eb140c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/**
 * Copyright (c) 2004 - 2010 Eike Stepper (Berlin, Germany) and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Andre Dietisheim - initial API and implementation
 */
package org.eclipse.emf.cdo.ui.internal.branch.layout;

import org.eclipse.emf.cdo.common.branch.CDOBranch;
import org.eclipse.emf.cdo.ui.internal.branch.geometry.GeometryUtils;
import org.eclipse.emf.cdo.ui.internal.branch.item.AbstractBranchPointNode;
import org.eclipse.emf.cdo.ui.internal.branch.item.BranchPointNode;
import org.eclipse.emf.cdo.ui.internal.branch.item.BranchTreeUtils;

import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension;
import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle;

import java.util.Collection;

/**
 * A Branch is a structure that holds the baseline node of a branch. Its main purpose is to climb through the branch
 * tree and call the layout strategy on all nodes in an appropriate manner.
 * <p>
 * The strategy is to first lay out all (sibling) nodes in the order of their time stamp. Sub-branches are skipped. In a
 * second step all branches are positioned while beginning with the latest one (in terms of time stamp).
 * 
 * @author Andre Dietisheim
 * @see VerticallyDistributedSubBranches
 */
public class BranchView
{
  private CDOBranch branch;

  private AbstractBranchPointNode baselineNode;

  protected Deque<AbstractBranchPointNode> nodes = new Deque<AbstractBranchPointNode>();

  private Deque<BranchView> subBranchViews = new Deque<BranchView>();

  private DisplayIndependentRectangle bounds;

  private BranchViewLayoutStrategy layoutStrategy;

  public BranchView(AbstractBranchPointNode baselineNode, BranchViewLayoutStrategy layoutStrategy)
  {
    branch = baselineNode.getBranch();
    this.baselineNode = baselineNode;
    this.layoutStrategy = layoutStrategy;
    nodes.addLast(baselineNode);
    layoutStrategy.layoutBaselineNode(this, baselineNode);
    addNode(baselineNode.getNextOnSameBranch());

    if (baselineNode instanceof BranchPointNode)
    {
      // add a branch to this node
      BranchPointNode branchpointNode = (BranchPointNode)baselineNode;
      addBranchView(branchpointNode.getNextOnNewBranch(), branchpointNode);
    }
  }

  public CDOBranch getBranch()
  {
    return branch;
  }

  public AbstractBranchPointNode getBaselineNode()
  {
    return baselineNode;
  }

  public Collection<AbstractBranchPointNode> getNodes()
  {
    return nodes;
  }

  public BranchViewLayoutStrategy getLayoutStrategy()
  {
    return layoutStrategy;
  }

  /**
   * Adds the given node to this branch. Climbs recursively up to all (sibling) nodes on the same branch. When it gets
   * back from recursion it builds and attaches branches to those nodes.
   * <p>
   * The strategy is to add all sibling nodes in the order of their time stamp and to add the branches in the reverse
   * (in terms of time stamp) order
   * 
   * @see #addBranchView(AbstractBranchPointNode, BranchPointNode)
   */
  private void addNode(AbstractBranchPointNode node)
  {
    if (node != null)
    {
      AbstractBranchPointNode previousNode = nodes.peekLast();
      nodes.addLast(node);
      layoutStrategy.layoutNode(this, node, previousNode);
      // recursively navigate to sibling
      addNode(node.getNextOnSameBranch());

      if (node instanceof BranchPointNode)
      {
        // add a branch to this node
        BranchPointNode branchPointNode = (BranchPointNode)node;
        addBranchView(branchPointNode.getNextOnNewBranch(), branchPointNode);
      }
    }
  }

  /**
   * Adds a sub-branch to the given branch point node with the given baseline node.
   */
  private void addBranchView(AbstractBranchPointNode baselineNode, BranchPointNode branchPointNode)
  {
    if (baselineNode != null)
    {
      BranchView subBranchView = new BranchView(baselineNode, getLayoutStrategy());
      layoutStrategy.layoutSubBranchView(this, subBranchView, branchPointNode);
      subBranchViews.add(subBranchView);
    }
  }

  /**
   * Returns the bounds of this branch view. The bounds returned contain all sub-branches (and their nodes)
   * 
   * @return the bounds
   */
  public DisplayIndependentRectangle getBounds()
  {
    return bounds;
  }

  /**
   * Sets the bounds of this branch view. The bounds must contain this branch and all its sub branch views (and all
   * their nodes)
   * 
   * @param bounds
   *          the new bounds
   */
  public void setBounds(DisplayIndependentRectangle bounds)
  {
    this.bounds = bounds;
  }

  /**
   * Translates the given branch view by the given dimension.
   * 
   * @param dimension
   *          the dimension the x- and y-offset
   */
  public void translate(DisplayIndependentDimension dimension)
  {
    translateNodesOnSameBranch(dimension);
    translateSubBranches(dimension);
    GeometryUtils.translateRectangle(dimension.width, dimension.height, getBounds());
  }

  /**
   * Translates all sub branches of the given branch.
   * 
   * @param dimension
   *          the dimension to translate this branch by
   */
  private void translateSubBranches(DisplayIndependentDimension dimension)
  {
    for (BranchView branch : getSubBranchViews())
    {
      branch.translate(dimension);
    }
  }

  /**
   * Translates all the sibling nodes in this branch view. Applies the given horizontal and vertical offset.
   */
  private void translateNodesOnSameBranch(DisplayIndependentDimension dimension)
  {
    for (AbstractBranchPointNode node : getNodes())
    {
      BranchTreeUtils.translateInternalLocation(node, dimension.width, dimension.height);
    }
  }

  /**
   * Returns not the last, but the one before the last sub branch view. If none is present, <tt>null<tt> is returned.
   * 
   * @return the second to last sub branch view or <tt>null<tt>
   */
  public BranchView getSecondToLastSubBranchView()
  {
    if (subBranchViews.isEmpty() || subBranchViews.size() < 2)
    {
      return null;
    }

    return subBranchViews.get(subBranchViews.size() - 1);
  }

  /**
   * Returns all sub branch views present in this branch view.
   * 
   * @return all sub branch views in this branch view
   */
  public Collection<BranchView> getSubBranchViews()
  {
    return subBranchViews;
  }
}

Back to the top