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