1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A sequencer and musical notation editor.
6     Copyright 2000-2021 the Rosegarden development team.
7     See the AUTHORS file for more details.
8 
9     This program is free software; you can redistribute it and/or
10     modify it under the terms of the GNU General Public License as
11     published by the Free Software Foundation; either version 2 of the
12     License, or (at your option) any later version.  See the file
13     COPYING included with this distribution for more information.
14 */
15 
16 #ifndef RG_SNAP_GRID_H
17 #define RG_SNAP_GRID_H
18 
19 #include "RulerScale.h"
20 
21 #include <map>
22 
23 namespace Rosegarden {
24 
25 /**
26  * SnapGrid is a class that maps x-coordinate onto time, using a
27  * RulerScale to get the mapping but constraining the results to a
28  * discrete set of suitable times.
29  *
30  * (It also snaps y-coordinates, but that bit isn't very interesting.)
31  */
32 
33 class SnapGrid
34 {
35 public:
36     /**
37      * Construct a SnapGrid that uses the given RulerScale for
38      * x-coordinate mappings and the given ysnap for y-coords.
39      * If ysnap is zero, y-coords are not snapped at all.
40      */
41     SnapGrid(const RulerScale *rulerScale, int ysnap = 0);
42 
43     static const timeT NoSnap;
44     static const timeT SnapToBar;
45     static const timeT SnapToBeat;
46     static const timeT SnapToUnit;
47 
48     enum SnapDirection { SnapEither, SnapLeft, SnapRight };
49 
50     /**
51      * Set the snap size of the grid to the given time.
52      * The snap time must be positive, or else one of the
53      * special constants NoSnap, SnapToBar, SnapToBeat or
54      * SnapToUnit.
55      * The default is SnapToBeat.
56      */
57     void setSnapTime(timeT snap);
58 
59     /**
60      * Return the snap size of the grid, at the given x-coordinate.
61      * (The x-coordinate is required in case the built-in snap size is
62      * SnapToBar, SnapToBeat or SnapToUnit, in which case we need to
63      * know the current time signature.)  Returns zero for NoSnap.
64      */
65     timeT getSnapTime(double x) const;
66 
67     /**
68      * Return the snap setting -- the argument that was passed to
69      * setSnapTime.  This differs from getSnapTime, which interprets
70      * the NoSnap, SnapToBar, SnapToBeat and SnapToUnit settings to
71      * return actual timeT values; instead this function returns those
72      * actual constants if set.
73      */
74     timeT getSnapSetting() const;
75 
76     /**
77      * Return the snap size of the grid, at the given time.  (The time
~AbstractSet()78      * is required in case the built-in snap size is SnapToBar,
79      * SnapToBeat or SnapToUnit, in which case we need to know the
80      * current time signature.)  Returns zero for NoSnap.
81      */
82     timeT getSnapTime(timeT t) const;
83 
84     /**
getInitialElement()85      * Snap a given x-coordinate to the nearest time on the grid.  Of
86      * course this also does x-to-time conversion, so it's useful even
87      * in NoSnap mode.  If the snap time is greater than the bar
88      * duration at this point, the bar duration will be used instead.
89      *
90      * If d is SnapLeft or SnapRight, a time to the left or right
91      * respectively of the given coordinate will be returned;
92      * otherwise the nearest time on either side will be returned.
93      */
94     timeT snapX(double x, SnapDirection d = SnapEither) const;
95 
getLongestElement()96     /**
97      * Snap a given time to the nearest time on the grid.  Unlike
98      * snapX, this is not useful in NoSnap mode.  If the snap time is
99      * greater than the bar duration at this point, the bar duration
100      * will be used instead.
101      *
102      * If d is SnapLeft or SnapRight, a time to the left or right
103      * respectively of the given coordinate will be returned;
104      * otherwise the nearest time on either side will be returned.
105      */
106     timeT snapTime(timeT t, SnapDirection d = SnapEither) const;
107 
108     /**
109      * Snap a given y-coordinate to the nearest lower bin coordinate.
110      */
111     int snapY(int y) const {
112         if (m_ysnap == 0) return y;
113         return getYBinCoordinate(getYBin(y));
114     }
115 
116     /**
117      * Return the bin number for the given y-coordinate.
118      */
119     int getYBin(int y) const;
120 
getContainer()121     /**
122      * Return the y-coordinate of the grid line at the start of the
123      * given bin.
124      */
125     int getYBinCoordinate(int bin) const;
126 
127     /**
128      * Set the default vertical step.  This is used as the height for
129      * bins that have no specific height multiple set, and the base
130      * height for bins that have a multiple.  Setting the Y snap here
131      * is equivalent to specifying it in the constructor.
132      */
133     void setYSnap(int ysnap) {
134         m_ysnap = ysnap;
135     }
136 
137     /**
138      * Retrieve the default vertical step.
139      */
140     int getYSnap() const {
141         return m_ysnap;
142     }
143 
144     /**
145      * Set the height multiple for a specific bin.  The bin will be
146      * multiple * ysnap high.  The default is 1 for all bins.
147      */
148     void setBinHeightMultiple(int bin, int multiple) {
149         m_ymultiple[bin] = multiple;
150     }
151 
152     /**
153      * Retrieve the height multiple for a bin.
154      */
155     int getBinHeightMultiple(int bin) {
156         if (m_ymultiple.find(bin) == m_ymultiple.end()) return 1;
157         return m_ymultiple[bin];
158     }
159 
160     const RulerScale *getRulerScale() const {
161         return m_rulerScale;
162     }
163 
164 protected:
165     const RulerScale *m_rulerScale; // I don't own this
166     timeT m_snapTime;
167     int m_ysnap;
168 
169     /// Number of segments high for each track.
170     std::map<int, int> m_ymultiple;
171 };
172 
173 }
174 
175 #endif
176