1 //=============================================================================
2 //  MuseScore
3 //  Music Composition & Notation
4 //
5 //  Copyright (C) 2013 Werner Schweer
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 //  as published by the Free Software Foundation and appearing in
10 //  the file LICENCE.GPL
11 //=============================================================================
12 
13 #include "libmscore/score.h"
14 #include "libmscore/measure.h"
15 #include "libmscore/segment.h"
16 #include "libmscore/system.h"
17 #include "libmscore/staff.h"
18 #include "libmscore/page.h"
19 #include "libmscore/sym.h"
20 #include "libmscore/instrument.h"
21 #include "libmscore/part.h"
22 #include "libmscore/timesig.h"
23 #include "libmscore/keysig.h"
24 #include "libmscore/barline.h"
25 #include "libmscore/rest.h"
26 #include "libmscore/stafflines.h"
27 
28 #include "preferences.h"
29 #include "scoreview.h"
30 #include "continuouspanel.h"
31 
32 namespace Ms {
33 
34 //---------------------------------------------------------
35 //   ContinuousPanel
36 //---------------------------------------------------------
37 
ContinuousPanel(ScoreView * sv)38 ContinuousPanel::ContinuousPanel(ScoreView* sv)
39       {
40       _sv         = sv;
41       _score      = nullptr;
42       _active     = true;
43       _visible    = false;
44       _width      = 0.0;
45       }
46 
47 //---------------------------------------------------------
48 //   paintContinousPanel
49 //---------------------------------------------------------
50 
paint(const QRect &,QPainter & painter)51 void ContinuousPanel::paint(const QRect&, QPainter& painter)
52       {
53       qreal _offsetPanel = 0;
54       qreal _y = 0;
55       qreal _oldWidth = 0;        // The last final panel width
56       qreal _newWidth = 0;        // New panel width
57       qreal _height = 0;
58       qreal _leftMarginTotal = 0; // Sum of all elements left margin
59       qreal _panelRightPadding = 5;  // Extra space for the panel after last element
60 
61       Measure* measure = _score->firstMeasure();
62 
63       if (!_active || !measure) {
64             _visible = false;
65             return;
66             }
67 
68       if (measure->mmRest()) {
69             measure = measure->mmRest();
70             }
71 
72       System* system   = measure->system();
73       if (system == 0) {
74             _visible = false;
75             return;
76             }
77 
78       Segment* s      = measure->first();
79       double _spatium = _score->spatium();
80       if (_width <= 0)
81             _width  = s->x();
82 
83       //
84       // Set panel height for whole system
85       //
86       _height = 6 * _spatium;
87       _y = system->staffYpage(0) + system->page()->pos().y();
88       double y2 = 0.0;
89       for (int i = 0; i < _score->nstaves(); ++i) {
90             SysStaff* ss = system->staff(i);
91             if (!ss->show() || !_score->staff(i)->show())
92                   continue;
93             y2 = ss->y() + ss->bbox().height();
94             }
95       _height += y2 + 6*_spatium;
96       _y -= 6 * _spatium;
97 
98       //
99       // Check elements at current panel position
100       //
101       _offsetPanel = -(_sv->xoffset()) / _sv->physicalZoomLevel();
102       _rect        = QRect(_offsetPanel + _width, _y, 1, _height);
103       Page* page   = _score->pages().front();
104       QList<Element*> el = page->items(_rect);
105       if (el.empty()) {
106             _visible = false;
107             return;
108             }
109       std::stable_sort(el.begin(), el.end(), elementLessThan);
110 
111       const Measure*_currentMeasure = 0;
112       for (const Element* e : qAsConst(el)) {
113             e->itemDiscovered = 0;
114             if (!e->visible() && !_score->showInvisible())
115                   continue;
116 
117             if (e->isStaffLines()) {
118                   _currentMeasure = toStaffLines(e)->measure();
119                   break;
120                   }
121             }
122       if (!_currentMeasure)
123             return;
124 
125       // Don't show panel if staff names are visible
126       if (_currentMeasure == _score->firstMeasure() && _sv->toPhysical(_currentMeasure->canvasPos()).x() > 0) {
127             _visible = false;
128             return;
129             }
130 
131       qreal _xPosMeasure       = _currentMeasure->canvasX();
132       qreal _measureWidth      = _currentMeasure->width();
133       int tick                 = _currentMeasure->tick().ticks();
134       Fraction _currentTimeSig = _currentMeasure->timesig();
135       //qDebug() << "_sv->xoffset()=" <<_sv->xoffset() << " _sv->physicalZoomLevel()="<< _sv->physicalZoomLevel() <<" s->x=" << s->x() << " width=" << _width << " currentMeasure=" << _currentMeasure->x() << " _xPosMeasure=" << _xPosMeasure;
136 
137       //---------------------------------------------------------
138       //   findElementWidths
139       //      determines the max width for each element types
140       //---------------------------------------------------------
141 
142       // The first pass serves to get the maximum width for each elements
143 
144       qreal lineWidthName = 0;
145       qreal _widthClef    = 0;
146       qreal _widthKeySig  = 0;
147       qreal _widthTimeSig = 0;
148       qreal _xPosTimeSig  = 0;
149 
150       for (const Element* e : qAsConst(el)) {
151             e->itemDiscovered = 0;
152             if (!e->visible() && !_score->showInvisible())
153                   continue;
154 
155             if (e->isRest() && toRest(e)->isGap())
156                   continue;
157 
158             if (e->isStaffLines()) {
159                   Staff* currentStaff = _score->staff(e->staffIdx());
160                   Segment* parent = _score->tick2segment(Fraction::fromTicks(tick));
161 
162                   // Find maximum width for the staff name
163                   QList<StaffName>& staffNamesLong = currentStaff->part()->instrument(Fraction::fromTicks(tick))->longNames();
164                   QString staffName = staffNamesLong.isEmpty() ? " " : staffNamesLong[0].name();
165                   if (staffName == "") {
166                         QList<StaffName>& staffNamesShort = currentStaff->part()->instrument(Fraction::fromTicks(tick))->shortNames();
167                         staffName = staffNamesShort.isEmpty() ? "" : staffNamesShort[0].name();
168                         }
169                   Text* newName = new Text(_score);
170                   newName->setXmlText(staffName);
171                   newName->setParent(parent);
172                   newName->setTrack(e->track());
173                   newName->setFamily("FreeSans");
174                   newName->setSizeIsSpatiumDependent(true);
175                   newName->layout();
176                   newName->setPlainText(newName->plainText());
177                   newName->layout();
178 
179                   // Find maximum width for the current Clef
180                   Clef* newClef = new Clef(_score);
181                   ClefType currentClef = currentStaff->clef(Fraction::fromTicks(tick));
182                   newClef->setClefType(currentClef);
183                   newClef->setParent(parent);
184                   newClef->setTrack(e->track());
185                   newClef->layout();
186                   if (newClef->width() > _widthClef)
187                         _widthClef = newClef->width();
188 
189                   // Find maximum width for the current KeySignature
190                   KeySig* newKs = new KeySig(_score);
191                   KeySigEvent currentKeySigEvent = currentStaff->keySigEvent(Fraction::fromTicks(tick));
192                   newKs->setKeySigEvent(currentKeySigEvent);
193                   // The Parent and the Track must be set to have the key signature layout adjusted to different clefs
194                   // This also adds naturals to the key signature (if set in the score style)
195                   newKs->setParent(parent);
196                   newKs->setTrack(e->track());
197                   newKs->setHideNaturals(true);
198                   newKs->layout();
199                   if (newKs->width() > _widthKeySig)
200                         _widthKeySig = newKs->width();
201 
202                   // Find maximum width for the current TimeSignature
203                   TimeSig* newTs = new TimeSig(_score);
204 
205                   // Try to get local time signature, if not, get the current measure one
206                   TimeSig* currentTimeSig = currentStaff->timeSig(Fraction::fromTicks(tick));
207                   if (currentTimeSig)
208                         newTs->setFrom(currentTimeSig);
209                   else
210                         newTs->setSig(Fraction(_currentTimeSig.numerator(), _currentTimeSig.denominator()), TimeSigType::NORMAL);
211                   newTs->setParent(parent);
212                   newTs->setTrack(e->track());
213                   newTs->layout();
214 
215                   if ((newName->width() > lineWidthName) && (newName->xmlText() != ""))
216                         lineWidthName = newName->width();
217 
218                   if (newTs->width() > _widthTimeSig)
219                         _widthTimeSig = newTs->width();
220 
221                   delete newClef;
222                   delete newName;
223                   delete newKs;
224                   delete newTs;
225                  }
226             }
227 
228       _leftMarginTotal = _score->styleP(Sid::clefLeftMargin);
229       _leftMarginTotal += _score->styleP(Sid::keysigLeftMargin);
230       _leftMarginTotal += _score->styleP(Sid::timesigLeftMargin);
231 
232       _newWidth = _widthClef + _widthKeySig + _widthTimeSig + _leftMarginTotal + _panelRightPadding;
233       _xPosMeasure -= _offsetPanel;
234 
235       lineWidthName += _score->spatium() + _score->styleP(Sid::clefLeftMargin) + _widthClef;
236       if (_newWidth < lineWidthName) {
237             _newWidth = lineWidthName;
238             _oldWidth = 0;
239             }
240       if (_oldWidth == 0) {
241             _oldWidth = _newWidth;
242             _width = _newWidth;
243             }
244       else if (_newWidth > 0) {
245             if (_newWidth == _width) {
246                   _oldWidth = _width;
247                   _width = _newWidth;
248                   }
249             else if (((_xPosMeasure <= _newWidth) && (_xPosMeasure >= _oldWidth)) ||
250                      ((_xPosMeasure >= _newWidth) && (_xPosMeasure <= _oldWidth)))
251                         _width = _xPosMeasure;
252             else if (((_xPosMeasure+_measureWidth <= _newWidth) && (_xPosMeasure+_measureWidth >= _oldWidth)) ||
253                      ((_xPosMeasure+_measureWidth >= _newWidth) && (_xPosMeasure+_measureWidth <= _oldWidth)))
254                         _width = _xPosMeasure+_measureWidth;
255             else {
256                   _oldWidth = _width;
257                   _width = _newWidth;
258                   }
259             }
260 
261       _rect = QRect(0, _y, _width, _height);
262 
263       //====================
264 
265       painter.save();
266 
267       // Draw colored rectangle
268       painter.setClipping(false);
269       QPointF pos(_offsetPanel, 0);
270 
271       painter.translate(pos);
272       QPen pen;
273       pen.setWidthF(0.0);
274       pen.setStyle(Qt::NoPen);
275       painter.setPen(pen);
276       painter.setBrush(preferences.getColor(PREF_UI_CANVAS_FG_COLOR));
277       QRectF bg(_rect);
278 
279       bg.setWidth(_widthClef + _widthKeySig + _widthTimeSig + _leftMarginTotal + _panelRightPadding);
280       QPixmap* fgPixmap = _sv->fgPixmap();
281       if (fgPixmap == 0 || fgPixmap->isNull())
282             painter.fillRect(bg, preferences.getColor(PREF_UI_CANVAS_FG_COLOR));
283       else
284             painter.drawTiledPixmap(bg, *fgPixmap, bg.topLeft()
285                - QPoint(lrint(_sv->matrix().dx()), lrint(_sv->matrix().dy())));
286 
287       painter.setClipRect(_rect);
288       painter.setClipping(true);
289 
290       QColor color(MScore::layoutBreakColor);
291 
292       // Draw measure text number
293       // TODO: simplify (no Text element)
294       QString text = QString("#%1").arg(_currentMeasure->no()+1);
295       Text* newElement = new Text(_score);
296       newElement->setFlag(ElementFlag::MOVABLE, false);
297       newElement->setXmlText(text);
298       newElement->setFamily("FreeSans");
299       newElement->setSizeIsSpatiumDependent(true);
300       newElement->setColor(color);
301       newElement->layout1();
302       pos = QPointF(_score->styleP(Sid::clefLeftMargin) + _widthClef, _y + newElement->height());
303       painter.translate(pos);
304       newElement->draw(&painter);
305       pos += QPointF(_offsetPanel, 0);
306       painter.translate(-pos);
307       delete newElement;
308 
309       // This second pass draws the elements spaced evently using the width of the largest element
310       for (const Element* e : qAsConst(el)) {
311             if (!e->visible() && !_score->showInvisible())
312                   continue;
313 
314             if (e->isRest() && toRest(e)->isGap())
315                   continue;
316 
317             if (e->isStaffLines()) {
318                   painter.save();
319                   Staff* currentStaff = _score->staff(e->staffIdx());
320                   Segment* parent = _score->tick2segmentMM(Fraction::fromTicks(tick));
321 
322                   pos = QPointF (_offsetPanel, e->pagePos().y());
323                   painter.translate(pos);
324 
325                   // Draw staff lines
326                   StaffLines newStaffLines(*toStaffLines(e));
327                   newStaffLines.setParent(parent->measure());
328                   newStaffLines.setTrack(e->track());
329                   newStaffLines.layoutForWidth(bg.width());
330                   newStaffLines.setColor(color);
331                   newStaffLines.draw(&painter);
332 
333                   // Draw barline
334                   BarLine barLine(_score);
335                   barLine.setBarLineType(BarLineType::NORMAL);
336                   barLine.setParent(parent);
337                   barLine.setTrack(e->track());
338                   barLine.setSpanStaff(currentStaff->barLineSpan());
339                   barLine.setSpanFrom(currentStaff->barLineFrom());
340                   barLine.setSpanTo(currentStaff->barLineTo());
341                   barLine.layout();
342                   barLine.setColor(color);
343                   barLine.draw(&painter);
344 
345                   // Draw the current staff name
346                   QList<StaffName>& staffNamesLong = currentStaff->part()->instrument(Fraction::fromTicks(tick))->longNames();
347                   QString staffName = staffNamesLong.isEmpty() ? " " : staffNamesLong[0].name();
348                   if (staffName == "") {
349                         QList<StaffName>& staffNamesShort = currentStaff->part()->instrument(Fraction::fromTicks(tick))->shortNames();
350                         staffName = staffNamesShort.isEmpty() ? "" : staffNamesShort[0].name();
351                         }
352 
353                   Text* newName = new Text(_score);
354                   newName->setXmlText(staffName);
355                   newName->setParent(parent);
356                   newName->setTrack(e->track());
357                   newName->setColor(color);
358                   newName->setFamily("FreeSans");
359                   newName->setSizeIsSpatiumDependent(true);
360                   newName->layout();
361                   newName->setPlainText(newName->plainText());
362                   newName->layout();
363                   if (currentStaff->part()->staff(0) == currentStaff) {
364                         const double spatium = _score->spatium();
365                         pos = QPointF (_score->styleP(Sid::clefLeftMargin) + _widthClef, -spatium * 2);
366                         painter.translate(pos);
367                         newName->draw(&painter);
368                         painter.translate(-pos);
369                         }
370                   delete newName;
371 
372                   qreal posX = 0.0;
373 
374                   // Draw the current Clef
375                   Clef clef(_score);
376                   clef.setClefType(currentStaff->clef(Fraction::fromTicks(tick)));
377                   clef.setParent(parent);
378                   clef.setTrack(e->track());
379                   clef.setColor(color);
380                   clef.layout();
381                   posX += _score->styleP(Sid::clefLeftMargin);
382                   clef.drawAt(&painter, QPointF(posX, clef.pos().y()));
383                   posX += _widthClef;
384 
385                   // Draw the current KeySignature
386                   KeySig newKs(_score);
387                   newKs.setKeySigEvent(currentStaff->keySigEvent(Fraction::fromTicks(tick)));
388 
389                   // The Parent and the track must be set to have the key signature layout adjusted to different clefs
390                   // This also adds naturals to the key signature (if set in the score style)
391                   newKs.setParent(parent);
392                   newKs.setTrack(e->track());
393                   newKs.setColor(color);
394                   newKs.setHideNaturals(true);
395                   newKs.layout();
396                   posX += _score->styleP(Sid::keysigLeftMargin);
397                   newKs.drawAt(&painter, QPointF(posX, 0.0));
398 
399                   posX += _widthKeySig + _xPosTimeSig;
400 
401                   // Draw the current TimeSignature
402                   TimeSig newTs(_score);
403 
404                   // Try to get local time signature, if not, get the current measure one
405                   TimeSig* currentTimeSig = currentStaff->timeSig(Fraction::fromTicks(tick));
406                   if (currentTimeSig) {
407                         newTs.setFrom(currentTimeSig);
408                         newTs.setParent(parent);
409                         newTs.setTrack(e->track());
410                         newTs.setColor(color);
411                         newTs.layout();
412                         posX += _score->styleP(Sid::timesigLeftMargin);
413                         newTs.drawAt(&painter, QPointF(posX, 0.0));
414                         }
415                   painter.restore();
416                   }
417             }
418       painter.restore();
419       _visible = true;
420       }
421 
422 }
423