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++; |
