1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Rosegarden
5 A MIDI and audio sequencer and musical notation editor.
6 Copyright 2000-2021 the Rosegarden development team.
7
8 Other copyrights also apply to some parts of this work. Please
9 see the AUTHORS file and individual file headers for details.
10
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License as
13 published by the Free Software Foundation; either version 2 of the
14 License, or (at your option) any later version. See the file
15 COPYING included with this distribution for more information.
16 */
17
18 #define RG_MODULE_STRING "[StaffLayout]"
19 #define RG_NO_DEBUG_PRINT 1
20
21 #include "StaffLayout.h"
22
23 #include "base/Event.h"
24 #include "base/LayoutEngine.h"
25 #include "base/NotationTypes.h"
26 #include "base/Profiler.h"
27 #include "base/SnapGrid.h"
28 #include "base/ViewElement.h"
29 #include "base/ViewSegment.h"
30 #include "gui/editors/notation/BarLineItem.h"
31 #include "gui/general/GUIPalette.h"
32 #include "misc/Debug.h"
33
34 #include <QColor>
35 #include <QFont>
36 #include <QFontMetrics>
37 #include <QPen>
38 #include <QRect>
39 #include <QString>
40 #include <QGraphicsScene>
41 #include <QGraphicsItem>
42 #include <QGraphicsLineItem>
43 #include <QGraphicsRectItem>
44 #include <QGraphicsSimpleTextItem>
45 #include <QtGlobal>
46
47 #include <algorithm>
48
49
50 namespace Rosegarden
51 {
52
53 const int pointerWidth = 3;
54
StaffLayout(QGraphicsScene * scene,ViewSegment * viewSegment,SnapGrid * snapGrid,int id,int resolution,int lineThickness)55 StaffLayout::StaffLayout(QGraphicsScene *scene, ViewSegment *viewSegment,
56 SnapGrid *snapGrid, int id,
57 int resolution, int lineThickness) :
58 m_scene(scene),
59 m_viewSegment(viewSegment),
60 m_snapGrid(snapGrid),
61 m_id(id),
62 m_x(0.0),
63 m_y(0),
64 m_margin(0.0),
65 m_titleHeight(0),
66 m_resolution(resolution),
67 m_lineThickness(lineThickness),
68 m_pageMode(LinearMode),
69 m_pageWidth(2000.0), // fairly arbitrary, but we need something non-zero
70 m_rowsPerPage(0),
71 m_rowSpacing(0),
72 m_connectingLineLength(0),
73 m_startLayoutX(0),
74 m_endLayoutX(0),
75 m_current(false)
76 {
77 }
78
StaffLayout(QGraphicsScene * scene,ViewSegment * viewSegment,SnapGrid * snapGrid,int id,int resolution,int lineThickness,double pageWidth,int rowsPerPage,int rowSpacing)79 StaffLayout::StaffLayout(QGraphicsScene *scene, ViewSegment *viewSegment,
80 SnapGrid *snapGrid,
81 int id, int resolution, int lineThickness,
82 double pageWidth, int rowsPerPage, int rowSpacing) :
83 m_scene(scene),
84 m_viewSegment(viewSegment),
85 m_snapGrid(snapGrid),
86 m_id(id),
87 m_x(0.0),
88 m_y(0),
89 m_margin(0.0),
90 m_titleHeight(0),
91 m_resolution(resolution),
92 m_lineThickness(lineThickness),
93 m_pageMode(rowsPerPage ? MultiPageMode : ContinuousPageMode),
94 m_pageWidth(pageWidth),
95 m_rowsPerPage(rowsPerPage),
96 m_rowSpacing(rowSpacing),
97 m_connectingLineLength(0),
98 m_startLayoutX(0),
99 m_endLayoutX(0),
100 m_current(false)
101 {
102 }
103
StaffLayout(QGraphicsScene * scene,ViewSegment * viewSegment,SnapGrid * snapGrid,int id,int resolution,int lineThickness,PageMode pageMode,double pageWidth,int rowsPerPage,int rowSpacing)104 StaffLayout::StaffLayout(QGraphicsScene *scene, ViewSegment *viewSegment,
105 SnapGrid *snapGrid,
106 int id, int resolution, int lineThickness,
107 PageMode pageMode, double pageWidth, int rowsPerPage,
108 int rowSpacing) :
109 m_scene(scene),
110 m_viewSegment(viewSegment),
111 m_snapGrid(snapGrid),
112 m_id(id),
113 m_x(0.0),
114 m_y(0),
115 m_margin(0.0),
116 m_titleHeight(0),
117 m_resolution(resolution),
118 m_lineThickness(lineThickness),
119 m_pageMode(pageMode),
120 m_pageWidth(pageWidth),
121 m_rowsPerPage(rowsPerPage),
122 m_rowSpacing(rowSpacing),
123 m_connectingLineLength(0),
124 m_startLayoutX(0),
125 m_endLayoutX(0),
126 m_current(false)
127 {
128 }
129
~StaffLayout()130 StaffLayout::~StaffLayout()
131 {
132 deleteBars();
133 for (int i = 0; i < (int)m_staffLines.size(); ++i) clearStaffLineRow(i);
134 }
135
136 void
setResolution(int resolution)137 StaffLayout::setResolution(int resolution)
138 {
139 m_resolution = resolution;
140 }
141
142 void
setLineThickness(int lineThickness)143 StaffLayout::setLineThickness(int lineThickness)
144 {
145 m_lineThickness = lineThickness;
146 }
147
148 void
setPageMode(PageMode pageMode)149 StaffLayout::setPageMode(PageMode pageMode)
150 {
151 m_pageMode = pageMode;
152 }
153
154 void
setPageWidth(double pageWidth)155 StaffLayout::setPageWidth(double pageWidth)
156 {
157 m_pageWidth = pageWidth;
158 }
159
160 void
setRowsPerPage(int rowsPerPage)161 StaffLayout::setRowsPerPage(int rowsPerPage)
162 {
163 m_rowsPerPage = rowsPerPage;
164 }
165
166 void
setRowSpacing(int rowSpacing)167 StaffLayout::setRowSpacing(int rowSpacing)
168 {
169 m_rowSpacing = rowSpacing;
170 }
171
172 void
setConnectingLineLength(int connectingLineLength)173 StaffLayout::setConnectingLineLength(int connectingLineLength)
174 {
175 m_connectingLineLength = connectingLineLength;
176 }
177
178 int
getId() const179 StaffLayout::getId() const
180 {
181 return m_id;
182 }
183
184 void
setX(double x)185 StaffLayout::setX(double x)
186 {
187 m_x = x;
188 }
189
190 double
getX() const191 StaffLayout::getX() const
192 {
193 return m_x;
194 }
195
196 void
setY(int y)197 StaffLayout::setY(int y)
198 {
199 m_y = y;
200 }
201
202 int
getY() const203 StaffLayout::getY() const
204 {
205 return m_y;
206 }
207
208 void
setMargin(double margin)209 StaffLayout::setMargin(double margin)
210 {
211 m_margin = margin;
212 }
213
214 double
getMargin() const215 StaffLayout::getMargin() const
216 {
217 if (m_pageMode != MultiPageMode)
218 return 0;
219 return m_margin;
220 }
221
222 void
setTitleHeight(int titleHeight)223 StaffLayout::setTitleHeight(int titleHeight)
224 {
225 m_titleHeight = titleHeight;
226 }
227
228 int
getTitleHeight() const229 StaffLayout::getTitleHeight() const
230 {
231 return m_titleHeight;
232 }
233
234 double
getTotalWidth() const235 StaffLayout::getTotalWidth() const
236 {
237 switch (m_pageMode) {
238
239 case ContinuousPageMode:
240 return getSceneXForRightOfRow(getRowForLayoutX(m_endLayoutX)) - m_x;
241
242 case MultiPageMode:
243 return getSceneXForRightOfRow(getRowForLayoutX(m_endLayoutX)) + m_margin - m_x;
244
245 case LinearMode:
246 default:
247 return getSceneXForLayoutX(m_endLayoutX) - m_x;
248 }
249 }
250
251 int
getTotalHeight() const252 StaffLayout::getTotalHeight() const
253 {
254 switch (m_pageMode) {
255
256 case ContinuousPageMode:
257 return getSceneYForTopOfStaff(getRowForLayoutX(m_endLayoutX)) +
258 getHeightOfRow() - m_y;
259
260 case MultiPageMode:
261 return getSceneYForTopOfStaff(m_rowsPerPage - 1) +
262 getHeightOfRow() - m_y;
263
264 case LinearMode:
265 default:
266 return getSceneYForTopOfStaff(0) + getHeightOfRow() - m_y;
267 }
268 }
269
270 int
getHeightOfRow() const271 StaffLayout::getHeightOfRow() const
272 {
273 return getTopLineOffset() + getLegerLineCount() * getLineSpacing()
274 + getBarLineHeight() + m_lineThickness;
275 }
276
277 bool
containsSceneCoords(double x,int y) const278 StaffLayout::containsSceneCoords(double x, int y) const
279 {
280 // std::cerr << "StaffLayout::containsSceneCoords(" << x << "," << y << ")" << std::endl;
281
282 switch (m_pageMode) {
283
284 case ContinuousPageMode:
285
286 for (int row = getRowForLayoutX(m_startLayoutX);
287 row <= getRowForLayoutX(m_endLayoutX); ++row) {
288
289 if (y >= getSceneYForTopOfStaff(row) &&
290 y < getSceneYForTopOfStaff(row) + getHeightOfRow()) {
291 return true;
292 }
293 }
294
295 return false;
296
297 case MultiPageMode:
298
299 for (int row = getRowForLayoutX(m_startLayoutX);
300 row <= getRowForLayoutX(m_endLayoutX); ++row) {
301
302 if (y >= getSceneYForTopOfStaff(row) &&
303 y < getSceneYForTopOfStaff(row) + getHeightOfRow() &&
304 x >= getSceneXForLeftOfRow(row) &&
305 x <= getSceneXForRightOfRow(row)) {
306 return true;
307 }
308 }
309
310 return false;
311
312 case LinearMode:
313 default:
314
315 return (y >= getSceneYForTopOfStaff() &&
316 y < getSceneYForTopOfStaff() + getHeightOfRow());
317 }
318 }
319
320 int
getSceneYForHeight(int h,double baseX,int baseY) const321 StaffLayout::getSceneYForHeight(int h, double baseX, int baseY) const
322 {
323 int y;
324
325 // RG_DEBUG << "getSceneYForHeight(" << h << "," << baseY
326 // << ")" << endl;
327
328 if (baseX < 0)
329 baseX = getX() + getMargin();
330
331 if (baseY >= 0) {
332 y = getSceneYForTopLine(getRowForSceneCoords(baseX, baseY));
333 } else {
334 y = getSceneYForTopLine();
335 }
336
337 y += getLayoutYForHeight(h);
338
339 return y;
340 }
341
342 int
getLayoutYForHeight(int h) const343 StaffLayout::getLayoutYForHeight(int h) const
344 {
345 int y = ((getTopLineHeight() - h) * getLineSpacing()) / getHeightPerLine();
346 if (h < getTopLineHeight() && (h % getHeightPerLine() != 0))
347 ++y;
348
349 return y;
350 }
351
352 int
getWeightedHeightAtSceneCoords(int originalHeight,double x,int y)353 StaffLayout::getWeightedHeightAtSceneCoords(int originalHeight, double x, int y)
354 {
355 RG_DEBUG << "getWeightedHeightAtSceneCoords: originalHeight: "
356 << originalHeight << " non-weighted height: "
357 << getHeightAtSceneCoords(x, y);
358
359 // return the non-weighted height if it already matches (ie. the user
360 // clicked pretty close to the center of the note head)
361 int nonWeightedHeight = getHeightAtSceneCoords(x, y);
362
363 if (originalHeight == nonWeightedHeight) return nonWeightedHeight;
364
365 // if no match, calculate an approximate height
366 int row = getRowForSceneCoords(x, y);
367 int approximateHeight = (y - getSceneYForTopLine(row)) * getHeightPerLine() / getLineSpacing();
368 approximateHeight = getTopLineHeight() - approximateHeight;
369
370 RG_DEBUG << "approximateHeight: " << approximateHeight
371 << " originalHeight: " << originalHeight;
372
373 int difference = approximateHeight - originalHeight;
374 if (difference < 0) difference *= -1;
375
376 // the approximate height is very coarse, so let's try using it as a rough
377 // measure of how far the new height differs from the original, and return
378 // the original if it's inside the range of "close enough"
379 if (difference > 1) return nonWeightedHeight;
380 else return originalHeight;
381 }
382
383 int
getHeightAtSceneCoords(double x,int y) const384 StaffLayout::getHeightAtSceneCoords(double x, int y) const
385 {
386 //!!! the lazy route: approximate, then get the right value
387 // by calling getSceneYForHeight a few times... ugh
388
389 // RG_DEBUG << "\nNotationStaff::heightOfYCoord: y = " << y
390 // << ", getTopLineOffset() = " << getTopLineOffset()
391 // << ", getLineSpacing() = " << m_npf->getLineSpacing()
392 // << endl;
393
394 if (x < 0)
395 x = getX() + getMargin();
396
397 int row = getRowForSceneCoords(x, y);
398 int ph = (y - getSceneYForTopLine(row)) * getHeightPerLine() /
399 getLineSpacing();
400 ph = getTopLineHeight() - ph;
401
402 int i;
403 int mi = -2;
404 int md = getLineSpacing() * 2;
405
406 int testi = -2;
407 int testMd = 1000;
408
409 for (i = -1; i <= 1; ++i) {
410 int d = y - getSceneYForHeight(ph + i, x, y);
411 if (d < 0) {
412 d = -d;
413 }
414 if (d < md) {
415 md = d;
416 mi = i;
417 }
418 if (d < testMd) {
419 testMd = d;
420 testi = i;
421 }
422 }
423
424 if (mi > -2) {
425 // RG_DEBUG << "StaffLayout::getHeightAtSceneCoords: " << y
426 // << " -> " << (ph + mi) << " (mi is " << mi << ", distance "
427 // << md << ")" << endl;
428 // if (mi == 0) {
429 // RG_DEBUG << "GOOD APPROXIMATION";
430 // } else {
431 // RG_DEBUG << "BAD APPROXIMATION";
432 // }
433 return ph + mi;
434 } else {
435 RG_DEBUG << "StaffLayout::getHeightAtSceneCoords: heuristic got " << ph << ", nothing within range (closest was " << (ph + testi) << " which is " << testMd << " away)";
436 return 0;
437 }
438 }
439
440 QRect
getBarExtents(double x,int y) const441 StaffLayout::getBarExtents(double x, int y) const
442 {
443 int row = getRowForSceneCoords(x, y);
444
445 RG_DEBUG << "getBarExtents(" << x << "," << y << "), row " << row << ", have " << m_barLines.size() << " bar records";
446
447 for (BarLineList::const_iterator i = m_barLines.begin();
448 i != m_barLines.end(); ++i) {
449
450 BarLineItem *line = *i;
451
452 double layoutX = line->getLayoutX();
453 int barRow = getRowForLayoutX(layoutX);
454
455 RG_DEBUG << "bar layoutX " << layoutX << ", row " << barRow << ", page mode " << m_pageMode << ", x " << line->x();
456
457 if (m_pageMode != LinearMode && (barRow < row)) continue;
458
459 if (line->x() <= x) continue;
460 if (i == m_barLines.begin()) continue;
461
462 BarLineList::const_iterator j = i;
463 --j;
464 BarLineItem *prevline = *j;
465
466 QRect r = QRect(int(prevline->x()),
467 getSceneYForTopOfStaff(barRow),
468 int(line->x() - prevline->x()),
469 getHeightOfRow());
470 RG_DEBUG << "Returning rect " << r;
471 return r;
472 }
473
474 // failure
475 return QRect(int(getX() + getMargin()), getSceneYForTopOfStaff(),
476 4, getHeightOfRow());
477 }
478
479 double
getSceneXForLayoutX(double x) const480 StaffLayout::getSceneXForLayoutX(double x) const
481 {
482 switch (m_pageMode) {
483
484 case ContinuousPageMode:
485 return m_x + x - (m_pageWidth * getRowForLayoutX(x));
486
487 case MultiPageMode: {
488 int pageNo = getRowForLayoutX(x) / getRowsPerPage();
489 double cx = m_x + x - (m_pageWidth * getRowForLayoutX(x));
490 cx += m_margin + (m_margin * 2 + m_pageWidth) * pageNo;
491 return cx;
492 }
493
494 case LinearMode:
495 default:
496 return m_x + x;
497 }
498 }
499
500 StaffLayout::StaffLayoutCoords
getLayoutCoordsForSceneCoords(double x,int y) const501 StaffLayout::getLayoutCoordsForSceneCoords(double x, int y) const
502 {
503 int row = getRowForSceneCoords(x, y);
504 return StaffLayoutCoords
505 ((row * m_pageWidth) + x - getSceneXForLeftOfRow(row),
506 y - getSceneYForTopOfStaff(row));
507 }
508
509 StaffLayout::StaffLayoutCoords
getSceneCoordsForLayoutCoords(double x,int y) const510 StaffLayout::getSceneCoordsForLayoutCoords(double x, int y) const
511 {
512 int row = getRowForLayoutX(x);
513 return StaffLayoutCoords
514 (getSceneXForLayoutX(x), getSceneYForTopLine(row) + y);
515 }
516
517 int
getRowForSceneCoords(double x,int y) const518 StaffLayout::getRowForSceneCoords(double x, int y) const
519 {
520 switch (m_pageMode) {
521
522 case ContinuousPageMode:
523 return ((y - m_y) / m_rowSpacing);
524
525 case MultiPageMode: {
526 int px = int(x - m_x - m_margin);
527 int pw = int(m_margin * 2 + m_pageWidth);
528 if (px < pw)
529 y -= m_titleHeight;
530 return (getRowsPerPage() * (px / pw)) + ((y - m_y) / m_rowSpacing);
531 }
532
533 case LinearMode:
534 default:
535 return (int)((x - m_x) / m_pageWidth);
536 }
537 }
538
539 int
getSceneYForTopOfStaff(int row) const540 StaffLayout::getSceneYForTopOfStaff(int row) const
541 {
542 switch (m_pageMode) {
543
544 case ContinuousPageMode:
545 if (row <= 0)
546 return m_y;
547 else
548 return m_y + (row * m_rowSpacing);
549
550 case MultiPageMode:
551 if (row <= 0)
552 return m_y + m_titleHeight;
553 else if (row < getRowsPerPage())
554 return m_y + ((row % getRowsPerPage()) * m_rowSpacing) + m_titleHeight;
555 else
556 return m_y + ((row % getRowsPerPage()) * m_rowSpacing);
557
558 case LinearMode:
559 default:
560 return m_y;
561 }
562 }
563
564 double
getSceneXForLeftOfRow(int row) const565 StaffLayout::getSceneXForLeftOfRow(int row) const
566 {
567 switch (m_pageMode) {
568
569 case ContinuousPageMode:
570 return m_x;
571
572 case MultiPageMode:
573 return m_x + m_margin +
574 (m_margin*2 + m_pageWidth) * (row / getRowsPerPage());
575
576 case LinearMode:
577 default:
578 return m_x + (row * m_pageWidth);
579 }
580 }
581
582 void
sizeStaff(HorizontalLayoutEngine & layout)583 StaffLayout::sizeStaff(HorizontalLayoutEngine &layout)
584 {
585 Profiler profiler("StaffLayout::sizeStaff", true);
586
587 deleteBars();
588 deleteRepeatedClefsAndKeys();
589 deleteTimeSignatures();
590
591 // RG_DEBUG << "StaffLayout::sizeStaff";
592
593 int lastBar = layout.getLastVisibleBarOnViewSegment(*m_viewSegment);
594
595 double xleft = 0, xright = 0;
596 bool haveXLeft = false;
597
598 xright = layout.getBarPosition(lastBar) - 1;
599
600 TimeSignature currentTimeSignature;
601
602 for (int barNo = layout.getFirstVisibleBarOnViewSegment(*m_viewSegment);
603 barNo <= lastBar; ++barNo) {
604
605 double x = layout.getBarPosition(barNo);
606
607 if (!haveXLeft) {
608 xleft = x;
609 haveXLeft = true;
610 }
611
612 double timeSigX = 0;
613 TimeSignature timeSig;
614 bool isNew = layout.getTimeSignaturePosition
615 (*m_viewSegment, barNo, timeSig, timeSigX);
616
617
618 if (isNew && barNo < lastBar) {
619 currentTimeSignature = timeSig;
620 // Ask for a gray color if the segment is a repetition
621 bool grayed = m_viewSegment->getSegment().isTmp();
622 insertTimeSignature(timeSigX, currentTimeSignature, grayed);
623 RG_DEBUG << "StaffLayout[" << this << "]::sizeStaff: bar no " << barNo << " has time signature at " << timeSigX;
624 }
625
626 RG_DEBUG << "StaffLayout::sizeStaff: inserting bar at " << x << " on staff " << this << " (isNew " << isNew << ", timeSigX " << timeSigX << ")";
627
628 bool showBarNo =
629 (showBarNumbersEvery() > 0 &&
630 ((barNo + 1) % showBarNumbersEvery()) == 0);
631
632 insertBar(x,
633 ((barNo == lastBar) ? 0 :
634 (layout.getBarPosition(barNo + 1) - x)),
635 layout.isBarCorrectOnViewSegment(*m_viewSegment, barNo - 1),
636 currentTimeSignature,
637 barNo,
638 showBarNo);
639 }
640
641 m_startLayoutX = xleft;
642 m_endLayoutX = xright;
643
644 drawStaffName();
645 resizeStaffLines();
646 }
647
648 void
deleteBars()649 StaffLayout::deleteBars()
650 {
651 for (BarLineList::iterator i = m_barLines.begin();
652 i != m_barLines.end(); ++i) {
653 delete *i;
654 }
655
656 for (LineRecList::iterator i = m_beatLines.begin();
657 i != m_beatLines.end(); ++i) {
658 delete i->second;
659 }
660
661 for (LineRecList::iterator i = m_barConnectingLines.begin();
662 i != m_barConnectingLines.end(); ++i) {
663 delete i->second;
664 }
665
666 for (ItemList::iterator i = m_barNumbers.begin();
667 i != m_barNumbers.end(); ++i) {
668 delete *i;
669 }
670
671 m_barLines.clear();
672 m_beatLines.clear();
673 m_barConnectingLines.clear();
674 m_barNumbers.clear();
675 }
676
677 void
insertBar(double layoutX,double width,bool isCorrect,const TimeSignature & timeSig,int barNo,bool showBarNo)678 StaffLayout::insertBar(double layoutX, double width, bool isCorrect,
679 const TimeSignature &timeSig,
680 int barNo, bool showBarNo)
681 {
682 // RG_DEBUG << "insertBar: " << layoutX << ", " << width
683 // << ", " << isCorrect << endl;
684
685 int barThickness = m_lineThickness * 5 / 4;
686
687 // hack to ensure the bar line appears on the correct row in
688 // notation page layouts, with a conditional to prevent us from
689 // moving the bar and beat lines in the matrix
690 if (!showBeatLines()) {
691 if (width > 0.01) { // not final bar in staff
692 layoutX += 1;
693 } else {
694 layoutX -= 1;
695 }
696 }
697
698 int row = getRowForLayoutX(layoutX);
699 double x = getSceneXForLayoutX(layoutX);
700 int y = getSceneYForTopLine(row);
701
702 bool firstBarInRow = false, lastBarInRow = false;
703
704 if (m_pageMode != LinearMode &&
705 (getRowForLayoutX(layoutX) >
706 getRowForLayoutX(layoutX - getMargin() - 2)))
707 firstBarInRow = true;
708
709 if (m_pageMode != LinearMode &&
710 width > 0.01 && // width == 0 for final bar in staff
711 (getRowForLayoutX(layoutX) <
712 getRowForLayoutX(layoutX + width + getMargin() + 2)))
713 lastBarInRow = true;
714
715 BarStyle style = getBarStyle(barNo);
716
717 if (style == RepeatBothBar && firstBarInRow)
718 style = RepeatStartBar;
719
720 if (firstBarInRow)
721 insertRepeatedClefAndKey(layoutX, barNo);
722
723 // If we're supposed to be hiding bar lines, we do just that --
724 // create them as normal, then hide them. We can't simply not
725 // create them because we rely on this to find bar extents for
726 // things like double-click selection in notation.
727 bool hidden = false;
728 if (style == PlainBar && timeSig.hasHiddenBars())
729 hidden = true;
730
731 double inset = 0.0;
732 if (style == RepeatStartBar || style == RepeatBothBar) {
733 inset = getBarInset(barNo, firstBarInRow);
734 }
735
736 BarLineItem *line = new BarLineItem(layoutX, getBarLineHeight(),
737 barThickness, getLineSpacing(),
738 (int)inset, style);
739
740 m_scene->addItem(line);
741 line->setPos(int(x), y);
742
743 if (isCorrect) {
744 line->setColour(GUIPalette::getColour(GUIPalette::BarLine));
745 } else {
746 line->setColour(GUIPalette::getColour(GUIPalette::BarLineIncorrect));
747 }
748
749 line->setZValue( -1);
750 if (hidden)
751 line->hide();
752 else
753 line->show();
754
755 // The bar lines have to be in order of layout-x (there's no
756 // such interesting stipulation for beat or connecting lines)
757 BarLineList::iterator insertPoint = lower_bound
758 (m_barLines.begin(), m_barLines.end(), line, compareBars);
759 m_barLines.insert(insertPoint, line);
760
761 if (lastBarInRow) {
762
763 double xe = x + width - barThickness;
764 style = getBarStyle(barNo + 1);
765 if (style == RepeatBothBar)
766 style = RepeatEndBar;
767
768 BarLineItem *eline = new BarLineItem(layoutX, getBarLineHeight(),
769 barThickness, getLineSpacing(),
770 0, style);
771 m_scene->addItem(eline);
772 eline->setPos(int(xe) + .5, y + .5);
773
774 eline->setColour(GUIPalette::getColour(GUIPalette::BarLine));
775
776 eline->setZValue( -1);
777 if (hidden)
778 eline->hide();
779 else
780 eline->show();
781
782 BarLineList::iterator insertPoint = lower_bound
783 (m_barLines.begin(), m_barLines.end(), eline, compareBars);
784 m_barLines.insert(insertPoint, eline);
785 }
786
787 if (showBarNo) {
788
789 QFont font;
790 font.setPixelSize(m_resolution * 3 / 2);
791 QFontMetrics metrics(font);
792 QString text = QString("%1").arg(barNo + 1);
793
794 QGraphicsSimpleTextItem *barNoText = new QGraphicsSimpleTextItem(text);
795 barNoText->setFont(font);
796 barNoText->setPos(x, y - metrics.height() - m_resolution * 2);
797 barNoText->setZValue( -1);
798 m_scene->addItem(barNoText);
799 if (hidden)
800 barNoText->hide();
801 else
802 barNoText->show();
803
804 m_barNumbers.push_back(barNoText);
805 }
806
807 QGraphicsRectItem *rect = nullptr;
808
809 if (showBeatLines()) {
810
811 double gridLines; // number of grid lines per bar may be fractional
812
813 // If the snap time is zero we default to beat markers
814 //
815 if (m_snapGrid && m_snapGrid->getSnapTime(x))
816 gridLines = double(timeSig.getBarDuration()) /
817 double(m_snapGrid->getSnapTime(x));
818 else
819 gridLines = timeSig.getBeatsPerBar();
820
821 double dx = width / gridLines;
822
823 for (int gridLine = hidden ? 0 : 1; gridLine < gridLines; ++gridLine) {
824
825 rect = new QGraphicsRectItem
826 (0, 0, barThickness, getBarLineHeight());
827 m_scene->addItem(rect);
828
829 rect->setPos(x + gridLine * dx + .5, y + .5);
830
831 double currentGrid = gridLines / double(timeSig.getBeatsPerBar());
832
833 rect->setPen(GUIPalette::getColour(GUIPalette::BeatLine));
834 rect->setBrush(GUIPalette::getColour(GUIPalette::BeatLine));
835
836 // Reset to SubBeatLine colour if we're not a beat line - avoid div by zero!
837 //
838 if (currentGrid > 1.0 && double(gridLine) / currentGrid != gridLine / int(currentGrid)) {
839 rect->setPen(GUIPalette::getColour(GUIPalette::SubBeatLine));
840 rect->setBrush(GUIPalette::getColour(GUIPalette::SubBeatLine));
841 }
842
843 rect->setZValue( -1);
844 rect->show(); // show beat lines even if the bar lines are hidden
845
846 LineRec beatLine(layoutX + gridLine * dx, rect);
847 m_beatLines.push_back(beatLine);
848 }
849 }
850
851 if (m_connectingLineLength > 0) {
852
853 rect = new QGraphicsRectItem
854 (0, 0, barThickness, m_connectingLineLength);
855 m_scene->addItem(rect);
856
857 rect->setPos(x + .5, y + .5);
858
859 rect->setPen(GUIPalette::getColour(GUIPalette::StaffConnectingLine));
860 rect->setBrush(GUIPalette::getColour(GUIPalette::StaffConnectingLine));
861 rect->setZValue( -3);
862 if (hidden)
863 rect->hide();
864 else
865 rect->show();
866
867 LineRec connectingLine(layoutX, rect);
868 m_barConnectingLines.push_back(connectingLine);
869 }
870 }
871
872 bool
compareBars(const BarLineItem * barLine1,const BarLineItem * barLine2)873 StaffLayout::compareBars(const BarLineItem *barLine1, const BarLineItem *barLine2)
874 {
875 return (barLine1->getLayoutX() < barLine2->getLayoutX());
876 }
877
878 bool
compareBarToLayoutX(const BarLineItem * barLine1,int x)879 StaffLayout::compareBarToLayoutX(const BarLineItem *barLine1, int x)
880 {
881 return (barLine1->getLayoutX() < x);
882 }
883
884 void
deleteTimeSignatures()885 StaffLayout::deleteTimeSignatures()
886 {
887 // default implementation is empty
888 }
889
890 void
insertTimeSignature(double,const TimeSignature &,bool)891 StaffLayout::insertTimeSignature(double, const TimeSignature &, bool)
892 {
893 // default implementation is empty
894 }
895
896 void
deleteRepeatedClefsAndKeys()897 StaffLayout::deleteRepeatedClefsAndKeys()
898 {
899 // default implementation is empty
900 }
901
902 void
insertRepeatedClefAndKey(double,int)903 StaffLayout::insertRepeatedClefAndKey(double, int)
904 {
905 // default implementation is empty
906 }
907
908 void
drawStaffName()909 StaffLayout::drawStaffName()
910 {
911 // default implementation is empty
912 }
913
914 void
resizeStaffLines()915 StaffLayout::resizeStaffLines()
916 {
917 Profiler profiler("StaffLayout::resizeStaffLines");
918
919 int firstRow = getRowForLayoutX(m_startLayoutX);
920 int lastRow = getRowForLayoutX(m_endLayoutX);
921
922 RG_DEBUG << "StaffLayout::resizeStaffLines: firstRow "
923 << firstRow << ", lastRow " << lastRow
924 << " (startLayoutX " << m_startLayoutX
925 << ", endLayoutX " << m_endLayoutX << ")";
926
927 Q_ASSERT(lastRow >= firstRow);
928
929 int i;
930 while ((int)m_staffLines.size() <= lastRow) {
931 m_staffLines.push_back(ItemList());
932 m_staffConnectingLines.push_back(nullptr);
933 }
934
935 // Remove all the staff lines that precede the start of the staff
936
937 for (i = 0; i < firstRow; ++i) clearStaffLineRow(i);
938
939 // now i == firstRow
940
941 while (i <= lastRow) {
942
943 double x0;
944 double x1;
945
946 if (i == firstRow) {
947 x0 = getSceneXForLayoutX(m_startLayoutX);
948 } else {
949 x0 = getSceneXForLeftOfRow(i);
950 }
951
952 if (i == lastRow) {
953 x1 = getSceneXForLayoutX(m_endLayoutX);
954 } else {
955 x1 = getSceneXForRightOfRow(i);
956 }
957
958 resizeStaffLineRow(i, x0, x1 - x0);
959
960 ++i;
961 }
962
963 // now i == lastRow + 1
964
965 while (i < (int)m_staffLines.size()) clearStaffLineRow(i++);
966 }
967
968 void
clearStaffLineRow(int row)969 StaffLayout::clearStaffLineRow(int row)
970 {
971 for (int h = 0; h < (int)m_staffLines[row].size(); ++h) {
972 delete m_staffLines[row][h];
973 }
974 m_staffLines[row].clear();
975
976 delete m_staffConnectingLines[row];
977 m_staffConnectingLines[row] = nullptr;
978 }
979
980 void
resizeStaffLineRow(int row,double x,double length)981 StaffLayout::resizeStaffLineRow(int row, double x, double length)
982 {
983 Profiler profiler("StaffLayout::resizeStaffLineRow");
984
985 // RG_DEBUG << "StaffLayout::resizeStaffLineRow: row "
986 // << row << ", x " << x << ", length "
987 // << length << endl;
988
989
990 // If the resolution is 8 or less, we want to reduce the blackness
991 // of the staff lines somewhat to make them less intrusive
992
993 int level = 0;
994 // int z = 2;
995 if (m_resolution < 6) {
996 // z = -1;
997 level = (9 - m_resolution) * 32;
998 if (level > 200)
999 level = 200;
1000 }
1001
1002 // As a result of a bug Michael reported where the staff changes color after
1003 // bar N, I decided to check the values. For m_resolution of 4 the level = 160.
1004 // However, if the previous value was < 12, the line remained black, i.e. level = 0.
1005 // If the previous value was 12 and it was reduced to 4, the line indeed was
1006 // gray, but it was UGLY. The reason for this is the notes remain black and
1007 // there are these gray lines through the notes. Since it doesn't always work,
1008 // and since it is ugly when it does work - kill it. 6 Nov 2009, Ilan
1009 level = 0;
1010 QColor lineColour(level, level, level);
1011
1012 int h;
1013
1014 /*!!! No longer really good enough. But we could potentially use the
1015 bar positions to sort this out
1016
1017 if (m_pageMode && row > 0 && offset == 0.0) {
1018 offset = (double)m_npf->getBarMargin() / 2;
1019 length -= offset;
1020 }
1021 */
1022
1023 int y;
1024
1025 delete m_staffConnectingLines[row];
1026
1027 if (m_pageMode != LinearMode && m_connectingLineLength > 0) {
1028
1029 // rather arbitrary (dup in insertBar)
1030 int barThickness = m_resolution / 12 + 1;
1031 y = getSceneYForTopLine(row);
1032 QGraphicsRectItem *line = new QGraphicsRectItem
1033 (int(x + length) + .5, y + .5, barThickness, m_connectingLineLength);
1034 m_scene->addItem(line);
1035 line->setPen(GUIPalette::getColour(GUIPalette::StaffConnectingTerminatingLine));
1036 line->setBrush(GUIPalette::getColour(GUIPalette::StaffConnectingTerminatingLine));
1037 line->setZValue( -2);
1038 line->show();
1039 m_staffConnectingLines[row] = line;
1040
1041 } else {
1042 m_staffConnectingLines[row] = nullptr;
1043 }
1044
1045 while ((int)m_staffLines[row].size() <= getLineCount() * m_lineThickness) {
1046 m_staffLines[row].push_back(nullptr);
1047 }
1048
1049 int lineIndex = 0;
1050
1051 for (h = 0; h < getLineCount(); ++h) {
1052
1053 y = getSceneYForHeight
1054 (getBottomLineHeight() + getHeightPerLine() * h,
1055 x, getSceneYForTopLine(row));
1056
1057 if (elementsInSpaces()) {
1058 y -= getLineSpacing() / 2 + 1;
1059 }
1060
1061 // RG_DEBUG << "StaffLayout: drawing line from ("
1062 // << x << "," << y << ") to (" << (x+length-1)
1063 // << "," << y << ")" << endl;
1064
1065 if (m_lineThickness > 1) {
1066
1067 QGraphicsRectItem *line = dynamic_cast<QGraphicsRectItem *>
1068 (m_staffLines[row][lineIndex]);
1069
1070 if (!line) {
1071 delete m_staffLines[row][lineIndex];
1072 m_staffLines[row][lineIndex] = line = new QGraphicsRectItem;
1073 line->setPen(QPen(lineColour, 0));
1074 line->setBrush(lineColour);
1075 m_scene->addItem(line);
1076 }
1077
1078 line->setRect(int(x) + .5, y + .5, int(length), m_lineThickness);
1079 line->show();
1080
1081 } else {
1082
1083 QGraphicsLineItem *line = dynamic_cast<QGraphicsLineItem *>
1084 (m_staffLines[row][lineIndex]);
1085
1086 if (!line) {
1087 delete m_staffLines[row][lineIndex];
1088 m_staffLines[row][lineIndex] = line = new QGraphicsLineItem;
1089 line->setPen(QPen(lineColour, 0));
1090 m_scene->addItem(line);
1091 }
1092
1093 line->setLine(int(x) + .5, y + .5, int(x + length) + .5, y + .5);
1094 line->show();
1095 }
1096
1097 ++lineIndex;
1098 }
1099
1100 while (lineIndex < (int)m_staffLines[row].size()) {
1101 delete m_staffLines[row][lineIndex];
1102 m_staffLines[row][lineIndex] = nullptr;
1103 ++lineIndex;
1104 }
1105 }
1106
1107 void
setCurrent(bool current)1108 StaffLayout::setCurrent(bool current)
1109 {
1110 m_current = current;
1111 }
1112
1113 void
renderElements(ViewElementList::iterator,ViewElementList::iterator)1114 StaffLayout::renderElements(ViewElementList::iterator,
1115 ViewElementList::iterator)
1116 {
1117 // nothing -- we assume rendering will be done by the implementation
1118 // of positionElements
1119 }
1120
1121 QRectF
getSceneArea()1122 StaffLayout::getSceneArea()
1123 {
1124 double top, bottom;
1125 double left, right;
1126 int firstRow, lastRow;
1127
1128 switch (m_pageMode) {
1129
1130 case ContinuousPageMode:
1131
1132 firstRow = getRowForLayoutX(m_startLayoutX);
1133 lastRow = getRowForLayoutX(m_endLayoutX);
1134
1135 if (lastRow != firstRow) {
1136 left = m_x;
1137 right = m_x + m_pageWidth;
1138 } else {
1139 left = getSceneXForLayoutX(m_startLayoutX);
1140 right = getSceneXForLayoutX(m_endLayoutX);
1141 }
1142
1143 top = getSceneYForTopOfStaff(firstRow);
1144 bottom = getSceneYForTopOfStaff(lastRow) + getHeightOfRow();
1145
1146 break;
1147
1148 case MultiPageMode:
1149
1150 firstRow = getRowForLayoutX(m_startLayoutX);
1151 lastRow = getRowForLayoutX(m_endLayoutX);
1152
1153 if (lastRow == firstRow) {
1154 left = getSceneXForLayoutX(m_startLayoutX);
1155 right = getSceneXForLayoutX(m_endLayoutX);
1156 top = getSceneYForTopOfStaff(firstRow);
1157 bottom = getSceneYForTopOfStaff(lastRow) + getHeightOfRow();
1158 } else {
1159
1160 int firstPage = firstRow / getRowsPerPage();
1161 int lastPage = lastRow / getRowsPerPage();
1162
1163 if (lastPage == firstPage) {
1164 left = getSceneXForLeftOfRow(firstRow);
1165 right = getSceneXForRightOfRow(lastRow);
1166
1167 top = getSceneYForTopOfStaff(firstRow);
1168 bottom = getSceneYForTopOfStaff(lastRow) + getHeightOfRow();
1169 } else {
1170
1171 /// TODO : Two special cases should be processed here
1172 /// 1 - Only one row on the first page
1173 /// 2 - Only one row on the last page
1174
1175 left = getSceneXForLeftOfRow(firstRow);
1176 right = getSceneXForRightOfRow(lastRow);
1177
1178 top = m_y;
1179 bottom = m_y + getHeightOfRow()
1180 + getRowsPerPage() * m_rowSpacing;
1181 }
1182 }
1183
1184 break;
1185
1186 case LinearMode:
1187 default:
1188
1189 left = m_startLayoutX;
1190 right = m_endLayoutX;
1191 top = getSceneYForTopOfStaff();
1192 bottom = top + getHeightOfRow();
1193 }
1194
1195 return QRectF(left, top, right - left, bottom - top);
1196 }
1197
1198 }
1199