comparison layer/RegionLayer.cpp @ 1459:42c87368287c

Merge from branch single-point
author Chris Cannam
date Fri, 17 May 2019 10:02:52 +0100
parents 5b9692768beb
children 696e569ff21b
comparison
equal deleted inserted replaced
1441:8d5bf4ab98ef 1459:42c87368287c
259 m_distributionMap.clear(); 259 m_distributionMap.clear();
260 if (!m_model) return; 260 if (!m_model) return;
261 261
262 // SVDEBUG << "RegionLayer::recalcSpacing" << endl; 262 // SVDEBUG << "RegionLayer::recalcSpacing" << endl;
263 263
264 for (RegionModel::PointList::const_iterator i = m_model->getPoints().begin(); 264 EventVector allEvents = m_model->getAllEvents();
265 i != m_model->getPoints().end(); ++i) { 265 for (const Event &e: allEvents) {
266 m_distributionMap[i->value]++; 266 m_distributionMap[e.getValue()]++;
267 // SVDEBUG << "RegionLayer::recalcSpacing: value found: " << i->value << " (now have " << m_distributionMap[i->value] << " of this value)" << endl; 267 // SVDEBUG << "RegionLayer::recalcSpacing: value found: " << e.getValue() << " (now have " << m_distributionMap[e.getValue()] << " of this value)" << endl;
268 } 268 }
269 269
270 int n = 0; 270 int n = 0;
271 271
272 for (SpacingMap::const_iterator i = m_distributionMap.begin(); 272 for (SpacingMap::const_iterator i = m_distributionMap.begin();
301 max = m_model->getValueMaximum(); 301 max = m_model->getValueMaximum();
302 302
303 return true; 303 return true;
304 } 304 }
305 305
306 RegionModel::PointList 306 EventVector
307 RegionLayer::getLocalPoints(LayerGeometryProvider *v, int x) const 307 RegionLayer::getLocalPoints(LayerGeometryProvider *v, int x) const
308 { 308 {
309 if (!m_model) return RegionModel::PointList(); 309 if (!m_model) return EventVector();
310 310
311 sv_frame_t frame = v->getFrameForX(x); 311 sv_frame_t frame = v->getFrameForX(x);
312 312
313 RegionModel::PointList onPoints = 313 EventVector local = m_model->getEventsCovering(frame);
314 m_model->getPoints(frame); 314 if (!local.empty()) return local;
315 315
316 if (!onPoints.empty()) { 316 int fuzz = ViewManager::scalePixelSize(2);
317 return onPoints; 317 sv_frame_t start = v->getFrameForX(x - fuzz);
318 } 318 sv_frame_t end = v->getFrameForX(x + fuzz);
319 319
320 RegionModel::PointList prevPoints = 320 local = m_model->getEventsStartingWithin(frame, end - frame);
321 m_model->getPreviousPoints(frame); 321 if (!local.empty()) return local;
322 RegionModel::PointList nextPoints = 322
323 m_model->getNextPoints(frame); 323 local = m_model->getEventsSpanning(start, frame - start);
324 324 if (!local.empty()) return local;
325 RegionModel::PointList usePoints = prevPoints; 325
326 326 return {};
327 if (prevPoints.empty()) {
328 usePoints = nextPoints;
329 } else if (long(prevPoints.begin()->frame) < v->getStartFrame() &&
330 !(nextPoints.begin()->frame > v->getEndFrame())) {
331 usePoints = nextPoints;
332 } else if (long(nextPoints.begin()->frame) - frame <
333 frame - long(prevPoints.begin()->frame)) {
334 usePoints = nextPoints;
335 }
336
337 if (!usePoints.empty()) {
338 int fuzz = ViewManager::scalePixelSize(2);
339 int px = v->getXForFrame(usePoints.begin()->frame);
340 if ((px > x && px - x > fuzz) ||
341 (px < x && x - px > fuzz + 1)) {
342 usePoints.clear();
343 }
344 }
345
346 return usePoints;
347 } 327 }
348 328
349 bool 329 bool
350 RegionLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, RegionModel::Point &p) const 330 RegionLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &point) const
351 { 331 {
352 if (!m_model) return false; 332 if (!m_model) return false;
353 333
354 sv_frame_t frame = v->getFrameForX(x); 334 sv_frame_t frame = v->getFrameForX(x);
355 335
356 RegionModel::PointList onPoints = m_model->getPoints(frame); 336 EventVector onPoints = m_model->getEventsCovering(frame);
357 if (onPoints.empty()) return false; 337 if (onPoints.empty()) return false;
358 338
359 int nearestDistance = -1; 339 int nearestDistance = -1;
360 340 for (const auto &p: onPoints) {
361 for (RegionModel::PointList::const_iterator i = onPoints.begin(); 341 int distance = getYForValue(v, p.getValue()) - y;
362 i != onPoints.end(); ++i) {
363
364 int distance = getYForValue(v, (*i).value) - y;
365 if (distance < 0) distance = -distance; 342 if (distance < 0) distance = -distance;
366 if (nearestDistance == -1 || distance < nearestDistance) { 343 if (nearestDistance == -1 || distance < nearestDistance) {
367 nearestDistance = distance; 344 nearestDistance = distance;
368 p = *i; 345 point = p;
369 } 346 }
370 } 347 }
371 348
372 return true; 349 return true;
373 } 350 }
374 351
375 QString 352 QString
376 RegionLayer::getLabelPreceding(sv_frame_t frame) const 353 RegionLayer::getLabelPreceding(sv_frame_t frame) const
377 { 354 {
378 if (!m_model) return ""; 355 if (!m_model) return "";
379 RegionModel::PointList points = m_model->getPreviousPoints(frame); 356 EventVector points = m_model->getEventsStartingWithin
380 for (RegionModel::PointList::const_iterator i = points.begin(); 357 (m_model->getStartFrame(), frame - m_model->getStartFrame());
381 i != points.end(); ++i) { 358 if (!points.empty()) {
382 if (i->label != "") return i->label; 359 for (auto i = points.rbegin(); i != points.rend(); ++i) {
383 } 360 if (i->getLabel() != QString()) {
384 return ""; 361 return i->getLabel();
362 }
363 }
364 }
365 return QString();
385 } 366 }
386 367
387 QString 368 QString
388 RegionLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const 369 RegionLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
389 { 370 {
390 int x = pos.x(); 371 int x = pos.x();
391 372
392 if (!m_model || !m_model->getSampleRate()) return ""; 373 if (!m_model || !m_model->getSampleRate()) return "";
393 374
394 RegionModel::PointList points = getLocalPoints(v, x); 375 EventVector points = getLocalPoints(v, x);
395 376
396 if (points.empty()) { 377 if (points.empty()) {
397 if (!m_model->isReady()) { 378 if (!m_model->isReady()) {
398 return tr("In progress"); 379 return tr("In progress");
399 } else { 380 } else {
400 return tr("No local points"); 381 return tr("No local points");
401 } 382 }
402 } 383 }
403 384
404 RegionRec region(0); 385 Event region;
405 RegionModel::PointList::iterator i; 386 EventVector::iterator i;
406 387
407 //!!! harmonise with whatever decision is made about point y 388 //!!! harmonise with whatever decision is made about point y
408 //!!! coords in paint method 389 //!!! coords in paint method
409 390
410 for (i = points.begin(); i != points.end(); ++i) { 391 for (i = points.begin(); i != points.end(); ++i) {
411 392
412 int y = getYForValue(v, i->value); 393 int y = getYForValue(v, i->getValue());
413 int h = 3; 394 int h = 3;
414 395
415 if (m_model->getValueQuantization() != 0.0) { 396 if (m_model->getValueQuantization() != 0.0) {
416 h = y - getYForValue(v, i->value + m_model->getValueQuantization()); 397 h = y - getYForValue
398 (v, i->getValue() + m_model->getValueQuantization());
417 if (h < 3) h = 3; 399 if (h < 3) h = 3;
418 } 400 }
419 401
420 if (pos.y() >= y - h && pos.y() <= y) { 402 if (pos.y() >= y - h && pos.y() <= y) {
421 region = *i; 403 region = *i;
423 } 405 }
424 } 406 }
425 407
426 if (i == points.end()) return tr("No local points"); 408 if (i == points.end()) return tr("No local points");
427 409
428 RealTime rt = RealTime::frame2RealTime(region.frame, 410 RealTime rt = RealTime::frame2RealTime(region.getFrame(),
429 m_model->getSampleRate()); 411 m_model->getSampleRate());
430 RealTime rd = RealTime::frame2RealTime(region.duration, 412 RealTime rd = RealTime::frame2RealTime(region.getDuration(),
431 m_model->getSampleRate()); 413 m_model->getSampleRate());
432 414
433 QString valueText; 415 QString valueText;
434 416
435 valueText = tr("%1 %2").arg(region.value).arg(getScaleUnits()); 417 valueText = tr("%1 %2").arg(region.getValue()).arg(getScaleUnits());
436 418
437 QString text; 419 QString text;
438 420
439 if (region.label == "") { 421 if (region.getLabel() == "") {
440 text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nNo label")) 422 text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nNo label"))
441 .arg(rt.toText(true).c_str()) 423 .arg(rt.toText(true).c_str())
442 .arg(valueText) 424 .arg(valueText)
443 .arg(rd.toText(true).c_str()); 425 .arg(rd.toText(true).c_str());
444 } else { 426 } else {
445 text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nLabel:\t%4")) 427 text = QString(tr("Time:\t%1\nValue:\t%2\nDuration:\t%3\nLabel:\t%4"))
446 .arg(rt.toText(true).c_str()) 428 .arg(rt.toText(true).c_str())
447 .arg(valueText) 429 .arg(valueText)
448 .arg(rd.toText(true).c_str()) 430 .arg(rd.toText(true).c_str())
449 .arg(region.label); 431 .arg(region.getLabel());
450 } 432 }
451 433
452 pos = QPoint(v->getXForFrame(region.frame), 434 pos = QPoint(v->getXForFrame(region.getFrame()),
453 getYForValue(v, region.value)); 435 getYForValue(v, region.getValue()));
454 return text; 436 return text;
455 } 437 }
456 438
457 bool 439 bool
458 RegionLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, 440 RegionLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
461 { 443 {
462 if (!m_model) { 444 if (!m_model) {
463 return Layer::snapToFeatureFrame(v, frame, resolution, snap); 445 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
464 } 446 }
465 447
448 // SnapLeft / SnapRight: return frame of nearest feature in that
449 // direction no matter how far away
450 //
451 // SnapNeighbouring: return frame of feature that would be used in
452 // an editing operation, i.e. closest feature in either direction
453 // but only if it is "close enough"
454
466 resolution = m_model->getResolution(); 455 resolution = m_model->getResolution();
467 RegionModel::PointList points;
468 456
469 if (snap == SnapNeighbouring) { 457 if (snap == SnapNeighbouring) {
470 458 EventVector points = getLocalPoints(v, v->getXForFrame(frame));
471 points = getLocalPoints(v, v->getXForFrame(frame));
472 if (points.empty()) return false; 459 if (points.empty()) return false;
473 frame = points.begin()->frame; 460 frame = points.begin()->getFrame();
474 return true; 461 return true;
475 } 462 }
476 463
477 points = m_model->getPoints(frame, frame); 464 // Normally we snap to the start frame of whichever event we
478 sv_frame_t snapped = frame; 465 // find. However here, for SnapRight only, if the end frame of
479 bool found = false; 466 // whichever event we would have snapped to had we been snapping
480 467 // left is closer than the start frame of the next event to the
481 for (RegionModel::PointList::const_iterator i = points.begin(); 468 // right, then we snap to that frame instead. Clear?
482 i != points.end(); ++i) { 469
483 470 Event left;
484 if (snap == SnapRight) { 471 bool haveLeft = false;
485 472 if (m_model->getNearestEventMatching
486 // The best frame to snap to is the end frame of whichever 473 (frame, [](Event) { return true; }, EventSeries::Backward, left)) {
487 // feature we would have snapped to the start frame of if 474 haveLeft = true;
488 // we had been snapping left. 475 }
489 476
490 if (i->frame <= frame) { 477 if (snap == SnapLeft) {
491 if (i->frame + i->duration > frame) { 478 frame = left.getFrame();
492 snapped = i->frame + i->duration; 479 return haveLeft;
493 found = true; // don't break, as the next may be better 480 }
481
482 Event right;
483 bool haveRight = false;
484 if (m_model->getNearestEventMatching
485 (frame, [](Event) { return true; }, EventSeries::Forward, right)) {
486 haveRight = true;
487 }
488
489 if (haveLeft) {
490 sv_frame_t leftEnd = left.getFrame() + left.getDuration();
491 if (leftEnd > frame) {
492 if (haveRight) {
493 if (leftEnd - frame < right.getFrame() - frame) {
494 frame = leftEnd;
495 } else {
496 frame = right.getFrame();
494 } 497 }
495 } else { 498 } else {
496 if (!found) { 499 frame = leftEnd;
497 snapped = i->frame;
498 found = true;
499 }
500 break;
501 } 500 }
502 501 return true;
503 } else if (snap == SnapLeft) { 502 }
504 503 }
505 if (i->frame <= frame) { 504
506 snapped = i->frame; 505 if (haveRight) {
507 found = true; // don't break, as the next may be better 506 frame = right.getFrame();
508 } else { 507 return true;
509 break; 508 }
510 } 509
511 510 return false;
512 } else { // nearest
513
514 RegionModel::PointList::const_iterator j = i;
515 ++j;
516
517 if (j == points.end()) {
518
519 snapped = i->frame;
520 found = true;
521 break;
522
523 } else if (j->frame >= frame) {
524
525 if (j->frame - frame < frame - i->frame) {
526 snapped = j->frame;
527 } else {
528 snapped = i->frame;
529 }
530 found = true;
531 break;
532 }
533 }
534 }
535
536 frame = snapped;
537 return found;
538 } 511 }
539 512
540 bool 513 bool
541 RegionLayer::snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame, 514 RegionLayer::snapToSimilarFeature(LayerGeometryProvider *v, sv_frame_t &frame,
542 int &resolution, 515 int &resolution,
544 { 517 {
545 if (!m_model) { 518 if (!m_model) {
546 return Layer::snapToSimilarFeature(v, frame, resolution, snap); 519 return Layer::snapToSimilarFeature(v, frame, resolution, snap);
547 } 520 }
548 521
522 // snap is only permitted to be SnapLeft or SnapRight here. We
523 // don't do the same trick as in snapToFeatureFrame, of snapping
524 // to the end of a feature sometimes.
525
549 resolution = m_model->getResolution(); 526 resolution = m_model->getResolution();
550 527
551 const RegionModel::PointList &points = m_model->getPoints(); 528 Event ref;
552 RegionModel::PointList close = m_model->getPoints(frame, frame); 529 Event e;
553 530 float matchvalue;
554 RegionModel::PointList::const_iterator i; 531 bool found;
555 532
556 sv_frame_t matchframe = frame; 533 found = m_model->getNearestEventMatching
557 double matchvalue = 0.f; 534 (frame, [](Event) { return true; }, EventSeries::Backward, ref);
558 535
559 for (i = close.begin(); i != close.end(); ++i) { 536 if (!found) {
560 if (i->frame > frame) break; 537 return false;
561 matchvalue = i->value; 538 }
562 matchframe = i->frame; 539
563 } 540 matchvalue = ref.getValue();
564 541
565 sv_frame_t snapped = frame; 542 found = m_model->getNearestEventMatching
566 bool found = false; 543 (frame,
567 bool distant = false; 544 [matchvalue](Event e) {
568 double epsilon = 0.0001; 545 double epsilon = 0.0001;
569 546 return fabs(e.getValue() - matchvalue) < epsilon;
570 i = close.begin(); 547 },
571 548 snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward,
572 // Scan through the close points first, then the more distant ones 549 e);
573 // if no suitable close one is found. So the while-termination 550
574 // condition here can only happen once i has passed through the 551 if (!found) {
575 // whole of the close container and then the whole of the separate 552 return false;
576 // points container. The two iterators are totally distinct, but 553 }
577 // have the same type so we cheekily use the same variable and a 554
578 // single loop for both. 555 frame = e.getFrame();
579 556 return true;
580 while (i != points.end()) {
581
582 if (!distant) {
583 if (i == close.end()) {
584 // switch from the close container to the points container
585 i = points.begin();
586 distant = true;
587 }
588 }
589
590 if (snap == SnapRight) {
591
592 if (i->frame > matchframe &&
593 fabs(i->value - matchvalue) < epsilon) {
594 snapped = i->frame;
595 found = true;
596 break;
597 }
598
599 } else if (snap == SnapLeft) {
600
601 if (i->frame < matchframe) {
602 if (fabs(i->value - matchvalue) < epsilon) {
603 snapped = i->frame;
604 found = true; // don't break, as the next may be better
605 }
606 } else if (found || distant) {
607 break;
608 }
609
610 } else {
611 // no other snap types supported
612 }
613
614 ++i;
615 }
616
617 frame = snapped;
618 return found;
619 } 557 }
620 558
621 QString 559 QString
622 RegionLayer::getScaleUnits() const 560 RegionLayer::getScaleUnits() const
623 { 561 {
880 int x0 = rect.left() - 40, x1 = rect.right(); 818 int x0 = rect.left() - 40, x1 = rect.right();
881 819
882 sv_frame_t wholeFrame0 = v->getFrameForX(0); 820 sv_frame_t wholeFrame0 = v->getFrameForX(0);
883 sv_frame_t wholeFrame1 = v->getFrameForX(v->getPaintWidth()); 821 sv_frame_t wholeFrame1 = v->getFrameForX(v->getPaintWidth());
884 822
885 RegionModel::PointList points(m_model->getPoints(wholeFrame0, wholeFrame1)); 823 EventVector points(m_model->getEventsSpanning(wholeFrame0,
824 wholeFrame1 - wholeFrame0));
886 if (points.empty()) return; 825 if (points.empty()) return;
887 826
888 paint.setPen(getBaseQColor()); 827 paint.setPen(getBaseQColor());
889 828
890 QColor brushColour(getBaseQColor()); 829 QColor brushColour(getBaseQColor());
896 double min = m_model->getValueMinimum(); 835 double min = m_model->getValueMinimum();
897 double max = m_model->getValueMaximum(); 836 double max = m_model->getValueMaximum();
898 if (max == min) max = min + 1.0; 837 if (max == min) max = min + 1.0;
899 838
900 QPoint localPos; 839 QPoint localPos;
901 RegionModel::Point illuminatePoint(0); 840 Event illuminatePoint(0);
902 bool shouldIlluminate = false; 841 bool shouldIlluminate = false;
903 842
904 if (v->shouldIlluminateLocalFeatures(this, localPos)) { 843 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
905 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(), 844 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
906 illuminatePoint); 845 illuminatePoint);
915 //!!! if it does have distinct values, we should still ensure y 854 //!!! if it does have distinct values, we should still ensure y
916 //!!! coord is never completely flat on the top or bottom 855 //!!! coord is never completely flat on the top or bottom
917 856
918 int fontHeight = paint.fontMetrics().height(); 857 int fontHeight = paint.fontMetrics().height();
919 858
920 for (RegionModel::PointList::const_iterator i = points.begin(); 859 for (EventVector::const_iterator i = points.begin();
921 i != points.end(); ++i) { 860 i != points.end(); ++i) {
922 861
923 const RegionModel::Point &p(*i); 862 const Event &p(*i);
924 863
925 int x = v->getXForFrame(p.frame); 864 int x = v->getXForFrame(p.getFrame());
926 int w = v->getXForFrame(p.frame + p.duration) - x; 865 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x;
927 int y = getYForValue(v, p.value); 866 int y = getYForValue(v, p.getValue());
928 int h = 9; 867 int h = 9;
929 int ex = x + w; 868 int ex = x + w;
930 869
931 int gap = v->scalePixelSize(2); 870 int gap = v->scalePixelSize(2);
932 871
933 RegionModel::PointList::const_iterator j = i; 872 EventVector::const_iterator j = i;
934 ++j; 873 ++j;
935 874
936 if (j != points.end()) { 875 if (j != points.end()) {
937 const RegionModel::Point &q(*j); 876 const Event &q(*j);
938 int nx = v->getXForFrame(q.frame); 877 int nx = v->getXForFrame(q.getFrame());
939 if (nx < ex) ex = nx; 878 if (nx < ex) ex = nx;
940 } 879 }
941 880
942 if (m_model->getValueQuantization() != 0.0) { 881 if (m_model->getValueQuantization() != 0.0) {
943 h = y - getYForValue(v, p.value + m_model->getValueQuantization()); 882 h = y - getYForValue
883 (v, p.getValue() + m_model->getValueQuantization());
944 if (h < 3) h = 3; 884 if (h < 3) h = 3;
945 } 885 }
946 886
947 if (w < 1) w = 1; 887 if (w < 1) w = 1;
948 888
949 if (m_plotStyle == PlotSegmentation) { 889 if (m_plotStyle == PlotSegmentation) {
950 paint.setPen(getForegroundQColor(v->getView())); 890 paint.setPen(getForegroundQColor(v->getView()));
951 paint.setBrush(getColourForValue(v, p.value)); 891 paint.setBrush(getColourForValue(v, p.getValue()));
952 } else { 892 } else {
953 paint.setPen(getBaseQColor()); 893 paint.setPen(getBaseQColor());
954 paint.setBrush(brushColour); 894 paint.setBrush(brushColour);
955 } 895 }
956 896
957 if (m_plotStyle == PlotSegmentation) { 897 if (m_plotStyle == PlotSegmentation) {
958 898
959 if (ex <= x) continue; 899 if (ex <= x) continue;
960 900
961 if (!shouldIlluminate || 901 if (!shouldIlluminate || illuminatePoint != p) {
962 // "illuminatePoint != p"
963 RegionModel::Point::Comparator()(illuminatePoint, p) ||
964 RegionModel::Point::Comparator()(p, illuminatePoint)) {
965 902
966 paint.setPen(QPen(getForegroundQColor(v->getView()), 1)); 903 paint.setPen(QPen(getForegroundQColor(v->getView()), 1));
967 paint.drawLine(x, 0, x, v->getPaintHeight()); 904 paint.drawLine(x, 0, x, v->getPaintHeight());
968 paint.setPen(Qt::NoPen); 905 paint.setPen(Qt::NoPen);
969 906
973 910
974 paint.drawRect(x, -1, ex - x, v->getPaintHeight() + gap); 911 paint.drawRect(x, -1, ex - x, v->getPaintHeight() + gap);
975 912
976 } else { 913 } else {
977 914
978 if (shouldIlluminate && 915 if (shouldIlluminate && illuminatePoint == p) {
979 // "illuminatePoint == p"
980 !RegionModel::Point::Comparator()(illuminatePoint, p) &&
981 !RegionModel::Point::Comparator()(p, illuminatePoint)) {
982 916
983 paint.setPen(v->getForeground()); 917 paint.setPen(v->getForeground());
984 paint.setBrush(v->getForeground()); 918 paint.setBrush(v->getForeground());
985 919
986 QString vlabel = QString("%1%2").arg(p.value).arg(getScaleUnits()); 920 QString vlabel =
921 QString("%1%2").arg(p.getValue()).arg(getScaleUnits());
987 PaintAssistant::drawVisibleText(v, paint, 922 PaintAssistant::drawVisibleText(v, paint,
988 x - paint.fontMetrics().width(vlabel) - gap, 923 x - paint.fontMetrics().width(vlabel) - gap,
989 y + paint.fontMetrics().height()/2 924 y + paint.fontMetrics().height()/2
990 - paint.fontMetrics().descent(), 925 - paint.fontMetrics().descent(),
991 vlabel, PaintAssistant::OutlinedText); 926 vlabel, PaintAssistant::OutlinedText);
992 927
993 QString hlabel = RealTime::frame2RealTime 928 QString hlabel = RealTime::frame2RealTime
994 (p.frame, m_model->getSampleRate()).toText(true).c_str(); 929 (p.getFrame(), m_model->getSampleRate()).toText(true).c_str();
995 PaintAssistant::drawVisibleText(v, paint, 930 PaintAssistant::drawVisibleText(v, paint,
996 x, 931 x,
997 y - h/2 - paint.fontMetrics().descent() - gap, 932 y - h/2 - paint.fontMetrics().descent() - gap,
998 hlabel, PaintAssistant::OutlinedText); 933 hlabel, PaintAssistant::OutlinedText);
999 } 934 }
1006 } 941 }
1007 942
1008 int nextLabelMinX = -100; 943 int nextLabelMinX = -100;
1009 int lastLabelY = 0; 944 int lastLabelY = 0;
1010 945
1011 for (RegionModel::PointList::const_iterator i = points.begin(); 946 for (EventVector::const_iterator i = points.begin();
1012 i != points.end(); ++i) { 947 i != points.end(); ++i) {
1013 948
1014 const RegionModel::Point &p(*i); 949 const Event &p(*i);
1015 950
1016 int x = v->getXForFrame(p.frame); 951 int x = v->getXForFrame(p.getFrame());
1017 int w = v->getXForFrame(p.frame + p.duration) - x; 952 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x;
1018 int y = getYForValue(v, p.value); 953 int y = getYForValue(v, p.getValue());
1019 954
1020 QString label = p.label; 955 QString label = p.getLabel();
1021 if (label == "") { 956 if (label == "") {
1022 label = QString("%1%2").arg(p.value).arg(getScaleUnits()); 957 label = QString("%1%2").arg(p.getValue()).arg(getScaleUnits());
1023 } 958 }
1024 int labelWidth = paint.fontMetrics().width(label); 959 int labelWidth = paint.fontMetrics().width(label);
1025 960
1026 int gap = v->scalePixelSize(2); 961 int gap = v->scalePixelSize(2);
1027 962
1036 } 971 }
1037 972
1038 bool illuminated = false; 973 bool illuminated = false;
1039 974
1040 if (m_plotStyle != PlotSegmentation) { 975 if (m_plotStyle != PlotSegmentation) {
1041 976 if (shouldIlluminate && illuminatePoint == p) {
1042 if (shouldIlluminate &&
1043 // "illuminatePoint == p"
1044 !RegionModel::Point::Comparator()(illuminatePoint, p) &&
1045 !RegionModel::Point::Comparator()(p, illuminatePoint)) {
1046
1047 illuminated = true; 977 illuminated = true;
1048 } 978 }
1049 } 979 }
1050 980
1051 if (!illuminated) { 981 if (!illuminated) {
1099 } 1029 }
1100 1030
1101 void 1031 void
1102 RegionLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const 1032 RegionLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const
1103 { 1033 {
1104 if (!m_model || m_model->getPoints().empty()) return; 1034 if (!m_model || m_model->isEmpty()) return;
1105 1035
1106 QString unit; 1036 QString unit;
1107 double min, max; 1037 double min, max;
1108 bool logarithmic; 1038 bool logarithmic;
1109 1039
1150 if (frame < 0) frame = 0; 1080 if (frame < 0) frame = 0;
1151 frame = frame / m_model->getResolution() * m_model->getResolution(); 1081 frame = frame / m_model->getResolution() * m_model->getResolution();
1152 1082
1153 double value = getValueForY(v, e->y()); 1083 double value = getValueForY(v, e->y());
1154 1084
1155 m_editingPoint = RegionModel::Point(frame, float(value), 0, ""); 1085 m_editingPoint = Event(frame, float(value), 0, "");
1156 m_originalPoint = m_editingPoint; 1086 m_originalPoint = m_editingPoint;
1157 1087
1158 if (m_editingCommand) finish(m_editingCommand); 1088 if (m_editingCommand) finish(m_editingCommand);
1159 m_editingCommand = new RegionModel::EditCommand(m_model, 1089 m_editingCommand = new ChangeEventsCommand(m_model,
1160 tr("Draw Region")); 1090 tr("Draw Region"));
1161 m_editingCommand->addPoint(m_editingPoint); 1091 m_editingCommand->add(m_editingPoint);
1162 1092
1163 recalcSpacing(); 1093 recalcSpacing();
1164 1094
1165 m_editing = true; 1095 m_editing = true;
1166 } 1096 }
1172 1102
1173 sv_frame_t frame = v->getFrameForX(e->x()); 1103 sv_frame_t frame = v->getFrameForX(e->x());
1174 if (frame < 0) frame = 0; 1104 if (frame < 0) frame = 0;
1175 frame = frame / m_model->getResolution() * m_model->getResolution(); 1105 frame = frame / m_model->getResolution() * m_model->getResolution();
1176 1106
1177 double newValue = m_editingPoint.value; 1107 double newValue = m_editingPoint.getValue();
1178 if (m_verticalScale != EqualSpaced) newValue = getValueForY(v, e->y()); 1108 if (m_verticalScale != EqualSpaced) newValue = getValueForY(v, e->y());
1179 1109
1180 sv_frame_t newFrame = m_editingPoint.frame; 1110 sv_frame_t newFrame = m_editingPoint.getFrame();
1181 sv_frame_t newDuration = frame - newFrame; 1111 sv_frame_t newDuration = frame - newFrame;
1182 if (newDuration < 0) { 1112 if (newDuration < 0) {
1183 newFrame = frame; 1113 newFrame = frame;
1184 newDuration = -newDuration; 1114 newDuration = -newDuration;
1185 } else if (newDuration == 0) { 1115 } else if (newDuration == 0) {
1186 newDuration = 1; 1116 newDuration = 1;
1187 } 1117 }
1188 1118
1189 m_editingCommand->deletePoint(m_editingPoint); 1119 m_editingCommand->remove(m_editingPoint);
1190 m_editingPoint.frame = newFrame; 1120 m_editingPoint = m_editingPoint
1191 m_editingPoint.value = float(newValue); 1121 .withFrame(newFrame)
1192 m_editingPoint.duration = newDuration; 1122 .withValue(float(newValue))
1193 m_editingCommand->addPoint(m_editingPoint); 1123 .withDuration(newDuration);
1124 m_editingCommand->add(m_editingPoint);
1194 1125
1195 recalcSpacing(); 1126 recalcSpacing();
1196 } 1127 }
1197 1128
1198 void 1129 void
1232 { 1163 {
1233 if (!m_model || !m_editing) return; 1164 if (!m_model || !m_editing) return;
1234 1165
1235 m_editing = false; 1166 m_editing = false;
1236 1167
1237 RegionModel::Point p(0); 1168 Event p(0);
1238 if (!getPointToDrag(v, e->x(), e->y(), p)) return; 1169 if (!getPointToDrag(v, e->x(), e->y(), p)) return;
1239 if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return; 1170 if (p.getFrame() != m_editingPoint.getFrame() ||
1240 1171 p.getValue() != m_editingPoint.getValue()) return;
1241 m_editingCommand = new RegionModel::EditCommand 1172
1173 m_editingCommand = new ChangeEventsCommand
1242 (m_model, tr("Erase Region")); 1174 (m_model, tr("Erase Region"));
1243 1175
1244 m_editingCommand->deletePoint(m_editingPoint); 1176 m_editingCommand->remove(m_editingPoint);
1245 1177
1246 finish(m_editingCommand); 1178 finish(m_editingCommand);
1247 m_editingCommand = nullptr; 1179 m_editingCommand = nullptr;
1248 m_editing = false; 1180 m_editing = false;
1249 recalcSpacing(); 1181 recalcSpacing();
1256 1188
1257 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) { 1189 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) {
1258 return; 1190 return;
1259 } 1191 }
1260 1192
1261 m_dragPointX = v->getXForFrame(m_editingPoint.frame); 1193 m_dragPointX = v->getXForFrame(m_editingPoint.getFrame());
1262 m_dragPointY = getYForValue(v, m_editingPoint.value); 1194 m_dragPointY = getYForValue(v, m_editingPoint.getValue());
1263 1195
1264 m_originalPoint = m_editingPoint; 1196 m_originalPoint = m_editingPoint;
1265 1197
1266 if (m_editingCommand) { 1198 if (m_editingCommand) {
1267 finish(m_editingCommand); 1199 finish(m_editingCommand);
1288 if (frame < 0) frame = 0; 1220 if (frame < 0) frame = 0;
1289 frame = frame / m_model->getResolution() * m_model->getResolution(); 1221 frame = frame / m_model->getResolution() * m_model->getResolution();
1290 1222
1291 // Do not bisect between two values, if one of those values is 1223 // Do not bisect between two values, if one of those values is
1292 // that of the point we're actually moving ... 1224 // that of the point we're actually moving ...
1293 int avoid = m_spacingMap[m_editingPoint.value]; 1225 int avoid = m_spacingMap[m_editingPoint.getValue()];
1294 1226
1295 // ... unless there are other points with the same value 1227 // ... unless there are other points with the same value
1296 if (m_distributionMap[m_editingPoint.value] > 1) avoid = -1; 1228 if (m_distributionMap[m_editingPoint.getValue()] > 1) avoid = -1;
1297 1229
1298 double value = getValueForY(v, newy, avoid); 1230 double value = getValueForY(v, newy, avoid);
1299 1231
1300 if (!m_editingCommand) { 1232 if (!m_editingCommand) {
1301 m_editingCommand = new RegionModel::EditCommand(m_model, 1233 m_editingCommand = new ChangeEventsCommand(m_model,
1302 tr("Drag Region")); 1234 tr("Drag Region"));
1303 } 1235 }
1304 1236
1305 m_editingCommand->deletePoint(m_editingPoint); 1237 m_editingCommand->remove(m_editingPoint);
1306 m_editingPoint.frame = frame; 1238 m_editingPoint = m_editingPoint
1307 m_editingPoint.value = float(value); 1239 .withFrame(frame)
1308 m_editingCommand->addPoint(m_editingPoint); 1240 .withValue(float(value));
1241 m_editingCommand->add(m_editingPoint);
1309 recalcSpacing(); 1242 recalcSpacing();
1310 } 1243 }
1311 1244
1312 void 1245 void
1313 RegionLayer::editEnd(LayerGeometryProvider *, QMouseEvent *) 1246 RegionLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
1316 1249
1317 if (m_editingCommand) { 1250 if (m_editingCommand) {
1318 1251
1319 QString newName = m_editingCommand->getName(); 1252 QString newName = m_editingCommand->getName();
1320 1253
1321 if (m_editingPoint.frame != m_originalPoint.frame) { 1254 if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) {
1322 if (m_editingPoint.value != m_originalPoint.value) { 1255 if (m_editingPoint.getValue() != m_originalPoint.getValue()) {
1323 newName = tr("Edit Region"); 1256 newName = tr("Edit Region");
1324 } else { 1257 } else {
1325 newName = tr("Relocate Region"); 1258 newName = tr("Relocate Region");
1326 } 1259 }
1327 } else { 1260 } else {
1340 bool 1273 bool
1341 RegionLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e) 1274 RegionLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
1342 { 1275 {
1343 if (!m_model) return false; 1276 if (!m_model) return false;
1344 1277
1345 RegionModel::Point region(0); 1278 Event region(0);
1346 if (!getPointToDrag(v, e->x(), e->y(), region)) return false; 1279 if (!getPointToDrag(v, e->x(), e->y(), region)) return false;
1347 1280
1348 ItemEditDialog *dialog = new ItemEditDialog 1281 ItemEditDialog *dialog = new ItemEditDialog
1349 (m_model->getSampleRate(), 1282 (m_model->getSampleRate(),
1350 ItemEditDialog::ShowTime | 1283 ItemEditDialog::ShowTime |
1351 ItemEditDialog::ShowDuration | 1284 ItemEditDialog::ShowDuration |
1352 ItemEditDialog::ShowValue | 1285 ItemEditDialog::ShowValue |
1353 ItemEditDialog::ShowText, 1286 ItemEditDialog::ShowText,
1354 getScaleUnits()); 1287 getScaleUnits());
1355 1288
1356 dialog->setFrameTime(region.frame); 1289 dialog->setFrameTime(region.getFrame());
1357 dialog->setValue(region.value); 1290 dialog->setValue(region.getValue());
1358 dialog->setFrameDuration(region.duration); 1291 dialog->setFrameDuration(region.getDuration());
1359 dialog->setText(region.label); 1292 dialog->setText(region.getLabel());
1360 1293
1361 if (dialog->exec() == QDialog::Accepted) { 1294 if (dialog->exec() == QDialog::Accepted) {
1362 1295
1363 RegionModel::Point newRegion = region; 1296 Event newRegion = region
1364 newRegion.frame = dialog->getFrameTime(); 1297 .withFrame(dialog->getFrameTime())
1365 newRegion.value = dialog->getValue(); 1298 .withValue(dialog->getValue())
1366 newRegion.duration = dialog->getFrameDuration(); 1299 .withDuration(dialog->getFrameDuration())
1367 newRegion.label = dialog->getText(); 1300 .withLabel(dialog->getText());
1368 1301
1369 RegionModel::EditCommand *command = new RegionModel::EditCommand 1302 ChangeEventsCommand *command = new ChangeEventsCommand
1370 (m_model, tr("Edit Region")); 1303 (m_model, tr("Edit Region"));
1371 command->deletePoint(region); 1304 command->remove(region);
1372 command->addPoint(newRegion); 1305 command->add(newRegion);
1373 finish(command); 1306 finish(command);
1374 } 1307 }
1375 1308
1376 delete dialog; 1309 delete dialog;
1377 recalcSpacing(); 1310 recalcSpacing();
1381 void 1314 void
1382 RegionLayer::moveSelection(Selection s, sv_frame_t newStartFrame) 1315 RegionLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
1383 { 1316 {
1384 if (!m_model) return; 1317 if (!m_model) return;
1385 1318
1386 RegionModel::EditCommand *command = 1319 ChangeEventsCommand *command =
1387 new RegionModel::EditCommand(m_model, tr("Drag Selection")); 1320 new ChangeEventsCommand(m_model, tr("Drag Selection"));
1388 1321
1389 RegionModel::PointList points = 1322 EventVector points =
1390 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 1323 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1391 1324
1392 for (RegionModel::PointList::iterator i = points.begin(); 1325 for (EventVector::iterator i = points.begin();
1393 i != points.end(); ++i) { 1326 i != points.end(); ++i) {
1394 1327
1395 if (s.contains(i->frame)) { 1328 Event newPoint = (*i)
1396 RegionModel::Point newPoint(*i); 1329 .withFrame(i->getFrame() + newStartFrame - s.getStartFrame());
1397 newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); 1330 command->remove(*i);
1398 command->deletePoint(*i); 1331 command->add(newPoint);
1399 command->addPoint(newPoint);
1400 }
1401 } 1332 }
1402 1333
1403 finish(command); 1334 finish(command);
1404 recalcSpacing(); 1335 recalcSpacing();
1405 } 1336 }
1406 1337
1407 void 1338 void
1408 RegionLayer::resizeSelection(Selection s, Selection newSize) 1339 RegionLayer::resizeSelection(Selection s, Selection newSize)
1409 { 1340 {
1410 if (!m_model) return; 1341 if (!m_model || !s.getDuration()) return;
1411 1342
1412 RegionModel::EditCommand *command = 1343 ChangeEventsCommand *command =
1413 new RegionModel::EditCommand(m_model, tr("Resize Selection")); 1344 new ChangeEventsCommand(m_model, tr("Resize Selection"));
1414 1345
1415 RegionModel::PointList points = 1346 EventVector points =
1416 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 1347 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1417 1348
1418 double ratio = 1349 double ratio = double(newSize.getDuration()) / double(s.getDuration());
1419 double(newSize.getEndFrame() - newSize.getStartFrame()) / 1350 double oldStart = double(s.getStartFrame());
1420 double(s.getEndFrame() - s.getStartFrame()); 1351 double newStart = double(newSize.getStartFrame());
1421 1352
1422 for (RegionModel::PointList::iterator i = points.begin(); 1353 for (Event p: points) {
1423 i != points.end(); ++i) { 1354
1424 1355 double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
1425 if (s.contains(i->frame)) { 1356 double newDuration = double(p.getDuration()) * ratio;
1426 1357
1427 double targetStart = double(i->frame); 1358 Event newPoint = p
1428 targetStart = double(newSize.getStartFrame()) + 1359 .withFrame(lrint(newFrame))
1429 targetStart - double(s.getStartFrame()) * ratio; 1360 .withDuration(lrint(newDuration));
1430 1361 command->remove(p);
1431 double targetEnd = double(i->frame + i->duration); 1362 command->add(newPoint);
1432 targetEnd = double(newSize.getStartFrame()) +
1433 targetEnd - double(s.getStartFrame()) * ratio;
1434
1435 RegionModel::Point newPoint(*i);
1436 newPoint.frame = lrint(targetStart);
1437 newPoint.duration = lrint(targetEnd - targetStart);
1438 command->deletePoint(*i);
1439 command->addPoint(newPoint);
1440 }
1441 } 1363 }
1442 1364
1443 finish(command); 1365 finish(command);
1444 recalcSpacing(); 1366 recalcSpacing();
1445 } 1367 }
1447 void 1369 void
1448 RegionLayer::deleteSelection(Selection s) 1370 RegionLayer::deleteSelection(Selection s)
1449 { 1371 {
1450 if (!m_model) return; 1372 if (!m_model) return;
1451 1373
1452 RegionModel::EditCommand *command = 1374 ChangeEventsCommand *command =
1453 new RegionModel::EditCommand(m_model, tr("Delete Selected Points")); 1375 new ChangeEventsCommand(m_model, tr("Delete Selected Points"));
1454 1376
1455 RegionModel::PointList points = 1377 EventVector points =
1456 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 1378 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1457 1379
1458 for (RegionModel::PointList::iterator i = points.begin(); 1380 for (EventVector::iterator i = points.begin();
1459 i != points.end(); ++i) { 1381 i != points.end(); ++i) {
1460 1382
1461 if (s.contains(i->frame)) { 1383 if (s.contains(i->getFrame())) {
1462 command->deletePoint(*i); 1384 command->remove(*i);
1463 } 1385 }
1464 } 1386 }
1465 1387
1466 finish(command); 1388 finish(command);
1467 recalcSpacing(); 1389 recalcSpacing();
1470 void 1392 void
1471 RegionLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to) 1393 RegionLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
1472 { 1394 {
1473 if (!m_model) return; 1395 if (!m_model) return;
1474 1396
1475 RegionModel::PointList points = 1397 EventVector points =
1476 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 1398 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1477 1399
1478 for (RegionModel::PointList::iterator i = points.begin(); 1400 for (Event p: points) {
1479 i != points.end(); ++i) { 1401 to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame())));
1480 if (s.contains(i->frame)) {
1481 Clipboard::Point point(i->frame, i->value, i->duration, i->label);
1482 point.setReferenceFrame(alignToReference(v, i->frame));
1483 to.addPoint(point);
1484 }
1485 } 1402 }
1486 } 1403 }
1487 1404
1488 bool 1405 bool
1489 RegionLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */) 1406 RegionLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /* frameOffset */, bool /* interactive */)
1490 { 1407 {
1491 if (!m_model) return false; 1408 if (!m_model) return false;
1492 1409
1493 const Clipboard::PointList &points = from.getPoints(); 1410 const EventVector &points = from.getPoints();
1494 1411
1495 bool realign = false; 1412 bool realign = false;
1496 1413
1497 if (clipboardHasDifferentAlignment(v, from)) { 1414 if (clipboardHasDifferentAlignment(v, from)) {
1498 1415
1509 if (button == QMessageBox::Yes) { 1426 if (button == QMessageBox::Yes) {
1510 realign = true; 1427 realign = true;
1511 } 1428 }
1512 } 1429 }
1513 1430
1514 RegionModel::EditCommand *command = 1431 ChangeEventsCommand *command =
1515 new RegionModel::EditCommand(m_model, tr("Paste")); 1432 new ChangeEventsCommand(m_model, tr("Paste"));
1516 1433
1517 for (Clipboard::PointList::const_iterator i = points.begin(); 1434 for (EventVector::const_iterator i = points.begin();
1518 i != points.end(); ++i) { 1435 i != points.end(); ++i) {
1519 1436
1520 if (!i->haveFrame()) continue;
1521 sv_frame_t frame = 0; 1437 sv_frame_t frame = 0;
1522 1438
1523 if (!realign) { 1439 if (!realign) {
1524 1440
1525 frame = i->getFrame(); 1441 frame = i->getFrame();
1526 1442
1527 } else { 1443 } else {
1528 1444
1529 if (i->haveReferenceFrame()) { 1445 if (i->hasReferenceFrame()) {
1530 frame = i->getReferenceFrame(); 1446 frame = i->getReferenceFrame();
1531 frame = alignFromReference(v, frame); 1447 frame = alignFromReference(v, frame);
1532 } else { 1448 } else {
1533 frame = i->getFrame(); 1449 frame = i->getFrame();
1534 } 1450 }
1535 } 1451 }
1536 1452
1537 RegionModel::Point newPoint(frame); 1453 Event p = *i;
1538 1454 Event newPoint = p;
1539 if (i->haveLabel()) newPoint.label = i->getLabel(); 1455 if (!p.hasValue()) {
1540 if (i->haveValue()) newPoint.value = i->getValue(); 1456 newPoint = newPoint.withValue((m_model->getValueMinimum() +
1541 else newPoint.value = (m_model->getValueMinimum() + 1457 m_model->getValueMaximum()) / 2);
1542 m_model->getValueMaximum()) / 2; 1458 }
1543 if (i->haveDuration()) newPoint.duration = i->getDuration(); 1459 if (!p.hasDuration()) {
1544 else {
1545 sv_frame_t nextFrame = frame; 1460 sv_frame_t nextFrame = frame;
1546 Clipboard::PointList::const_iterator j = i; 1461 EventVector::const_iterator j = i;
1547 for (; j != points.end(); ++j) { 1462 for (; j != points.end(); ++j) {
1548 if (!j->haveFrame()) continue;
1549 if (j != i) break; 1463 if (j != i) break;
1550 } 1464 }
1551 if (j != points.end()) { 1465 if (j != points.end()) {
1552 nextFrame = j->getFrame(); 1466 nextFrame = j->getFrame();
1553 } 1467 }
1554 if (nextFrame == frame) { 1468 if (nextFrame == frame) {
1555 newPoint.duration = m_model->getResolution(); 1469 newPoint = newPoint.withDuration(m_model->getResolution());
1556 } else { 1470 } else {
1557 newPoint.duration = nextFrame - frame; 1471 newPoint = newPoint.withDuration(nextFrame - frame);
1558 } 1472 }
1559 } 1473 }
1560 1474
1561 command->addPoint(newPoint); 1475 command->add(newPoint);
1562 } 1476 }
1563 1477
1564 finish(command); 1478 finish(command);
1565 recalcSpacing(); 1479 recalcSpacing();
1566 return true; 1480 return true;