Chris@44
|
1
|
Chris@44
|
2 #include "grapher.h"
|
Chris@44
|
3
|
cannam@45
|
4 #include <QGraphicsScene>
|
Chris@44
|
5
|
Chris@44
|
6 #include <iostream>
|
Chris@44
|
7
|
cannam@45
|
8 int
|
cannam@45
|
9 Grapher::findAvailableColumn(int row, int parent, bool preferParentCol)
|
cannam@45
|
10 {
|
cannam@45
|
11 int col = parent;
|
cannam@45
|
12 if (preferParentCol) {
|
cannam@45
|
13 if (!m_alloc[row].contains(col)) {
|
cannam@45
|
14 return col;
|
cannam@45
|
15 }
|
cannam@45
|
16 }
|
cannam@45
|
17 while (col > 0) {
|
cannam@45
|
18 if (!m_alloc[row].contains(--col)) return col;
|
cannam@45
|
19 }
|
cannam@45
|
20 while (col < 0) {
|
cannam@45
|
21 if (!m_alloc[row].contains(++col)) return col;
|
cannam@45
|
22 }
|
cannam@45
|
23 col = parent;
|
cannam@45
|
24 int sign = (col < 0 ? -1 : 1);
|
cannam@45
|
25 while (1) {
|
cannam@45
|
26 col += sign;
|
cannam@45
|
27 if (!m_alloc[row].contains(col)) return col;
|
cannam@45
|
28 }
|
cannam@45
|
29 }
|
Chris@44
|
30
|
cannam@45
|
31 void
|
cannam@45
|
32 Grapher::layoutRow(QString id)
|
Chris@44
|
33 {
|
cannam@45
|
34 if (m_handled.contains(id)) {
|
cannam@45
|
35 return;
|
Chris@44
|
36 }
|
cannam@45
|
37 if (!m_idCsetMap.contains(id)) {
|
cannam@45
|
38 throw LayoutException(QString("Changeset %1 not in ID map").arg(id));
|
Chris@44
|
39 }
|
cannam@45
|
40 if (!m_items.contains(id)) {
|
cannam@45
|
41 throw LayoutException(QString("Changeset %1 not in item map").arg(id));
|
Chris@44
|
42 }
|
cannam@45
|
43 Changeset *cs = m_idCsetMap[id];
|
cannam@45
|
44 ChangesetItem *item = m_items[id];
|
cannam@45
|
45 std::cerr << "Looking at " << id.toStdString() << std::endl;
|
cannam@45
|
46
|
Chris@44
|
47 int row = 0;
|
cannam@45
|
48 int nparents = cs->parents().size();
|
cannam@45
|
49
|
cannam@45
|
50 if (nparents > 0) {
|
Chris@44
|
51 bool haveRow = false;
|
Chris@44
|
52 foreach (QString parentId, cs->parents()) {
|
cannam@45
|
53
|
cannam@45
|
54 if (!m_idCsetMap.contains(parentId)) continue;
|
cannam@45
|
55 if (!m_items.contains(parentId)) continue;
|
cannam@45
|
56
|
cannam@45
|
57 if (!m_handled.contains(parentId)) {
|
cannam@45
|
58 layoutRow(parentId);
|
cannam@45
|
59 }
|
cannam@45
|
60
|
cannam@45
|
61 ChangesetItem *parentItem = m_items[parentId];
|
Chris@44
|
62 if (!haveRow || parentItem->row() < row) {
|
Chris@44
|
63 row = parentItem->row();
|
Chris@44
|
64 haveRow = true;
|
Chris@44
|
65 }
|
Chris@44
|
66 }
|
Chris@44
|
67 row = row - 1;
|
cannam@45
|
68 }
|
cannam@45
|
69
|
cannam@45
|
70 std::cerr << "putting " << cs->id().toStdString() << " at row " << row
|
cannam@45
|
71 << std::endl;
|
cannam@45
|
72
|
Chris@44
|
73 item->setRow(row);
|
Chris@44
|
74 item->setY(row * 100);
|
cannam@45
|
75 m_handled.insert(id);
|
Chris@44
|
76 }
|
Chris@44
|
77
|
Chris@44
|
78 void
|
cannam@45
|
79 Grapher::layoutCol(QString id)
|
Chris@44
|
80 {
|
cannam@45
|
81 if (m_handled.contains(id)) {
|
cannam@45
|
82 std::cerr << "Already looked at " << id.toStdString() << std::endl;
|
cannam@45
|
83 return;
|
cannam@45
|
84 }
|
cannam@45
|
85 if (!m_idCsetMap.contains(id)) {
|
cannam@45
|
86 throw LayoutException(QString("Changeset %1 not in ID map").arg(id));
|
cannam@45
|
87 }
|
cannam@45
|
88 if (!m_items.contains(id)) {
|
cannam@45
|
89 throw LayoutException(QString("Changeset %1 not in item map").arg(id));
|
cannam@45
|
90 }
|
cannam@45
|
91 Changeset *cs = m_idCsetMap[id];
|
cannam@45
|
92 ChangesetItem *item = m_items[id];
|
cannam@45
|
93 std::cerr << "Looking at " << id.toStdString() << std::endl;
|
cannam@45
|
94
|
cannam@45
|
95 int col = 0;
|
cannam@45
|
96 int nparents = cs->parents().size();
|
cannam@45
|
97
|
cannam@45
|
98 if (nparents > 0) {
|
cannam@45
|
99 bool preferParentCol = true;
|
cannam@45
|
100 foreach (QString parentId, cs->parents()) {
|
cannam@45
|
101
|
cannam@45
|
102 if (!m_idCsetMap.contains(parentId)) continue;
|
cannam@45
|
103 if (!m_items.contains(parentId)) continue;
|
cannam@45
|
104
|
cannam@45
|
105 if (nparents == 1) {
|
cannam@45
|
106 // when introducing a new branch, aim _not_ to
|
cannam@45
|
107 // position child on the same column as parent
|
cannam@45
|
108 Changeset *parent = m_idCsetMap[parentId];
|
cannam@45
|
109 if (parent->branch() != cs->branch()) {
|
cannam@45
|
110 preferParentCol = false;
|
cannam@45
|
111 }
|
cannam@45
|
112 }
|
cannam@45
|
113
|
cannam@45
|
114 if (!m_handled.contains(parentId)) {
|
cannam@45
|
115 layoutCol(parentId);
|
cannam@45
|
116 }
|
cannam@45
|
117
|
cannam@45
|
118 ChangesetItem *parentItem = m_items[parentId];
|
cannam@45
|
119 col += parentItem->column();
|
Chris@44
|
120 }
|
cannam@45
|
121
|
cannam@45
|
122 col /= cs->parents().size();
|
cannam@45
|
123 col = findAvailableColumn(item->row(), col, preferParentCol);
|
cannam@45
|
124 m_alloc[item->row()].insert(col);
|
Chris@44
|
125 }
|
Chris@44
|
126
|
cannam@45
|
127 std::cerr << "putting " << cs->id().toStdString() << " at col " << col << std::endl;
|
cannam@45
|
128
|
cannam@45
|
129 item->setColumn(col);
|
cannam@45
|
130 item->setX(col * 100);
|
cannam@45
|
131 m_handled.insert(id);
|
cannam@45
|
132 }
|
cannam@45
|
133
|
cannam@45
|
134 void
|
cannam@45
|
135 Grapher::layout(Changesets csets)
|
cannam@45
|
136 {
|
cannam@45
|
137 m_idCsetMap.clear();
|
cannam@45
|
138 m_items.clear();
|
cannam@45
|
139 m_alloc.clear();
|
cannam@45
|
140
|
Chris@44
|
141 foreach (Changeset *cs, csets) {
|
cannam@45
|
142
|
cannam@45
|
143 QString id = cs->id();
|
cannam@45
|
144 std::cerr << id.toStdString() << std::endl;
|
cannam@45
|
145
|
cannam@45
|
146 if (id == "") {
|
cannam@45
|
147 throw LayoutException("Changeset has no ID");
|
cannam@45
|
148 }
|
cannam@45
|
149 if (m_idCsetMap.contains(id)) {
|
cannam@45
|
150 throw LayoutException(QString("Duplicate changeset ID %1").arg(id));
|
cannam@45
|
151 }
|
cannam@45
|
152
|
cannam@45
|
153 m_idCsetMap[id] = cs;
|
cannam@45
|
154
|
cannam@45
|
155 ChangesetItem *item = new ChangesetItem(cs);
|
cannam@45
|
156 item->setX(0);
|
cannam@45
|
157 item->setY(0);
|
cannam@45
|
158 m_items[id] = item;
|
cannam@45
|
159 m_scene->addItem(item);
|
cannam@45
|
160 }
|
cannam@45
|
161
|
cannam@45
|
162 // Layout in reverse order, i.e. forward chronological order.
|
cannam@45
|
163 // This ensures that parents will normally be laid out before
|
cannam@45
|
164 // their children -- though we can recurse from layout() if we
|
cannam@45
|
165 // find any weird exceptions
|
cannam@45
|
166 m_handled.clear();
|
cannam@45
|
167 for (int i = csets.size() - 1; i >= 0; --i) {
|
cannam@45
|
168 layoutRow(csets[i]->id());
|
cannam@45
|
169 }
|
cannam@45
|
170 m_handled.clear();
|
cannam@45
|
171 for (int i = csets.size() - 1; i >= 0; --i) {
|
cannam@45
|
172 layoutCol(csets[i]->id());
|
cannam@45
|
173 }
|
cannam@45
|
174
|
cannam@45
|
175 foreach (Changeset *cs, csets) {
|
cannam@45
|
176 QString id = cs->id();
|
cannam@45
|
177 if (!m_items.contains(id)) continue;
|
cannam@45
|
178 ChangesetItem *me = m_items[id];
|
cannam@45
|
179 foreach (QString parentId, cs->parents()) {
|
cannam@45
|
180 if (!m_items.contains(parentId)) continue;
|
cannam@45
|
181 ChangesetItem *parent = m_items[parentId];
|
cannam@45
|
182 QGraphicsLineItem *line = new QGraphicsLineItem;
|
cannam@45
|
183 line->setLine(me->x() + 25, me->y() + 50,
|
cannam@45
|
184 parent->x() + 25, parent->y());
|
cannam@45
|
185 m_scene->addItem(line);
|
cannam@45
|
186 }
|
Chris@44
|
187 }
|
Chris@44
|
188 }
|
Chris@44
|
189
|