comparison grapher.cpp @ 106:729438d70af8

* Retrieve and store current branch and heads; some refactoring
author Chris Cannam
date Thu, 25 Nov 2010 17:54:35 +0000
parents 10eb97683aa9
children 4bd17f36d059
comparison
equal deleted inserted replaced
105:1928f9b408e6 106:729438d70af8
21 21
22 #include <QGraphicsScene> 22 #include <QGraphicsScene>
23 23
24 #include <iostream> 24 #include <iostream>
25 25
26 int 26 int Grapher::findAvailableColumn(int row, int parent, bool preferParentCol)
27 Grapher::findAvailableColumn(int row, int parent, bool preferParentCol)
28 { 27 {
29 int col = parent; 28 int col = parent;
30 if (preferParentCol) { 29 if (preferParentCol) {
31 if (!m_alloc[row].contains(col)) { 30 if (!m_alloc[row].contains(col)) {
32 return col; 31 return col;
33 } 32 }
34 } 33 }
35 while (col > 0) { 34 while (col > 0) {
36 if (!m_alloc[row].contains(--col)) return col; 35 if (!m_alloc[row].contains(--col)) return col;
37 } 36 }
38 while (col < 0) { 37 while (col < 0) {
39 if (!m_alloc[row].contains(++col)) return col; 38 if (!m_alloc[row].contains(++col)) return col;
40 } 39 }
41 col = parent; 40 col = parent;
42 int sign = (col < 0 ? -1 : 1); 41 int sign = (col < 0 ? -1 : 1);
43 while (1) { 42 while (1) {
44 col += sign; 43 col += sign;
45 if (!m_alloc[row].contains(col)) return col; 44 if (!m_alloc[row].contains(col)) return col;
46 } 45 }
47 } 46 }
48 47
49 void 48 void Grapher::layoutRow(QString id)
50 Grapher::layoutRow(QString id)
51 { 49 {
52 if (m_handled.contains(id)) { 50 if (m_handled.contains(id)) {
53 return; 51 return;
54 } 52 }
55 if (!m_changesets.contains(id)) { 53 if (!m_changesets.contains(id)) {
56 throw LayoutException(QString("Changeset %1 not in ID map").arg(id)); 54 throw LayoutException(QString("Changeset %1 not in ID map").arg(id));
57 } 55 }
58 if (!m_items.contains(id)) { 56 if (!m_items.contains(id)) {
59 throw LayoutException(QString("Changeset %1 not in item map").arg(id)); 57 throw LayoutException(QString("Changeset %1 not in item map").arg(id));
60 } 58 }
61 Changeset *cs = m_changesets[id]; 59 Changeset *cs = m_changesets[id];
62 ChangesetItem *item = m_items[id]; 60 ChangesetItem *item = m_items[id];
63 std::cerr << "layoutRow: Looking at " << id.toStdString() << std::endl; 61 std::cerr << "layoutRow: Looking at " << id.toStdString() << std::endl;
64 62
65 int row = 0; 63 int row = 0;
66 int nparents = cs->parents().size(); 64 int nparents = cs->parents().size();
67 65
68 if (nparents > 0) { 66 if (nparents > 0) {
69 bool haveRow = false; 67 bool haveRow = false;
70 foreach (QString parentId, cs->parents()) { 68 foreach (QString parentId, cs->parents()) {
71 69
72 if (!m_changesets.contains(parentId)) continue; 70 if (!m_changesets.contains(parentId)) continue;
73 if (!m_items.contains(parentId)) continue; 71 if (!m_items.contains(parentId)) continue;
74 72
75 if (!m_handled.contains(parentId)) { 73 if (!m_handled.contains(parentId)) {
76 layoutRow(parentId); 74 layoutRow(parentId);
77 } 75 }
78 76
79 ChangesetItem *parentItem = m_items[parentId]; 77 ChangesetItem *parentItem = m_items[parentId];
80 if (!haveRow || parentItem->row() < row) { 78 if (!haveRow || parentItem->row() < row) {
81 row = parentItem->row(); 79 row = parentItem->row();
82 haveRow = true; 80 haveRow = true;
83 } 81 }
84 } 82 }
85 row = row - 1; 83 row = row - 1;
86 } 84 }
87 85
88 // row is now an upper bound on our eventual row (because we want 86 // row is now an upper bound on our eventual row (because we want
89 // to be above all parents). But we also want to ensure we are 87 // to be above all parents). But we also want to ensure we are
90 // above all nodes that have earlier dates (to the nearest day). 88 // above all nodes that have earlier dates (to the nearest day).
91 // m_rowDates maps each row to a date: use that. 89 // m_rowDates maps each row to a date: use that.
92 90
93 QString date = cs->age(); 91 QString date = cs->age();
94 while (m_rowDates.contains(row) && m_rowDates[row] != date) { 92 while (m_rowDates.contains(row) && m_rowDates[row] != date) {
95 --row; 93 --row;
96 } 94 }
97 95
98 // We have already laid out all nodes that have earlier timestamps 96 // We have already laid out all nodes that have earlier timestamps
99 // than this one, so we know (among other things) that we can 97 // than this one, so we know (among other things) that we can
100 // safely fill in this row has having this date, if it isn't in 98 // safely fill in this row has having this date, if it isn't in
101 // the map yet (it cannot have an earlier date) 99 // the map yet (it cannot have an earlier date)
102 100
103 if (!m_rowDates.contains(row)) { 101 if (!m_rowDates.contains(row)) {
104 m_rowDates[row] = date; 102 m_rowDates[row] = date;
105 } 103 }
106 104
107 std::cerr << "putting " << cs->id().toStdString() << " at row " << row 105 std::cerr << "putting " << cs->id().toStdString() << " at row " << row
108 << std::endl; 106 << std::endl;
109 107
110 item->setRow(row); 108 item->setRow(row);
111 m_handled.insert(id); 109 m_handled.insert(id);
112 } 110 }
113 111
114 void 112 void Grapher::layoutCol(QString id)
115 Grapher::layoutCol(QString id)
116 { 113 {
117 if (m_handled.contains(id)) { 114 if (m_handled.contains(id)) {
118 std::cerr << "Already looked at " << id.toStdString() << std::endl; 115 std::cerr << "Already looked at " << id.toStdString() << std::endl;
119 return; 116 return;
120 } 117 }
121 if (!m_changesets.contains(id)) { 118 if (!m_changesets.contains(id)) {
122 throw LayoutException(QString("Changeset %1 not in ID map").arg(id)); 119 throw LayoutException(QString("Changeset %1 not in ID map").arg(id));
123 } 120 }
124 if (!m_items.contains(id)) { 121 if (!m_items.contains(id)) {
125 throw LayoutException(QString("Changeset %1 not in item map").arg(id)); 122 throw LayoutException(QString("Changeset %1 not in item map").arg(id));
126 } 123 }
127 124
128 Changeset *cs = m_changesets[id]; 125 Changeset *cs = m_changesets[id];
129 std::cerr << "layoutCol: Looking at " << id.toStdString() << std::endl; 126 std::cerr << "layoutCol: Looking at " << id.toStdString() << std::endl;
130 127
139 int parentsOnSameBranch = 0; 136 int parentsOnSameBranch = 0;
140 137
141 switch (nparents) { 138 switch (nparents) {
142 139
143 case 0: 140 case 0:
144 col = m_branchHomes[cs->branch()]; 141 col = m_branchHomes[cs->branch()];
145 col = findAvailableColumn(row, col, true); 142 col = findAvailableColumn(row, col, true);
146 break; 143 break;
147 144
148 case 1: 145 case 1:
149 parentId = cs->parents()[0]; 146 parentId = cs->parents()[0];
150 147
151 if (!m_changesets.contains(parentId) || 148 if (!m_changesets.contains(parentId) ||
152 m_changesets[parentId]->branch() != branch) { 149 m_changesets[parentId]->branch() != branch) {
153 // new branch 150 // new branch
154 col = m_branchHomes[branch]; 151 col = m_branchHomes[branch];
155 } else { 152 } else {
156 col = m_items[parentId]->column(); 153 col = m_items[parentId]->column();
157 } 154 }
158 155
159 col = findAvailableColumn(row, col, true); 156 col = findAvailableColumn(row, col, true);
160 break; 157 break;
161 158
162 case 2: 159 case 2:
163 // a merge: behave differently if parents are both on the same 160 // a merge: behave differently if parents are both on the same
164 // branch (we also want to behave differently for nodes that 161 // branch (we also want to behave differently for nodes that
165 // have multiple children on the same branch -- spreading them 162 // have multiple children on the same branch -- spreading them
166 // out rather than having affinity to a specific branch) 163 // out rather than having affinity to a specific branch)
167 164
168 foreach (QString parentId, cs->parents()) { 165 foreach (QString parentId, cs->parents()) {
169 if (!m_changesets.contains(parentId)) continue; 166 if (!m_changesets.contains(parentId)) continue;
170 if (m_changesets[parentId]->branch() == branch) { 167 if (m_changesets[parentId]->branch() == branch) {
171 ChangesetItem *parentItem = m_items[parentId]; 168 ChangesetItem *parentItem = m_items[parentId];
172 col += parentItem->column(); 169 col += parentItem->column();
173 parentsOnSameBranch++; 170 parentsOnSameBranch++;
174 } 171 }
175 } 172 }
176 173
177 if (parentsOnSameBranch > 0) { 174 if (parentsOnSameBranch > 0) {
178 col /= parentsOnSameBranch; 175 col /= parentsOnSameBranch;
179 col = findAvailableColumn(item->row(), col, true); 176 col = findAvailableColumn(item->row(), col, true);
180 } else { 177 } else {
181 col = findAvailableColumn(item->row(), col, false); 178 col = findAvailableColumn(item->row(), col, false);
182 } 179 }
183 break; 180 break;
184 } 181 }
185 182
186 std::cerr << "putting " << cs->id().toStdString() << " at col " << col << std::endl; 183 std::cerr << "putting " << cs->id().toStdString() << " at col " << col << std::endl;
187 184
188 m_alloc[row].insert(col); 185 m_alloc[row].insert(col);
199 // connection lines 196 // connection lines
200 197
201 foreach (QString childId, cs->children()) { 198 foreach (QString childId, cs->children()) {
202 if (!m_changesets.contains(childId)) continue; 199 if (!m_changesets.contains(childId)) continue;
203 Changeset *child = m_changesets[childId]; 200 Changeset *child = m_changesets[childId];
204 int childRow = m_items[childId]->row(); 201 int childRow = m_items[childId]->row();
205 if (child->parents().size() > 1 || 202 if (child->parents().size() > 1 ||
206 child->branch() == cs->branch()) { 203 child->branch() == cs->branch()) {
207 for (int r = row-1; r > childRow; --r) { 204 for (int r = row-1; r > childRow; --r) {
208 m_alloc[r].insert(col); 205 m_alloc[r].insert(col);
209 } 206 }
210 } 207 }
211 } 208 }
212 209
213 // look for the case where exactly two children have the same 210 // look for the case where exactly two children have the same
214 // branch as us: split them to a little either side of our position 211 // branch as us: split them to a little either side of our position
215 212
216 if (nchildren > 1) { 213 if (nchildren > 1) {
217 214
218 QList<QString> special; 215 QList<QString> special;
219 foreach (QString childId, cs->children()) { 216 foreach (QString childId, cs->children()) {
220 if (!m_changesets.contains(childId)) continue; 217 if (!m_changesets.contains(childId)) continue;
221 Changeset *child = m_changesets[childId]; 218 Changeset *child = m_changesets[childId];
222 if (child->branch() == branch && 219 if (child->branch() == branch &&
223 child->parents().size() == 1) { 220 child->parents().size() == 1) {
224 special.push_back(childId); 221 special.push_back(childId);
225 } 222 }
226 } 223 }
227 if (special.size() == 2) { 224 if (special.size() == 2) {
228 for (int i = 0; i < 2; ++i) { 225 for (int i = 0; i < 2; ++i) {
229 int off = i * 2 - 1; // 0 -> -1, 1 -> 1 226 int off = i * 2 - 1; // 0 -> -1, 1 -> 1
230 ChangesetItem *it = m_items[special[i]]; 227 ChangesetItem *it = m_items[special[i]];
231 it->setColumn(findAvailableColumn(it->row(), col + off, true)); 228 it->setColumn(findAvailableColumn(it->row(), col + off, true));
232 for (int r = row-1; r >= it->row(); --r) { 229 for (int r = row-1; r >= it->row(); --r) {
233 m_alloc[r].insert(it->column()); 230 m_alloc[r].insert(it->column());
234 } 231 }
235 m_handled.insert(special[i]); 232 m_handled.insert(special[i]);
236 } 233 }
237 } 234 }
238 } 235 }
239 } 236 }
240 237
241 bool 238 bool Grapher::rangesConflict(const Range &r1, const Range &r2)
242 Grapher::rangesConflict(const Range &r1, const Range &r2)
243 { 239 {
244 // allow some additional space at edges. we really ought also to 240 // allow some additional space at edges. we really ought also to
245 // permit space at the end of a branch up to the point where the 241 // permit space at the end of a branch up to the point where the
246 // merge happens 242 // merge happens
247 int a1 = r1.first - 2, b1 = r1.second + 2; 243 int a1 = r1.first - 2, b1 = r1.second + 2;
249 if (a1 > b2 || b1 < a2) return false; 245 if (a1 > b2 || b1 < a2) return false;
250 if (a2 > b1 || b2 < a1) return false; 246 if (a2 > b1 || b2 < a1) return false;
251 return true; 247 return true;
252 } 248 }
253 249
254 void 250 void Grapher::allocateBranchHomes(Changesets csets)
255 Grapher::allocateBranchHomes(Changesets csets) 251 {
256 { 252 foreach (Changeset *cs, csets) {
257 foreach (Changeset *cs, csets) { 253 QString branch = cs->branch();
258 QString branch = cs->branch(); 254 ChangesetItem *item = m_items[cs->id()];
259 ChangesetItem *item = m_items[cs->id()]; 255 if (!item) continue;
260 if (!item) continue; 256 int row = item->row();
261 int row = item->row(); 257 if (!m_branchRanges.contains(branch)) {
262 if (!m_branchRanges.contains(branch)) { 258 m_branchRanges[branch] = Range(row, row);
263 m_branchRanges[branch] = Range(row, row); 259 } else {
264 } else { 260 Range p = m_branchRanges[branch];
265 Range p = m_branchRanges[branch]; 261 if (row < p.first) p.first = row;
266 if (row < p.first) p.first = row; 262 if (row > p.second) p.second = row;
267 if (row > p.second) p.second = row; 263 m_branchRanges[branch] = p;
268 m_branchRanges[branch] = p; 264 }
269 }
270 } 265 }
271 266
272 m_branchHomes[""] = 0; 267 m_branchHomes[""] = 0;
273 268
274 foreach (QString branch, m_branchRanges.keys()) { 269 foreach (QString branch, m_branchRanges.keys()) {
275 if (branch == "") continue; 270 if (branch == "") continue;
276 QSet<int> taken; 271 QSet<int> taken;
277 taken.insert(0); 272 taken.insert(0);
278 Range myRange = m_branchRanges[branch]; 273 Range myRange = m_branchRanges[branch];
279 foreach (QString other, m_branchRanges.keys()) { 274 foreach (QString other, m_branchRanges.keys()) {
280 if (other == branch || other == "") continue; 275 if (other == branch || other == "") continue;
281 Range otherRange = m_branchRanges[other]; 276 Range otherRange = m_branchRanges[other];
282 if (rangesConflict(myRange, otherRange)) { 277 if (rangesConflict(myRange, otherRange)) {
283 if (m_branchHomes.contains(other)) { 278 if (m_branchHomes.contains(other)) {
284 taken.insert(m_branchHomes[other]); 279 taken.insert(m_branchHomes[other]);
285 } 280 }
286 } 281 }
287 } 282 }
288 int home = 2; 283 int home = 2;
289 while (taken.contains(home)) { 284 while (taken.contains(home)) {
290 if (home > 0) { 285 if (home > 0) {
291 if (home % 2 == 1) { 286 if (home % 2 == 1) {
292 home = -home; 287 home = -home;
293 } else { 288 } else {
294 home = home + 1; 289 home = home + 1;
295 } 290 }
296 } else { 291 } else {
297 if ((-home) % 2 == 1) { 292 if ((-home) % 2 == 1) {
298 home = home + 1; 293 home = home + 1;
299 } else { 294 } else {
300 home = -(home-2); 295 home = -(home-2);
301 } 296 }
302 } 297 }
303 } 298 }
304 m_branchHomes[branch] = home; 299 m_branchHomes[branch] = home;
305 } 300 }
306 301
307 foreach (QString branch, m_branchRanges.keys()) { 302 foreach (QString branch, m_branchRanges.keys()) {
308 std::cerr << branch.toStdString() << ": " << m_branchRanges[branch].first << " - " << m_branchRanges[branch].second << ", home " << m_branchHomes[branch] << std::endl; 303 std::cerr << branch.toStdString() << ": " << m_branchRanges[branch].first << " - " << m_branchRanges[branch].second << ", home " << m_branchHomes[branch] << std::endl;
309 } 304 }
310 } 305 }
311 306
312 static bool 307 static bool
313 compareChangesetsByDate(Changeset *const &a, Changeset *const &b) 308 compareChangesetsByDate(Changeset *const &a, Changeset *const &b)
314 { 309 {
315 return a->timestamp() < b->timestamp(); 310 return a->timestamp() < b->timestamp();
316 } 311 }
317 312
318 ChangesetItem * 313 ChangesetItem *
319 Grapher::getItemFor(Changeset *cs) 314 Grapher::getItemFor(Changeset *cs)
320 { 315 {
321 if (!cs || !m_items.contains(cs->id())) return 0; 316 if (!cs || !m_items.contains(cs->id())) return 0;
322 return m_items[cs->id()]; 317 return m_items[cs->id()];
323 } 318 }
324 319
325 void 320 void Grapher::layout(Changesets csets)
326 Grapher::layout(Changesets csets)
327 { 321 {
328 m_changesets.clear(); 322 m_changesets.clear();
329 m_items.clear(); 323 m_items.clear();
330 m_alloc.clear(); 324 m_alloc.clear();
331 m_branchHomes.clear(); 325 m_branchHomes.clear();
332 326
333 if (csets.empty()) return; 327 if (csets.empty()) return;
334 328
335 foreach (Changeset *cs, csets) { 329 foreach (Changeset *cs, csets) {
336 330
337 QString id = cs->id(); 331 QString id = cs->id();
338 std::cerr << id.toStdString() << std::endl; 332 std::cerr << id.toStdString() << std::endl;
339 333
340 if (id == "") { 334 if (id == "") {
341 throw LayoutException("Changeset has no ID"); 335 throw LayoutException("Changeset has no ID");
342 } 336 }
343 if (m_changesets.contains(id)) { 337 if (m_changesets.contains(id)) {
344 throw LayoutException(QString("Duplicate changeset ID %1").arg(id)); 338 throw LayoutException(QString("Duplicate changeset ID %1").arg(id));
345 } 339 }
346 340
347 m_changesets[id] = cs; 341 m_changesets[id] = cs;
348 342
349 ChangesetItem *item = new ChangesetItem(cs); 343 ChangesetItem *item = new ChangesetItem(cs);
350 item->setX(0); 344 item->setX(0);
351 item->setY(0); 345 item->setY(0);
352 m_items[id] = item; 346 m_items[id] = item;
353 m_scene->addItem(item); 347 m_scene->addItem(item);
354 } 348 }
355 349
356 // Add the connecting lines 350 // Add the connecting lines
357 351
358 foreach (Changeset *cs, csets) { 352 foreach (Changeset *cs, csets) {
359 QString id = cs->id(); 353 QString id = cs->id();
360 ChangesetItem *item = m_items[id]; 354 ChangesetItem *item = m_items[id];
361 bool merge = (cs->parents().size() > 1); 355 bool merge = (cs->parents().size() > 1);
362 foreach (QString parentId, cs->parents()) { 356 foreach (QString parentId, cs->parents()) {
363 if (!m_changesets.contains(parentId)) continue; 357 if (!m_changesets.contains(parentId)) continue;
364 Changeset *parent = m_changesets[parentId]; 358 Changeset *parent = m_changesets[parentId];
365 parent->addChild(id); 359 parent->addChild(id);
366 ConnectionItem *conn = new ConnectionItem(); 360 ConnectionItem *conn = new ConnectionItem();
367 if (merge) conn->setConnectionType(ConnectionItem::Merge); 361 if (merge) conn->setConnectionType(ConnectionItem::Merge);
368 conn->setChild(item); 362 conn->setChild(item);
369 conn->setParent(m_items[parentId]); 363 conn->setParent(m_items[parentId]);
370 m_scene->addItem(conn); 364 m_scene->addItem(conn);
371 } 365 }
372 } 366 }
373 367
374 // Add the branch labels 368 // Add the branch labels
375 foreach (Changeset *cs, csets) { 369 foreach (Changeset *cs, csets) {
376 QString id = cs->id(); 370 QString id = cs->id();
394 // above 388 // above
395 389
396 qStableSort(csets.begin(), csets.end(), compareChangesetsByDate); 390 qStableSort(csets.begin(), csets.end(), compareChangesetsByDate);
397 391
398 foreach (Changeset *cs, csets) { 392 foreach (Changeset *cs, csets) {
399 std::cerr << "id " << cs->id().toStdString() << ", ts " << cs->timestamp() << ", date " << cs->datetime().toStdString() << std::endl; 393 std::cerr << "id " << cs->id().toStdString() << ", ts " << cs->timestamp() << ", date " << cs->datetime().toStdString() << std::endl;
400 } 394 }
401 395
402 m_handled.clear(); 396 m_handled.clear();
403 foreach (Changeset *cs, csets) { 397 foreach (Changeset *cs, csets) {
404 layoutRow(cs->id()); 398 layoutRow(cs->id());
405 } 399 }
406 400
407 allocateBranchHomes(csets); 401 allocateBranchHomes(csets);
408 402
409 m_handled.clear(); 403 m_handled.clear();
410 foreach (Changeset *cs, csets) { 404 foreach (Changeset *cs, csets) {
411 foreach (QString parentId, cs->parents()) { 405 foreach (QString parentId, cs->parents()) {
412 if (!m_handled.contains(parentId) && 406 if (!m_handled.contains(parentId) &&
413 m_changesets.contains(parentId)) { 407 m_changesets.contains(parentId)) {
414 layoutCol(parentId); 408 layoutCol(parentId);
415 } 409 }
416 } 410 }
417 layoutCol(cs->id()); 411 layoutCol(cs->id());
418 } 412 }
419 413
420 foreach (Changeset *cs, csets) { 414 foreach (Changeset *cs, csets) {
421 ChangesetItem *item = m_items[cs->id()]; 415 ChangesetItem *item = m_items[cs->id()];
422 if (!m_alloc[item->row()].contains(item->column()-1) && 416 if (!m_alloc[item->row()].contains(item->column()-1) &&
423 !m_alloc[item->row()].contains(item->column()+1)) { 417 !m_alloc[item->row()].contains(item->column()+1)) {
424 item->setWide(true); 418 item->setWide(true);
425 } 419 }
426 } 420 }
427 421
428 // we know that 0 is an upper bound on row, and that mincol must 422 // we know that 0 is an upper bound on row, and that mincol must
429 // be <= 0 and maxcol >= 0, so these initial values are good 423 // be <= 0 and maxcol >= 0, so these initial values are good
430 int minrow = 0, maxrow = 0; 424 int minrow = 0, maxrow = 0;
431 int mincol = 0, maxcol = 0; 425 int mincol = 0, maxcol = 0;
432 426
433 foreach (int r, m_alloc.keys()) { 427 foreach (int r, m_alloc.keys()) {
434 if (r < minrow) minrow = r; 428 if (r < minrow) minrow = r;
435 if (r > maxrow) maxrow = r; 429 if (r > maxrow) maxrow = r;
436 ColumnSet &c = m_alloc[r]; 430 ColumnSet &c = m_alloc[r];
437 foreach (int i, c) { 431 foreach (int i, c) {
438 if (i < mincol) mincol = i; 432 if (i < mincol) mincol = i;
439 if (i > maxcol) maxcol = i; 433 if (i > maxcol) maxcol = i;
440 } 434 }
441 } 435 }
442 436
443 QString prevDate; 437 QString prevDate;
444 int changeRow = 0; 438 int changeRow = 0;
445 439
446 bool even = false; 440 bool even = false;
447 int n = 0; 441 int n = 0;
448 442
449 for (int row = minrow; row <= maxrow; ++row) { 443 for (int row = minrow; row <= maxrow; ++row) {
450 444
451 QString date = m_rowDates[row]; 445 QString date = m_rowDates[row];
452 n++; 446 n++;
453 447
454 if (date != prevDate) { 448 if (date != prevDate) {
455 if (prevDate != "") { 449 if (prevDate != "") {
456 DateItem *item = new DateItem(); 450 DateItem *item = new DateItem();
457 item->setDateString(prevDate); 451 item->setDateString(prevDate);
458 item->setCols(mincol, maxcol - mincol + 1); 452 item->setCols(mincol, maxcol - mincol + 1);
459 item->setRows(changeRow, n); 453 item->setRows(changeRow, n);
460 item->setEven(even); 454 item->setEven(even);
461 item->setZValue(-1); 455 item->setZValue(-1);
462 m_scene->addItem(item); 456 m_scene->addItem(item);
463 even = !even; 457 even = !even;
464 } 458 }
465 prevDate = date; 459 prevDate = date;
466 changeRow = row; 460 changeRow = row;
467 n = 0; 461 n = 0;
468 } 462 }
469 } 463 }
470 464
471 if (n > 0) { 465 if (n > 0) {
472 DateItem *item = new DateItem(); 466 DateItem *item = new DateItem();
473 item->setDateString(prevDate); 467 item->setDateString(prevDate);
474 item->setCols(mincol, maxcol - mincol + 1); 468 item->setCols(mincol, maxcol - mincol + 1);
475 item->setRows(changeRow, n+1); 469 item->setRows(changeRow, n+1);
476 item->setEven(even); 470 item->setEven(even);
477 item->setZValue(-1); 471 item->setZValue(-1);
478 m_scene->addItem(item); 472 m_scene->addItem(item);
479 even = !even; 473 even = !even;
480 } 474 }
481 } 475 }
482 476