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