comparison grapher.cpp @ 53:3c46b2ac45d3

* Put proper labels &c in changeset items; colour branches and users; etc
author Chris Cannam
date Fri, 12 Nov 2010 16:48:18 +0000
parents 384420567575
children 261bfb9481fe
comparison
equal deleted inserted replaced
52:384420567575 53:3c46b2ac45d3
1 1
2 #include "grapher.h" 2 #include "grapher.h"
3 #include "connectionitem.h" 3 #include "connectionitem.h"
4 #include "dateitem.h"
4 5
5 #include <QGraphicsScene> 6 #include <QGraphicsScene>
6 7
7 #include <iostream> 8 #include <iostream>
8 9
41 if (!m_items.contains(id)) { 42 if (!m_items.contains(id)) {
42 throw LayoutException(QString("Changeset %1 not in item map").arg(id)); 43 throw LayoutException(QString("Changeset %1 not in item map").arg(id));
43 } 44 }
44 Changeset *cs = m_changesets[id]; 45 Changeset *cs = m_changesets[id];
45 ChangesetItem *item = m_items[id]; 46 ChangesetItem *item = m_items[id];
46 std::cerr << "Looking at " << id.toStdString() << std::endl; 47 std::cerr << "layoutRow: Looking at " << id.toStdString() << std::endl;
47 48
48 int row = 0; 49 int row = 0;
49 int nparents = cs->parents().size(); 50 int nparents = cs->parents().size();
50 51
51 if (nparents > 0) { 52 if (nparents > 0) {
71 // row is now an upper bound on our eventual row (because we want 72 // row is now an upper bound on our eventual row (because we want
72 // to be above all parents). But we also want to ensure we are 73 // to be above all parents). But we also want to ensure we are
73 // above all nodes that have earlier dates (to the nearest day). 74 // above all nodes that have earlier dates (to the nearest day).
74 // m_rowDates maps each row to a date: use that. 75 // m_rowDates maps each row to a date: use that.
75 76
76 QString date = cs->date(); 77 QString date = cs->age();
77 78 while (m_rowDates.contains(row) && m_rowDates[row] != date) {
78 // n.b. this relies on the fact that the date component of an ISO
79 // date/time sorts correctly in a dictionary sort
80 while (m_rowDates.contains(row) && m_rowDates[row] < date) {
81 --row; 79 --row;
82 } 80 }
83 81
84 // We have already laid out all nodes that have earlier timestamps 82 // We have already laid out all nodes that have earlier timestamps
85 // than this one, so we know (among other things) that we can 83 // than this one, so we know (among other things) that we can
108 throw LayoutException(QString("Changeset %1 not in ID map").arg(id)); 106 throw LayoutException(QString("Changeset %1 not in ID map").arg(id));
109 } 107 }
110 if (!m_items.contains(id)) { 108 if (!m_items.contains(id)) {
111 throw LayoutException(QString("Changeset %1 not in item map").arg(id)); 109 throw LayoutException(QString("Changeset %1 not in item map").arg(id));
112 } 110 }
111
113 Changeset *cs = m_changesets[id]; 112 Changeset *cs = m_changesets[id];
113 std::cerr << "layoutCol: Looking at " << id.toStdString() << std::endl;
114
114 ChangesetItem *item = m_items[id]; 115 ChangesetItem *item = m_items[id];
115 std::cerr << "Looking at " << id.toStdString() << std::endl;
116
117 foreach (QString parentId, cs->parents()) {
118 if (!m_changesets.contains(parentId)) continue;
119 if (!m_handled.contains(parentId)) {
120 layoutCol(parentId);
121 }
122 }
123
124 // Parent may have layed out child in the recursive call
125 if (m_handled.contains(id)) {
126 std::cerr << "Looks like we've dealt with " << id.toStdString() << std::endl;
127 return;
128 }
129 116
130 int col = 0; 117 int col = 0;
131 int row = item->row(); 118 int row = item->row();
132 QString branch = cs->branch(); 119 QString branch = cs->branch();
133 120
189 // Normally the children will lay out themselves, but we can do 176 // Normally the children will lay out themselves, but we can do
190 // a better job in some special cases: 177 // a better job in some special cases:
191 178
192 int nchildren = cs->children().size(); 179 int nchildren = cs->children().size();
193 180
194 // look for merging children and make sure nobody 181 // look for merging children and children distant from us but in a
195 // is going to overwrite their "merge lines" if they extend further 182 // straight line, and make sure nobody is going to overwrite their
196 // than a single step 183 // connection lines
197 184
198 foreach (QString childId, cs->children()) { 185 foreach (QString childId, cs->children()) {
199 if (!m_changesets.contains(childId)) continue; 186 if (!m_changesets.contains(childId)) continue;
200 Changeset *child = m_changesets[childId]; 187 Changeset *child = m_changesets[childId];
201 if (child->parents().size() > 1) { 188 int childRow = m_items[childId]->row();
202 int childRow = m_items[childId]->row(); 189 if (child->parents().size() > 1 || child->branch() == cs->branch()) {
203 for (int r = row; r > childRow; --r) { 190 for (int r = row; r > childRow; --r) {
204 m_alloc[r].insert(col); 191 m_alloc[r].insert(col);
205 } 192 }
206 } 193 }
207 } 194 }
208 195
209 // look for the case where exactly two children have the same 196 // look for the case where exactly two children have the same
210 // branch as us: split them to a little either side of our position 197 // branch as us: split them to a little either side of our position
211 198
222 } 209 }
223 if (special.size() == 2) { 210 if (special.size() == 2) {
224 for (int i = 0; i < 2; ++i) { 211 for (int i = 0; i < 2; ++i) {
225 int off = i * 2 - 1; // 0 -> -1, 1 -> 1 212 int off = i * 2 - 1; // 0 -> -1, 1 -> 1
226 ChangesetItem *it = m_items[special[i]]; 213 ChangesetItem *it = m_items[special[i]];
214 m_alloc[it->row()].insert(col); // avoid our column
227 it->setColumn(findAvailableColumn(it->row(), col + off, true)); 215 it->setColumn(findAvailableColumn(it->row(), col + off, true));
228 m_alloc[it->row()].insert(it->column()); 216 for (int r = row; r >= it->row(); --r) {
217 m_alloc[r].insert(it->column());
218 }
229 m_handled.insert(special[i]); 219 m_handled.insert(special[i]);
230 } 220 }
231 } 221 }
232 } 222 }
233 } 223 }
277 if (m_branchHomes.contains(other)) { 267 if (m_branchHomes.contains(other)) {
278 taken.insert(m_branchHomes[other]); 268 taken.insert(m_branchHomes[other]);
279 } 269 }
280 } 270 }
281 } 271 }
282 int home = 3; 272 int home = 2;
283 while (taken.contains(home)) { 273 while (taken.contains(home)) {
284 if (home > 0) home = -home; 274 if (home > 0) {
285 else home = -(home-3); 275 if (home % 2 == 1) {
276 home = -home;
277 } else {
278 home = home + 1;
279 }
280 } else {
281 if ((-home) % 2 == 1) {
282 home = home + 1;
283 } else {
284 home = -(home-2);
285 }
286 }
286 } 287 }
287 m_branchHomes[branch] = home; 288 m_branchHomes[branch] = home;
288 } 289 }
289 290
290 foreach (QString branch, m_branchRanges.keys()) { 291 foreach (QString branch, m_branchRanges.keys()) {
294 295
295 static bool 296 static bool
296 compareChangesetsByDate(Changeset *const &a, Changeset *const &b) 297 compareChangesetsByDate(Changeset *const &a, Changeset *const &b)
297 { 298 {
298 return a->timestamp() < b->timestamp(); 299 return a->timestamp() < b->timestamp();
300 }
301
302 ChangesetItem *
303 Grapher::getItemFor(Changeset *cs)
304 {
305 if (!cs || !m_items.contains(cs->id())) return 0;
306 return m_items[cs->id()];
299 } 307 }
300 308
301 void 309 void
302 Grapher::layout(Changesets csets) 310 Grapher::layout(Changesets csets)
303 { 311 {
304 m_changesets.clear(); 312 m_changesets.clear();
305 m_items.clear(); 313 m_items.clear();
306 m_alloc.clear(); 314 m_alloc.clear();
307 m_branchHomes.clear(); 315 m_branchHomes.clear();
316
317 if (csets.empty()) return;
308 318
309 foreach (Changeset *cs, csets) { 319 foreach (Changeset *cs, csets) {
310 320
311 QString id = cs->id(); 321 QString id = cs->id();
312 std::cerr << id.toStdString() << std::endl; 322 std::cerr << id.toStdString() << std::endl;
328 } 338 }
329 339
330 foreach (Changeset *cs, csets) { 340 foreach (Changeset *cs, csets) {
331 QString id = cs->id(); 341 QString id = cs->id();
332 ChangesetItem *item = m_items[id]; 342 ChangesetItem *item = m_items[id];
343 bool merge = (cs->parents().size() > 1);
333 foreach (QString parentId, cs->parents()) { 344 foreach (QString parentId, cs->parents()) {
334 if (!m_changesets.contains(parentId)) continue; 345 if (!m_changesets.contains(parentId)) continue;
335 Changeset *parent = m_changesets[parentId]; 346 Changeset *parent = m_changesets[parentId];
336 parent->addChild(id); 347 parent->addChild(id);
337 ConnectionItem *conn = new ConnectionItem(); 348 ConnectionItem *conn = new ConnectionItem();
349 if (merge) conn->setConnectionType(ConnectionItem::Merge);
338 conn->setChild(item); 350 conn->setChild(item);
339 conn->setParent(m_items[parentId]); 351 conn->setParent(m_items[parentId]);
340 m_scene->addItem(conn); 352 m_scene->addItem(conn);
341 } 353 }
342 } 354 }
348 // guaranteed. So, sort explicitly using the date comparator 360 // guaranteed. So, sort explicitly using the date comparator
349 // above 361 // above
350 362
351 qStableSort(csets.begin(), csets.end(), compareChangesetsByDate); 363 qStableSort(csets.begin(), csets.end(), compareChangesetsByDate);
352 364
365 foreach (Changeset *cs, csets) {
366 std::cerr << "id " << cs->id().toStdString() << ", ts " << cs->timestamp() << ", date " << cs->datetime().toStdString() << std::endl;
367 }
368
353 m_handled.clear(); 369 m_handled.clear();
354 for (int i = csets.size() - 1; i >= 0; --i) { 370 foreach (Changeset *cs, csets) {
355 layoutRow(csets[i]->id()); 371 layoutRow(cs->id());
356 } 372 }
357 373
358 allocateBranchHomes(csets); 374 allocateBranchHomes(csets);
359 375
360 m_handled.clear(); 376 m_handled.clear();
361 for (int i = csets.size() - 1; i >= 0; --i) { 377 foreach (Changeset *cs, csets) {
362 layoutCol(csets[i]->id()); 378 foreach (QString parentId, cs->parents()) {
363 } 379 if (!m_handled.contains(parentId) &&
364 } 380 m_changesets.contains(parentId)) {
365 381 layoutCol(parentId);
382 }
383 }
384 layoutCol(cs->id());
385 }
386
387 // we know that 0 is an upper bound on row, and that mincol must
388 // be <= 0 and maxcol >= 0, so these initial values are good
389 int minrow = 0, maxrow = 0;
390 int mincol = 0, maxcol = 0;
391
392 foreach (int r, m_alloc.keys()) {
393 if (r < minrow) minrow = r;
394 if (r > maxrow) maxrow = r;
395 ColumnSet &c = m_alloc[r];
396 foreach (int i, c) {
397 if (i < mincol) mincol = i;
398 if (i > maxcol) maxcol = i;
399 }
400 }
401
402 QString prevDate;
403 int changeRow = 0;
404
405 bool even = false;
406 int n = 0;
407
408 for (int row = minrow; row <= maxrow; ++row) {
409
410 QString date = m_rowDates[row];
411 n++;
412
413 if (date != prevDate) {
414 if (prevDate != "") {
415 DateItem *item = new DateItem();
416 item->setDateString(prevDate);
417 item->setCols(mincol, maxcol - mincol + 1);
418 item->setRows(changeRow, n);
419 item->setEven(even);
420 item->setZValue(-1);
421 m_scene->addItem(item);
422 even = !even;
423 }
424 prevDate = date;
425 changeRow = row;
426 n = 0;
427 }
428 }
429
430 if (n > 0) {
431 DateItem *item = new DateItem();
432 item->setDateString(prevDate);
433 item->setCols(mincol, maxcol - mincol + 1);
434 item->setRows(changeRow, n+1);
435 item->setEven(even);
436 item->setZValue(-1);
437 m_scene->addItem(item);
438 even = !even;
439 }
440 }
441