Mercurial > hg > easyhg
comparison grapher.cpp @ 145:644bd31e8301
* Include the uncommitted item in general graph layout (in case it is not at the head, when other items will need to avoid it)
author | Chris Cannam |
---|---|
date | Wed, 01 Dec 2010 17:41:14 +0000 |
parents | f61f032b06f9 |
children | 38faf16df9b6 |
comparison
equal
deleted
inserted
replaced
143:f61f032b06f9 | 145:644bd31e8301 |
---|---|
25 | 25 |
26 int Grapher::findAvailableColumn(int row, int parent, bool preferParentCol) | 26 int Grapher::findAvailableColumn(int row, int parent, bool preferParentCol) |
27 { | 27 { |
28 int col = parent; | 28 int col = parent; |
29 if (preferParentCol) { | 29 if (preferParentCol) { |
30 if (!m_alloc[row].contains(col)) { | 30 if (isAvailable(row, col)) return col; |
31 return col; | |
32 } | |
33 } | 31 } |
34 while (col > 0) { | 32 while (col > 0) { |
35 if (!m_alloc[row].contains(--col)) return col; | 33 if (isAvailable(row, --col)) return col; |
36 } | 34 } |
37 while (col < 0) { | 35 while (col < 0) { |
38 if (!m_alloc[row].contains(++col)) return col; | 36 if (isAvailable(row, ++col)) return col; |
39 } | 37 } |
40 col = parent; | 38 col = parent; |
41 int sign = (col < 0 ? -1 : 1); | 39 int sign = (col < 0 ? -1 : 1); |
42 while (1) { | 40 while (1) { |
43 col += sign; | 41 col += sign; |
44 if (!m_alloc[row].contains(col)) return col; | 42 if (isAvailable(row, col)) return col; |
45 } | 43 } |
44 } | |
45 | |
46 bool Grapher::isAvailable(int row, int col) | |
47 { | |
48 if (m_alloc.contains(row) && m_alloc[row].contains(col)) return false; | |
49 if (!m_haveAllocatedUncommittedColumn) return true; | |
50 if (!m_uncommitted) return true; | |
51 return !(row <= m_uncommittedParentRow && col == m_uncommitted->column()); | |
46 } | 52 } |
47 | 53 |
48 void Grapher::layoutRow(QString id) | 54 void Grapher::layoutRow(QString id) |
49 { | 55 { |
50 if (m_handled.contains(id)) { | 56 if (m_handled.contains(id)) { |
98 // safely fill in this row has having this date, if it isn't in | 104 // safely fill in this row has having this date, if it isn't in |
99 // the map yet (it cannot have an earlier date) | 105 // the map yet (it cannot have an earlier date) |
100 | 106 |
101 if (!m_rowDates.contains(row)) { | 107 if (!m_rowDates.contains(row)) { |
102 m_rowDates[row] = date; | 108 m_rowDates[row] = date; |
109 } | |
110 | |
111 // If we're the parent of the uncommitted item, make a note of our | |
112 // row (we need it later, to avoid overwriting the connecting line) | |
113 if (m_uncommittedParentId == id) { | |
114 m_uncommittedParentRow = row; | |
103 } | 115 } |
104 | 116 |
105 DEBUG << "putting " << cs->id().toStdString() << " at row " << row | 117 DEBUG << "putting " << cs->id().toStdString() << " at row " << row |
106 << endl; | 118 << endl; |
107 | 119 |
183 DEBUG << "putting " << cs->id().toStdString() << " at col " << col << endl; | 195 DEBUG << "putting " << cs->id().toStdString() << " at col " << col << endl; |
184 | 196 |
185 m_alloc[row].insert(col); | 197 m_alloc[row].insert(col); |
186 item->setColumn(col); | 198 item->setColumn(col); |
187 m_handled.insert(id); | 199 m_handled.insert(id); |
200 | |
201 // If we're the parent of the uncommitted item, it should be given | |
202 // the same column as us (ideally) | |
203 | |
204 if (m_uncommittedParentId == id) { | |
205 int ucol = findAvailableColumn(row-1, col, true); | |
206 m_uncommitted->setColumn(ucol); | |
207 m_haveAllocatedUncommittedColumn = true; | |
208 } | |
188 | 209 |
189 // Normally the children will lay out themselves, but we can do | 210 // Normally the children will lay out themselves, but we can do |
190 // a better job in some special cases: | 211 // a better job in some special cases: |
191 | 212 |
192 int nchildren = cs->children().size(); | 213 int nchildren = cs->children().size(); |
304 DEBUG << branch.toStdString() << ": " << m_branchRanges[branch].first << " - " << m_branchRanges[branch].second << ", home " << m_branchHomes[branch] << endl; | 325 DEBUG << branch.toStdString() << ": " << m_branchRanges[branch].first << " - " << m_branchRanges[branch].second << ", home " << m_branchHomes[branch] << endl; |
305 } | 326 } |
306 } | 327 } |
307 | 328 |
308 static bool | 329 static bool |
309 compareChangesetsByDate(Changeset *const &a, Changeset *const &b) | 330 compareChangesetsByDate(Changeset *const &a, Changeset *const &b) |
310 { | 331 { |
311 return a->timestamp() < b->timestamp(); | 332 return a->timestamp() < b->timestamp(); |
312 } | 333 } |
313 | 334 |
314 ChangesetItem * | 335 ChangesetItem * |
315 Grapher::getItemFor(Changeset *cs) | 336 Grapher::getItemFor(Changeset *cs) |
316 { | 337 { |
317 if (!cs || !m_items.contains(cs->id())) return 0; | 338 if (!cs || !m_items.contains(cs->id())) return 0; |
318 return m_items[cs->id()]; | 339 return m_items[cs->id()]; |
319 } | 340 } |
320 | 341 |
321 void Grapher::layout(Changesets csets) | 342 void Grapher::layout(Changesets csets, QString uncommittedSproutsFrom) |
322 { | 343 { |
323 m_changesets.clear(); | 344 m_changesets.clear(); |
324 m_items.clear(); | 345 m_items.clear(); |
325 m_alloc.clear(); | 346 m_alloc.clear(); |
326 m_branchHomes.clear(); | 347 m_branchHomes.clear(); |
327 | 348 |
349 m_uncommittedParentId = uncommittedSproutsFrom; | |
350 m_haveAllocatedUncommittedColumn = false; | |
351 m_uncommittedParentRow = 0; | |
352 m_uncommitted = 0; | |
353 | |
328 DEBUG << "Grapher::layout: Have " << csets.size() << " changesets" << endl; | 354 DEBUG << "Grapher::layout: Have " << csets.size() << " changesets" << endl; |
329 | 355 |
330 if (csets.empty()) return; | 356 if (csets.empty()) return; |
331 | 357 |
358 // Create (but don't yet position) the changeset items | |
359 | |
332 foreach (Changeset *cs, csets) { | 360 foreach (Changeset *cs, csets) { |
333 | 361 |
334 QString id = cs->id(); | 362 QString id = cs->id(); |
335 // DEBUG << id.toStdString() << endl; | |
336 | 363 |
337 if (id == "") { | 364 if (id == "") { |
338 throw LayoutException("Changeset has no ID"); | 365 throw LayoutException("Changeset has no ID"); |
339 } | 366 } |
340 if (m_changesets.contains(id)) { | 367 if (m_changesets.contains(id)) { |
341 DEBUG << "Duplicate changeset ID " << id << " in Grapher::layout()" << endl; | 368 DEBUG << "Duplicate changeset ID " << id |
369 << " in Grapher::layout()" << endl; | |
342 throw LayoutException(QString("Duplicate changeset ID %1").arg(id)); | 370 throw LayoutException(QString("Duplicate changeset ID %1").arg(id)); |
343 } | 371 } |
344 | 372 |
345 m_changesets[id] = cs; | 373 m_changesets[id] = cs; |
346 | 374 |
366 conn->setChild(item); | 394 conn->setChild(item); |
367 conn->setParent(m_items[parentId]); | 395 conn->setParent(m_items[parentId]); |
368 m_scene->addItem(conn); | 396 m_scene->addItem(conn); |
369 } | 397 } |
370 } | 398 } |
399 | |
400 // Add uncommitted item and connecting line as necessary | |
401 | |
402 if (m_uncommittedParentId != "") { | |
403 m_uncommitted = new UncommittedItem(); | |
404 m_scene->addItem(m_uncommitted); | |
405 ConnectionItem *conn = new ConnectionItem(); | |
406 conn->setParent(m_items[m_uncommittedParentId]); | |
407 conn->setChild(m_uncommitted); | |
408 m_scene->addItem(conn); | |
409 } | |
371 | 410 |
372 // Add the branch labels | 411 // Add the branch labels |
412 | |
373 foreach (Changeset *cs, csets) { | 413 foreach (Changeset *cs, csets) { |
374 QString id = cs->id(); | 414 QString id = cs->id(); |
375 ChangesetItem *item = m_items[id]; | 415 ChangesetItem *item = m_items[id]; |
376 bool haveChildOnSameBranch = false; | 416 bool haveChildOnSameBranch = false; |
377 foreach (QString childId, cs->children()) { | 417 foreach (QString childId, cs->children()) { |
392 // above | 432 // above |
393 | 433 |
394 qStableSort(csets.begin(), csets.end(), compareChangesetsByDate); | 434 qStableSort(csets.begin(), csets.end(), compareChangesetsByDate); |
395 | 435 |
396 foreach (Changeset *cs, csets) { | 436 foreach (Changeset *cs, csets) { |
397 DEBUG << "id " << cs->id().toStdString() << ", ts " << cs->timestamp() << ", date " << cs->datetime().toStdString() << endl; | 437 DEBUG << "id " << cs->id().toStdString() << ", ts " << cs->timestamp() |
438 << ", date " << cs->datetime().toStdString() << endl; | |
398 } | 439 } |
399 | 440 |
400 m_handled.clear(); | 441 m_handled.clear(); |
401 foreach (Changeset *cs, csets) { | 442 foreach (Changeset *cs, csets) { |
402 layoutRow(cs->id()); | 443 layoutRow(cs->id()); |
413 } | 454 } |
414 } | 455 } |
415 layoutCol(cs->id()); | 456 layoutCol(cs->id()); |
416 } | 457 } |
417 | 458 |
418 foreach (Changeset *cs, csets) { | 459 // Find row and column extents. We know that 0 is an upper bound |
419 ChangesetItem *item = m_items[cs->id()]; | 460 // on row, and that mincol must be <= 0 and maxcol >= 0, so these |
420 if (!m_alloc[item->row()].contains(item->column()-1) && | 461 // initial values are good |
421 !m_alloc[item->row()].contains(item->column()+1)) { | 462 |
422 item->setWide(true); | |
423 } | |
424 } | |
425 | |
426 // we know that 0 is an upper bound on row, and that mincol must | |
427 // be <= 0 and maxcol >= 0, so these initial values are good | |
428 int minrow = 0, maxrow = 0; | 463 int minrow = 0, maxrow = 0; |
429 int mincol = 0, maxcol = 0; | 464 int mincol = 0, maxcol = 0; |
430 | 465 |
431 foreach (int r, m_alloc.keys()) { | 466 foreach (int r, m_alloc.keys()) { |
432 if (r < minrow) minrow = r; | 467 if (r < minrow) minrow = r; |
436 if (i < mincol) mincol = i; | 471 if (i < mincol) mincol = i; |
437 if (i > maxcol) maxcol = i; | 472 if (i > maxcol) maxcol = i; |
438 } | 473 } |
439 } | 474 } |
440 | 475 |
476 // We've given the uncommitted item a column, but not a row yet -- | |
477 // it always goes at the top | |
478 | |
479 if (m_uncommitted) { | |
480 --minrow; | |
481 DEBUG << "putting uncommitted item at row " << minrow << endl; | |
482 m_uncommitted->setRow(minrow); | |
483 } | |
484 | |
485 // Changeset items that have nothing to either side of them can be | |
486 // made double-width | |
487 | |
488 foreach (Changeset *cs, csets) { | |
489 ChangesetItem *item = m_items[cs->id()]; | |
490 if (isAvailable(item->row(), item->column()-1) && | |
491 isAvailable(item->row(), item->column()+1)) { | |
492 item->setWide(true); | |
493 } | |
494 } | |
495 | |
496 if (m_uncommitted) { | |
497 if (isAvailable(m_uncommitted->row(), m_uncommitted->column()-1) && | |
498 isAvailable(m_uncommitted->row(), m_uncommitted->column()+1)) { | |
499 m_uncommitted->setWide(true); | |
500 } | |
501 } | |
502 | |
441 QString prevDate; | 503 QString prevDate; |
442 int changeRow = 0; | 504 int changeRow = 0; |
443 | 505 |
444 bool even = false; | 506 bool even = false; |
445 int n = 0; | 507 int n = 0; |
508 | |
509 if (mincol == maxcol) { | |
510 --mincol; | |
511 ++maxcol; | |
512 } | |
446 | 513 |
447 for (int row = minrow; row <= maxrow; ++row) { | 514 for (int row = minrow; row <= maxrow; ++row) { |
448 | 515 |
449 QString date = m_rowDates[row]; | 516 QString date = m_rowDates[row]; |
450 n++; | 517 n++; |