annotate grapher.cpp @ 45:4286836bb3c9

* Some more work on graph layout; ensure LANG is set for parseable UTF8 output when running Hg
author Chris Cannam <cannam@all-day-breakfast.com>
date Wed, 10 Nov 2010 12:44:11 +0000
parents bed7ab59f62e
children bd3accba9b3f
rev   line source
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