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 #ifndef RG_STAFFLAYOUT_H
19 #define RG_STAFFLAYOUT_H
20 
21 #include "base/Event.h"
22 #include "base/ViewElement.h"
23 #include <QRect>
24 #include <utility>
25 #include <vector>
26 
27 class QGraphicsScene;
28 class QGraphicsLineItem;
29 class QGraphicsItem;
30 
31 namespace Rosegarden
32 {
33 
34 class BarLineItem;
35 class TimeSignature;
36 class SnapGrid;
37 class ViewSegment;
38 class HorizontalLayoutEngine;
39 class Event;
40 
41 
42 /**
43  * StaffLayout is a base for classes that display the contents of a
44  * Segment on a set of horizontal lines with optional vertical bar
45  * lines.  Possible subclasses include the notation and piano-roll
46  * staffs.
47  *
48  * In general, this class handles x coordinates in floating-point,
49  * but y-coordinates as integers because of the requirement that
50  * staff lines be a precise integral distance apart.
51  */
52 
53 class StaffLayout
54 {
55 public:
56     typedef std::pair<double, int> StaffLayoutCoords;
57 
58     enum PageMode {
59         LinearMode = 0,
60         ContinuousPageMode,
61         MultiPageMode
62     };
63 
64     enum BarStyle {
65         PlainBar = 0,
66         DoubleBar,
67         HeavyDoubleBar,
68         RepeatEndBar,
69         RepeatStartBar,
70         RepeatBothBar,
71         NoVisibleBar
72     };
73 
74 protected:
75     /**
76      * Create a new StaffLayout for the given ViewSegment, with a
77      * linear layout.
78      *
79      * \a id is an arbitrary id for the staff in its view,
80      *    not used within the StaffLayout implementation but
81      *    queryable via getId
82      *
83      * \a resolution is the number of blank pixels between
84      *    staff lines
85      *
86      * \a lineThickness is the number of pixels thick a
87      *    staff line should be
88      */
89     StaffLayout(QGraphicsScene *, ViewSegment *, SnapGrid *,
90                 int id, int resolution, int lineThickness);
91 
92     /**
93      * Create a new StaffLayout for the given ViewSegment, with a
94      * page layout.
95      *
96      * \a id is an arbitrary id for the staff in its view,
97      *    not used within the StaffLayout implementation but
98      *    queryable via getId
99      *
100      * \a resolution is the number of blank pixels between
101      *    staff lines
102      *
103      * \a lineThickness is the number of pixels thick a
104      *    staff line should be
105      *
106      * \a pageWidth is the width of a page, to determine
107      *    when to break lines for page layout
108      *
109      * \a rowsPerPage is the number of rows to a page, or zero
110      *    for a single continuous page
111      *
112      * \a rowSpacing is the distance in pixels between
113      *    the tops of consecutive rows on this staff
114      */
115     StaffLayout(QGraphicsScene *, ViewSegment *, SnapGrid *,
116                 int id, int resolution, int lineThickness,
117                 double pageWidth, int rowsPerPage, int rowSpacing);
118 
119     /**
120      * Create a new StaffLayout for the given Segment, with
121      * either page or linear layout.
122      */
123     StaffLayout(QGraphicsScene *, ViewSegment *, SnapGrid *,
124                 int id, int resolution, int lineThickness, PageMode pageMode,
125                 double pageWidth, int rowsPerPage, int rowSpacing);
126 
127 public:
128     virtual ~StaffLayout();
129 
130 protected:
131     // Methods required to define the type of staff this is
132 
133     /**
134      * Returns the number of visible staff lines
135      */
136     virtual int getLineCount() const = 0;
137 
138     /**
139      * Returns the number of invisible staff lines
140      * to leave space for above (and below) the visible staff
141      */
142     virtual int getLegerLineCount() const = 0;
143 
144     /**
145      * Returns the height-on-staff value for
146      * the bottom visible staff line (a shorthand means for
147      * referring to staff lines)
148      */
149     virtual int getBottomLineHeight() const = 0;
150 
151     /**
152      * Returns the difference between the height-on-
153      * staff value of one visible staff line and the next one
154      * above it
155      */
156     virtual int getHeightPerLine() const = 0;
157 
158     /**
159      * Returns the height-on-staff value for the top visible
160      * staff line.  This is deliberately not virtual.
161      */
getTopLineHeight()162     int getTopLineHeight() const {
163         return getBottomLineHeight() +
164             (getLineCount() - 1) * getHeightPerLine();
165     }
166 
167     /**
168      * Returns true if elements fill the spaces between lines,
169      * false if elements can fall on lines.  If true, the lines
170      * will be displaced vertically by half a line spacing.
171      */
elementsInSpaces()172     virtual bool elementsInSpaces() const {
173         return false;
174     }
175 
176     /**
177      * Returns true if the staff should draw a faint vertical line at
178      * each beat, in between the (darker) bar lines.
179      */
showBeatLines()180     virtual bool showBeatLines() const {
181         return false;
182     }
183 
184     /**
185      * Returns the number of bars between bar-line numbers, or zero if
186      * bar lines should not be numbered.  For example, if this
187      * function returns 5, every 5th bar (starting at bar 5) will be
188      * numbered.
189      */
showBarNumbersEvery()190     virtual int showBarNumbersEvery() const {
191         return 0;
192     }
193 
194     /**
195      * Returns the bar line / repeat style for the start of the given bar.
196      */
getBarStyle(int)197     virtual BarStyle getBarStyle(int /* barNo */) const {
198         return PlainBar;
199     }
200 
201     /**
202      * Returns the distance the opening (repeat) bar is inset from the
203      * nominal barline position.  This is to accommodate the situation
204      * where a repeat bar has to appear after the clef and key.
205      */
getBarInset(int,bool)206     virtual double getBarInset(int /* barNo */, bool /* isFirstBarInRow */) const {
207         return 0;
208     }
209 
210 protected:
211     /// Subclass may wish to expose this
212     virtual void setResolution(int resolution);
213 
214     /// Subclass may wish to expose this
215     virtual void setLineThickness(int lineThickness);
216 
217     /// Subclass may wish to expose this
218     virtual void setPageMode(PageMode pageMode);
219 
220     /// Subclass may wish to expose this
221     virtual void setPageWidth(double pageWidth);
222 
223     /// Subclass may wish to expose this
224     virtual void setRowsPerPage(int rowsPerPage);
225 
226     /// Subclass may wish to expose this
227     virtual void setRowSpacing(int rowSpacing);
228 
229     /// Subclass may wish to expose this.  Default is zero
230     virtual void setConnectingLineLength(int length);
231 
232 public:
233     /**
234      * Return the id of the staff.  This is only useful to external
235      * agents, it isn't used by the StaffLayout itself.
236      */
237     virtual int getId() const;
238 
239     /**
240      * Set the scene x-coordinate of the left-hand end of the staff.
241      * This does not move any scene items that have already been
242      * created; it should be called before the sizeStaff/positionElements
243      * procedure begins.
244      */
245     virtual void setX(double x);
246 
247     /**
248      * Get the scene x-coordinate of the left-hand end of the staff.
249      */
250     virtual double getX() const;
251 
252     /**
253      * Set the scene y-coordinate of the top of the first staff row.
254      * This does not move any scene items that have already been
255      * created; it should be called before the sizeStaff/positionElements
256      * procedure begins.
257      */
258     virtual void setY(int y);
259 
260     /**
261      * Get the scene y-coordinate of the top of the first staff row.
262      */
263     virtual int getY() const;
264 
265     /**
266      * Set the scene width of the margin to left and right of the
267      * staff on each page (used only in MultiPageMode).  Each staff
268      * row will still be pageWidth wide (that is, the margin is in
269      * addition to the pageWidth, not included in it).  This does not
270      * move any scene items that have already been created; it should
271      * be called before the sizeStaff/positionElements procedure
272      * begins.
273      */
274     virtual void setMargin(double m);
275 
276     /**
277      * Get the scene width of the left and right margins.
278      */
279     virtual double getMargin() const;
280 
281     /**
282      * Set the scene height of the area at the top of the first page
283      * reserved for the composition title and composer's name (used
284      * only in MultiPageMode).
285      */
286     virtual void setTitleHeight(int h);
287 
288     /**
289      * Get the scene height of the title area.
290      */
291     virtual int getTitleHeight() const;
292 
293     /**
294      * Returns the width of the entire staff after layout.  Call
295      * this only after you've done the full sizeStaff/positionElements
296      * procedure.
297      */
298     virtual double getTotalWidth() const;
299 
300     /**
301      * Returns the height of the entire staff after layout.  Call
302      * this only after you've done the full sizeStaff/positionElements
303      * procedure.  If there are multiple rows, this will be the
304      * height of all rows, including any space between rows that
305      * is used to display other staffs.
306      */
307     virtual int getTotalHeight() const;
308 
309     /**
310      * Returns the total number of pages used by the staff.
311      */
getPageCount()312     int getPageCount() const {
313         if (m_pageMode != MultiPageMode) return 1;
314         else return 1 + (getRowForLayoutX(m_endLayoutX) / getRowsPerPage());
315     }
316 
317     /**
318      * Returns the difference between the y coordinates of
319      * neighbouring visible staff lines.  Deliberately non-virtual
320      */
getLineSpacing()321     int getLineSpacing() const {
322         return m_resolution + m_lineThickness;
323     }
324 
325     /**
326      * Returns the total height of a single staff row, including ruler
327      */
328     virtual int getHeightOfRow() const;
329 
330     /**
331      * Returns true if the given scene coordinates fall within
332      * (any of the rows of) this staff.  False if they fall in the
333      * gap between two rows.
334      */
335     virtual bool containsSceneCoords(double sceneX, int sceneY) const;
336 
337     /**
338      * Returns the scene y coordinate of the specified line on the
339      * staff.  baseX/baseY are a scene coordinates somewhere on the
340      * correct row, or -1 for the default row.
341      */
342     virtual int getSceneYForHeight(int height, double baseX = -1, int baseY = -1) const;
343 
344     /**
345      * Returns the y coordinate of the specified line on the
346      * staff, relative to the top of the row.
347      */
348     virtual int getLayoutYForHeight(int height) const;
349 
350 
351     /**
352      * Returns the height-on-staff value nearest to the given scene coordinates,
353      * weighted toward the height specified in originalHeight.  This is used
354      * when we are comparing a new height calculation to an existing one, and
355      * want the new calculation to err on the side of matching the original
356      * value unless the difference is strong enough.
357      */
358     virtual int getWeightedHeightAtSceneCoords(int originalHeight, double x, int y);
359 
360     /**
361      * Returns the height-on-staff value nearest to the given
362      * scene coordinates.
363      */
364     virtual int getHeightAtSceneCoords(double x, int y) const;
365 
366     /**
367      * Return the full width, height and origin of the bar containing
368      * the given scene cooordinates.
369      */
370     virtual QRect getBarExtents(double x, int y) const;
371 
372     /**
373      * Set whether this is the current staff or not.  A staff that is
374      * current will differ visually from non-current staffs.
375      *
376      * The owner of the staffs should normally ensure that one staff
377      * is current (the default is non-current, even if there only is
378      * one staff) and that only one staff is current at once.
379      */
380     virtual void setCurrent(bool current);
381 
382     /**
383      * Query the given horizontal layout object (which is assumed to
384      * have just completed its layout procedure) to determine the
385      * required extents of the staff and the positions of the bars,
386      * and create the bars and staff lines accordingly.  It may be
387      * called either before or after renderElements and/or
388      * positionElements.
389      *
390      * No bars or staff lines will appear unless this method has
391      * been called.
392      */
393     virtual void sizeStaff(HorizontalLayoutEngine& layout);
394 
395     /**
396      * Generate or re-generate sprites for all the elements between
397      * from and to.  See subclasses for specific detailed comments.
398      *
399      * A very simplistic staff subclass may choose not to
400      * implement this (the default implementation is empty) and to
401      * do all the rendering work in positionElements.  If rendering
402      * elements is slow, however, it makes sense to do it here
403      * because this method may be called less often.
404      */
405     virtual void renderElements(ViewElementList::iterator from,
406                                 ViewElementList::iterator to);
407 
408     /**
409      * Assign suitable coordinates to the elements on the staff
410      * between the start and end times, based entirely on the layout
411      * X and Y coordinates they were given by the horizontal and
412      * vertical layout processes.
413      *
414      * The implementation is free to render any elements it
415      * chooses in this method as well.
416      */
417     virtual void positionElements(timeT from,
418                                   timeT to) = 0;
419 
420     /* Some optional methods for the subclass. */
421 
422     /**
423      * Return an iterator pointing to the nearest view element to the
424      * given scene coordinates.
425      *
426      * If notesAndRestsOnly is true, do not return any view element
427      * other than a note or rest.
428      *
429      * If the closest view element is further away than
430      * proximityThreshold pixels in either x or y axis, return end().
431      * If proximityThreshold is less than zero, treat it as infinite.
432      *
433      * Also return the clef and key in force at these coordinates.
434      *
435      * The default implementation should suit for subclasses that only
436      * show a single element per layout X coordinate.
437      */
438 /*
439     virtual ViewElementList::iterator getClosestElementToSceneCoords
440     (double x, int y,
441      Event *&clef, Event *&key,
442      bool notesAndRestsOnly = false, int proximityThreshold = 10) {
443         StaffLayoutCoords layoutCoords = getLayoutCoordsForSceneCoords(x, y);
444         return getClosestElementToLayoutX
445             (layoutCoords.first, clef, key,
446              notesAndRestsOnly, proximityThreshold);
447     }
448 */
449     /**
450      * Return an iterator pointing to the nearest view element to the
451      * given layout x-coordinate.
452      *
453      * If notesAndRestsOnly is true, do not return any view element
454      * other than a note or rest.
455      *
456      * If the closest view element is further away than
457      * proximityThreshold pixels in either x or y axis, return end().
458      * If proximityThreshold is less than zero, treat it as infinite.
459      *
460      * Also return the clef and key in force at these coordinates.
461      */
462 /*
463     virtual ViewElementList::iterator getClosestElementToLayoutX
464     (double x,
465      Event *&clef, Event *&key,
466      bool notesAndRestsOnly = false, int proximityThreshold = 10) = 0;
467 */
468     /**
469      * Return an iterator pointing to the element "under" the given
470      * scene coordinates.
471      *
472      * Return end() if there is no such element.
473      *
474      * Also return the clef and key in force at these coordinates.
475      *
476      *
477      * The default implementation should suit for subclasses that only
478      * show a single element per layout X coordinate.
479      */
getElementUnderSceneCoords(double x,int y,Event * & clef,Event * & key)480     virtual ViewElementList::iterator getElementUnderSceneCoords
481     (double x, int y, Event *&clef, Event *&key) {
482         StaffLayoutCoords layoutCoords = getLayoutCoordsForSceneCoords(x, y);
483         return getElementUnderLayoutX(layoutCoords.first, clef, key);
484     }
485 
486     /**
487      * Return an iterator pointing to the element "under" the given
488      * scene coordinates.
489      *
490      * Return end() if there is no such element.
491      *
492      * Also return the clef and key in force at these coordinates.
493      */
494     virtual ViewElementList::iterator getElementUnderLayoutX
495     (double x, Event *&clef, Event *&key) = 0;
496 
497     // The default implementation of the following is empty.  The
498     // subclass is presumed to know what the staff's name is and
499     // where to put it; this is simply called at some point during
500     // the staff-drawing process.
501     virtual void drawStaffName();
502 
503     /**
504      * Return the smaller rectangle (in scene coords) enclosing the
505      * whole segment area.
506      */
507     virtual QRectF getSceneArea();
508 
509 public:
510     // This should not really be public -- it should be one of the
511     // protected methods below -- but we have some code that needs
512     // it and hasn't been supplied with a proper way to do without.
513     // Please try to avoid calling this method.
514     //!!! fix NotationView::doDeferredCursorMove
515 
516     // This should not really be public -- it should be one of the
517     // protected methods below -- but we have some code that needs
518     // it and hasn't been supplied with a proper way to do without.
519     // Please try to avoid calling this method.
520     //!!! fix NotationView::getStaffForSceneCoords
521     StaffLayoutCoords
522     getLayoutCoordsForSceneCoords(double x, int y) const;
523 
524     // This should not really be public -- it should be one of the
525     // protected methods below -- but we have some code that needs
526     // it and hasn't been supplied with a proper way to do without.
527     // Please try to avoid calling this method.
528     //!!! fix NotationView::scrollToTime
529     StaffLayoutCoords
530     getSceneCoordsForLayoutCoords(double x, int y) const;//!!!
531 
532     // This should not really be public -- it should be one of the
533     // protected methods below -- but we have some code that needs
534     // it and hasn't been supplied with a proper way to do without.
535     // Please try to avoid calling this method.
536     //!!! fix NotationView::print etc
getRowSpacing()537     int getRowSpacing() { return m_rowSpacing; }
538 
539 protected:
540     // Methods that the subclass may (indeed, should) use to convert
541     // between the layout coordinates of elements and their scene
542     // coordinates.  These are deliberately not virtual.
543 
544     // Note that even linear-layout staffs have multiple rows; their
545     // rows all have the same y coordinate but increasing x
546     // coordinates, instead of the other way around.  (The only reason
547     // for this is that it seems to be more efficient from the canvas
548     // perspective to create and manipulate many relatively short
549     // lines rather than a smaller number of very long ones.)
550     //!!! review that for qgraphicsview
551 
getTopLineOffset()552     int getTopLineOffset() const {
553         return getLineSpacing() * getLegerLineCount();
554     }
555 
getBarLineHeight()556     int getBarLineHeight() const {
557         return getLineSpacing() * (getLineCount() - 1) + m_lineThickness;
558     }
559 
getRowForLayoutX(double x)560     int getRowForLayoutX(double x) const {
561         return (int)(x / m_pageWidth);
562     }
563 
564     int getRowForSceneCoords(double x, int y) const;
565 
566     int getSceneYForTopOfStaff(int row = -1) const;
567 
568     int getSceneYForTopLine(int row = -1) const {
569         return getSceneYForTopOfStaff(row) + getTopLineOffset();
570     }
571 
572     double getSceneXForLeftOfRow(int row) const;
573 
getSceneXForRightOfRow(int row)574     double getSceneXForRightOfRow(int row) const {
575         return getSceneXForLeftOfRow(row) + m_pageWidth;
576     }
577 
578     StaffLayoutCoords
getSceneOffsetsForLayoutCoords(double x,int y)579     getSceneOffsetsForLayoutCoords(double x, int y) const {
580         StaffLayoutCoords cc = getSceneCoordsForLayoutCoords(x, y);
581         return StaffLayoutCoords(cc.first - x, cc.second - y);
582     }
583 
584     double getSceneXForLayoutX(double x) const;
585 
getRowsPerPage()586     int getRowsPerPage() const {
587         return m_rowsPerPage;
588     }
589 
590 protected:
591     // Actual implementation methods.  The default implementation
592     // shows staff lines, connecting lines (where appropriate) and bar
593     // lines, but does not show time signatures.  To see time
594     // signatures, override the deleteTimeSignatures and
595     // insertTimeSignature methods.  For repeated clefs and keys at
596     // the start of each row, override deleteRepeatedClefsAndKeys
597     // and insertRepeatedClefAndKey, but note that your layout class
598     // will need to allot the space for them separately.
599 
600     virtual void resizeStaffLines();
601     virtual void clearStaffLineRow(int row);
602     virtual void resizeStaffLineRow(int row, double offset, double length);
603 
604     virtual void deleteBars();
605     virtual void insertBar(double layoutX, double width, bool isCorrect,
606                            const TimeSignature &,
607                            int barNo, bool showBarNo);
608 
609     // The default implementations of the following two are empty.
610     virtual void deleteTimeSignatures();
611     virtual void insertTimeSignature(double layoutX,
612                                      const TimeSignature &, bool grayed);
613 
614     // The default implementations of the following two are empty.
615     virtual void deleteRepeatedClefsAndKeys();
616     virtual void insertRepeatedClefAndKey(double layoutX, int barNo);
617 
getScene()618     QGraphicsScene *getScene() { return m_scene; }
619 
620     void initCursors();
621 
622 protected:
623 
624     //--------------- Data members ---------------------------------
625 
626     QGraphicsScene *m_scene;
627     ViewSegment *m_viewSegment;
628     SnapGrid *m_snapGrid;
629 
630     int      m_id;
631 
632     double   m_x;
633     int      m_y;
634     double   m_margin;
635     int      m_titleHeight;
636     int      m_resolution;
637     int      m_lineThickness;
638 
639     PageMode m_pageMode;
640     double   m_pageWidth;
641     int      m_rowsPerPage;
642     int      m_rowSpacing;
643     int      m_connectingLineLength;
644 
645     double   m_startLayoutX;
646     double   m_endLayoutX;
647 
648     bool     m_current;
649 
650     typedef std::vector<QGraphicsItem *> ItemList;
651     typedef std::vector<ItemList> ItemMatrix;
652     ItemMatrix m_staffLines;
653     ItemList m_staffConnectingLines;
654 
655     struct BarLineComparator {
operatorBarLineComparator656         bool operator()(const BarLineItem *a, const BarLineItem *b) const {
657             return compareBars(a, b);
658         }
659     };
660 
661     typedef std::pair<double, QGraphicsItem *> LineRec; // layout-x, line
662     typedef std::vector<LineRec> LineRecList;
663     typedef std::multiset<BarLineItem *, BarLineComparator> BarLineList;
664     static bool compareBars(const BarLineItem *, const BarLineItem *);
665     static bool compareBarToLayoutX(const BarLineItem *, int);
666     BarLineList m_barLines;
667     LineRecList m_beatLines;
668     LineRecList m_barConnectingLines;
669     ItemList m_barNumbers;
670 };
671 
672 
673 }
674 
675 #endif
676