1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2010-2019 Werner Schweer and others
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 //=============================================================================
19
20 #include "libmscore/fret.h"
21 #include "libmscore/measure.h"
22 #include "libmscore/system.h"
23 #include "libmscore/score.h"
24 //#include "libmscore/stringdata.h"
25 #include "fretcanvas.h"
26 #include "preferences.h"
27 #include "libmscore/chord.h"
28 #include "libmscore/note.h"
29 #include "libmscore/segment.h"
30 #include "libmscore/undo.h"
31 #include "musescore.h"
32
33 namespace Ms {
34
35 //---------------------------------------------------------
36 // FretCanvas
37 //---------------------------------------------------------
38
FretCanvas(QWidget * parent)39 FretCanvas::FretCanvas(QWidget* parent)
40 : QFrame(parent)
41 {
42 setAcceptDrops(true);
43 // setFrameStyle(QFrame::Raised | QFrame::Panel);
44 cstring = -2;
45 cfret = -2;
46 _currentDtype = FretDotType::NORMAL;
47 }
48
49 //---------------------------------------------------------
50 // paintEvent
51 //---------------------------------------------------------
52
paintEvent(QPaintEvent * ev)53 void FretCanvas::paintEvent(QPaintEvent* ev)
54 {
55 double mag = 1.5;
56 double _spatium = 20.0 * mag;
57 double lw1 = _spatium * 0.08;
58 int fretOffset = diagram->fretOffset();
59 double lw2 = (fretOffset || !diagram->showNut()) ? lw1 : _spatium * 0.2;
60 double stringDist = _spatium * .7;
61 double fretDist = _spatium * .8;
62 int _strings = diagram->strings();
63 int _frets = diagram->frets();
64 double dotd = stringDist * .6 + lw1;
65
66 double w = (_strings - 1) * stringDist;
67 double xo = (width() - w) * .5;
68 double h = (_frets * fretDist) + fretDist * .5;
69 double yo = (height() - h) * .5;
70
71 QFont font("FreeSans");
72 int size = lrint(18.0 * mag);
73 font.setPixelSize(size);
74
75 QPainter p(this);
76 p.setRenderHint(QPainter::Antialiasing, preferences.getBool(PREF_UI_CANVAS_MISC_ANTIALIASEDDRAWING));
77 p.setRenderHint(QPainter::TextAntialiasing, true);
78 p.translate(xo, yo);
79
80 QPen pen(p.pen());
81 pen.setWidthF(lw2);
82 pen.setCapStyle(Qt::FlatCap);
83 p.setPen(pen);
84 p.setBrush(pen.color());
85 double x2 = (_strings-1) * stringDist;
86 p.drawLine(QLineF(-lw1 * .5, 0.0, x2 + lw1 * .5, 0.0));
87
88 pen.setWidthF(lw1);
89 p.setPen(pen);
90 double y2 = (_frets+1) * fretDist - fretDist*.5;
91
92 QPen symPen(pen);
93 symPen.setCapStyle(Qt::RoundCap);
94 symPen.setWidthF(lw1 * 1.2);
95
96 // Draw strings and frets
97 for (int i = 0; i < _strings; ++i) {
98 double x = stringDist * i;
99 p.drawLine(QLineF(x, fretOffset ? -_spatium*.2 : 0.0, x, y2));
100 }
101 for (int i = 1; i <= _frets; ++i) {
102 double y = fretDist * i;
103 p.drawLine(QLineF(0.0, y, x2, y));
104 }
105
106 // Draw dots and markers
107 for (int i = 0; i < _strings; ++i) {
108 for (auto const& d : diagram->dot(i)) {
109 if (d.exists()) {
110 p.setPen(symPen);
111 int fret = d.fret;
112 double x = stringDist * i - dotd * .5;
113 double y = fretDist * (fret - 1) + fretDist * .5 - dotd * .5;
114
115 paintDotSymbol(p, symPen, x, y, dotd, d.dtype);
116 }
117 }
118 p.setPen(pen);
119
120 FretItem::Marker mark = diagram->marker(i);
121 if (mark.exists()) {
122 p.setFont(font);
123 double x = stringDist * i;
124 double y = -fretDist * .1;
125 p.drawText(QRectF(x, y, 0.0, 0.0),
126 Qt::AlignHCenter | Qt::AlignBottom | Qt::TextDontClip, FretItem::markerToChar(mark.mtype));
127 }
128 }
129
130 // Draw barres
131 p.setPen(pen);
132 for (auto const& i : diagram->barres()) {
133 int fret = i.first;
134 int startString = i.second.startString;
135 int endString = i.second.endString;
136
137 qreal x1 = stringDist * startString;
138 qreal newX2 = endString == -1 ? x2 : stringDist * endString;
139
140 qreal y = fretDist * (fret - 1) + fretDist * .5;
141 pen.setWidthF(dotd * diagram->score()->styleD(Sid::barreLineWidth)); // don't use style barreLineWidth - why not?
142 pen.setCapStyle(Qt::RoundCap);
143 p.setPen(pen);
144 p.drawLine(QLineF(x1, y, newX2, y));
145 }
146
147 // Draw 'hover' dot
148 if ((cfret > 0) && (cfret <= _frets) && (cstring >= 0) && (cstring < _strings)) {
149 FretItem::Dot cd = diagram->dot(cstring, cfret)[0];
150 std::vector<FretItem::Dot> otherDots = diagram->dot(cstring);
151 FretDotType dtype;
152 symPen.setColor(Qt::lightGray);
153
154 if (cd.exists()) {
155 dtype = cd.dtype;
156 symPen.setColor(Qt::red);
157 }
158 else {
159 dtype = _automaticDotType ? FretDotType::NORMAL : _currentDtype;
160 }
161 p.setPen(symPen);
162
163 double x = stringDist * cstring - dotd * .5;
164 double y = fretDist * (cfret-1) + fretDist * .5 - dotd * .5;
165 p.setBrush(Qt::lightGray);
166 paintDotSymbol(p, symPen, x, y, dotd, dtype);
167 }
168
169 if (fretOffset > 0) {
170 qreal fretNumMag = 2.0; // TODO: get the value from Sid::fretNumMag
171 QFont scaledFont(font);
172 scaledFont.setPixelSize(font.pixelSize() * fretNumMag);
173 p.setFont(scaledFont);
174 p.setPen(pen);
175 // Todo: make dependent from Sid::fretNumPos
176 p.drawText(QRectF(-stringDist * .4, 0.0, 0.0, fretDist),
177 Qt::AlignVCenter|Qt::AlignRight|Qt::TextDontClip,
178 QString("%1").arg(fretOffset+1));
179 p.setFont(font);
180 }
181
182 QFrame::paintEvent(ev);
183 }
184
185 //---------------------------------------------------------
186 // paintDotSymbol
187 //---------------------------------------------------------
188
paintDotSymbol(QPainter & p,QPen & pen,qreal x,qreal y,qreal dotd,FretDotType dtype)189 void FretCanvas::paintDotSymbol(QPainter& p, QPen& pen, qreal x, qreal y, qreal dotd, FretDotType dtype)
190 {
191 switch (dtype) {
192 case FretDotType::CROSS:
193 p.drawLine(QLineF(x, y, x + dotd, y + dotd));
194 p.drawLine(QLineF(x + dotd, y, x, y + dotd));
195 break;
196 case FretDotType::SQUARE:
197 p.setBrush(Qt::NoBrush);
198 p.drawRect(QRectF(x, y, dotd, dotd));
199 break;
200 case FretDotType::TRIANGLE:
201 p.drawLine(QLineF(x, y + dotd, x + .5 * dotd, y));
202 p.drawLine(QLineF(x + .5 * dotd, y, x + dotd, y + dotd));
203 p.drawLine(QLineF(x + dotd, y + dotd, x, y + dotd));
204 break;
205 case FretDotType::NORMAL:
206 default:
207 p.setBrush(pen.color());
208 p.setPen(Qt::NoPen);
209 p.drawEllipse(QRectF(x, y, dotd, dotd));
210 break;
211 }
212 }
213
214 //---------------------------------------------------------
215 // getPosition
216 //---------------------------------------------------------
217
getPosition(const QPointF & p,int * string,int * fret)218 void FretCanvas::getPosition(const QPointF& p, int* string, int* fret)
219 {
220 double mag = 1.5;
221 double _spatium = 20.0 * mag;
222 int _strings = diagram->strings();
223 int _frets = diagram->frets();
224 double stringDist = _spatium * .7;
225 double fretDist = _spatium * .8;
226
227 double w = (_strings - 1) * stringDist;
228 double xo = (width() - w) * .5;
229 double h = (_frets * fretDist) + fretDist * .5;
230 double yo = (height() - h) * .5;
231 *fret = (p.y() - yo + fretDist) / fretDist;
232 *string = (p.x() - xo + stringDist * .5) / stringDist;
233 }
234
235 //---------------------------------------------------------
236 // mousePressEvent
237 //---------------------------------------------------------
238
mousePressEvent(QMouseEvent * ev)239 void FretCanvas::mousePressEvent(QMouseEvent* ev)
240 {
241 int string;
242 int fret;
243 getPosition(ev->pos(), &string, &fret);
244
245 int _strings = diagram->strings();
246 int _frets = diagram->frets();
247 if (fret < 0 || fret > _frets || string < 0 || string >= _strings)
248 return;
249
250 bool haveShift = (ev->modifiers() & Qt::ShiftModifier) || _barreMode;
251 bool haveCtrl = (ev->modifiers() & Qt::ControlModifier) || _multidotMode;
252
253 diagram->score()->startCmd();
254
255 // Click above the fret diagram, so change the open/closed string marker
256 if (fret == 0) {
257 if (!haveCtrl)
258 diagram->undoSetFretDot(string, 0);
259 switch (diagram->marker(string).mtype) {
260 case FretMarkerType::CIRCLE:
261 diagram->undoSetFretMarker(string, FretMarkerType::CROSS);
262 break;
263 case FretMarkerType::CROSS:
264 diagram->undoSetFretMarker(string, FretMarkerType::NONE);
265 break;
266 case FretMarkerType::NONE:
267 default:
268 diagram->undoSetFretMarker(string, FretMarkerType::CIRCLE);
269 break;
270 }
271 }
272 // Otherwise, the click is on the fretboard itself
273 else {
274 FretItem::Dot thisDot = diagram->dot(string, fret)[0];
275
276 // Click on an existing dot
277 if (thisDot.exists() && !haveShift)
278 diagram->undoSetFretDot(string, haveCtrl ? fret : 0, haveCtrl);
279 else {
280 // Shift adds a barre
281 if (haveShift)
282 diagram->undoSetFretBarre(string, fret, haveCtrl);
283 else {
284 FretDotType dtype = FretDotType::NORMAL;
285 if (_automaticDotType && haveCtrl && diagram->dot(string)[0].exists()) {
286 dtype = FretDotType::TRIANGLE;
287
288 std::vector<FretDotType> dtypes {
289 FretDotType::NORMAL,
290 FretDotType::CROSS,
291 FretDotType::SQUARE,
292 FretDotType::TRIANGLE
293 };
294
295 // Find the lowest dot type that doesn't already exist on the string
296 for (size_t i = 0; i < dtypes.size(); i++) {
297 FretDotType t = dtypes[i];
298
299 bool hasThisType = false;
300 for (auto const& dot : diagram->dot(string)) {
301 if (dot.dtype == t) {
302 hasThisType = true;
303 break;
304 }
305 }
306
307 if (hasThisType)
308 continue;
309
310 dtype = t;
311 break;
312 }
313 }
314 else if (!_automaticDotType)
315 dtype = _currentDtype;
316
317 // Ctrl adds a dot without removing other dots on a string
318 diagram->undoSetFretDot(string, fret, haveCtrl, dtype);
319 }
320 }
321 }
322 diagram->triggerLayout();
323 diagram->score()->endCmd();
324 update();
325 }
326
327 //---------------------------------------------------------
328 // mouseMoveEvent
329 //---------------------------------------------------------
330
mouseMoveEvent(QMouseEvent * ev)331 void FretCanvas::mouseMoveEvent(QMouseEvent* ev)
332 {
333 int string;
334 int fret;
335 getPosition(ev->pos(), &string, &fret);
336 if (string != cstring || cfret != fret) {
337 cfret = fret;
338 cstring = string;
339 update();
340 }
341 }
342
343 //---------------------------------------------------------
344 // setFretDiagram
345 //---------------------------------------------------------
346
setFretDiagram(FretDiagram * fd)347 void FretCanvas::setFretDiagram(FretDiagram* fd)
348 {
349 diagram = fd;
350 update();
351 }
352
353 //---------------------------------------------------------
354 // clear
355 //---------------------------------------------------------
356
clear()357 void FretCanvas::clear()
358 {
359 diagram->score()->startCmd();
360 diagram->undoFretClear();
361 diagram->score()->endCmd();
362 update();
363 }
364 } // namespace Ms
365