Mercurial > hg > easyhg
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 |