MuseScore/mscore/continuouspanel.cpp
2017-01-19 16:06:40 +01:00

425 lines
16 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2013 Werner Schweer
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
//=============================================================================
#include "libmscore/score.h"
#include "libmscore/measure.h"
#include "libmscore/segment.h"
#include "libmscore/system.h"
#include "libmscore/staff.h"
#include "libmscore/page.h"
#include "libmscore/sym.h"
#include "libmscore/instrument.h"
#include "libmscore/part.h"
#include "libmscore/timesig.h"
#include "libmscore/keysig.h"
#include "libmscore/barline.h"
#include "libmscore/rest.h"
#include "libmscore/stafflines.h"
#include "preferences.h"
#include "scoreview.h"
#include "continuouspanel.h"
namespace Ms {
//---------------------------------------------------------
// ContinuousPanel
//---------------------------------------------------------
ContinuousPanel::ContinuousPanel(ScoreView* sv)
{
_sv = sv;
_active = true;
_visible = false;
_width = 0.0;
}
//---------------------------------------------------------
// paintContinousPanel
//---------------------------------------------------------
void ContinuousPanel::paint(const QRect&, QPainter& painter)
{
qreal _offsetPanel = 0;
qreal _y = 0;
qreal _oldWidth = 0; // The last final panel width
qreal _newWidth = 0; // New panel width
qreal _height = 0;
qreal _leftMarginTotal = 0; // Sum of all elments left margin
qreal _panelRightPadding = 5; // Extra space for the panel after last element
Measure* measure = _score->firstMeasure();
if (!_active || !measure) {
_visible = false;
return;
}
if (measure->mmRest()) {
measure = measure->mmRest();
}
System* system = measure->system();
if (system == 0) {
_visible = false;
return;
}
Segment* s = measure->first();
double _spatium = _score->spatium();
if (_width <= 0)
_width = s->x();
//
// Set panel height for whole system
//
_height = 6 * _spatium;
_y = system->staffYpage(0) + system->page()->pos().y();
double y2 = 0.0;
for (int i = 0; i < _score->nstaves(); ++i) {
SysStaff* ss = system->staff(i);
if (!ss->show() || !_score->staff(i)->show())
continue;
y2 = ss->y() + ss->bbox().height();
}
_height += y2 + 6*_spatium;
_y -= 6 * _spatium;
//
// Check elements at current panel position
//
_offsetPanel = -(_sv->xoffset()) / _sv->mag();
_rect = QRect(_offsetPanel + _width, _y, 1, _height);
Page* page = _score->pages().front();
QList<Element*> el = page->items(_rect);
if (el.empty()) {
_visible = false;
return;
}
qStableSort(el.begin(), el.end(), elementLessThan);
const Measure*_currentMeasure = 0;
for (const Element* e : el) {
e->itemDiscovered = 0;
if (!e->visible() && !_score->showInvisible())
continue;
if (e->isMeasure()) {
_currentMeasure = toMeasure(e);
break;
}
}
if (!_currentMeasure)
return;
// Don't show panel if staff names are visible
if (_currentMeasure == _score->firstMeasure() && _sv->toPhysical(_currentMeasure->canvasPos()).x() > 0) {
_visible = false;
return;
}
qreal _xPosMeasure = _currentMeasure->canvasX();
qreal _measureWidth = _currentMeasure->width();
int tick = _currentMeasure->tick();
Fraction _currentTimeSig = _currentMeasure->timesig();
//qDebug() << "_sv->xoffset()=" <<_sv->xoffset() << " _sv->mag()="<< _sv->mag() <<" s->x=" << s->x() << " width=" << _width << " currentMeasure=" << _currentMeasure->x() << " _xPosMeasure=" << _xPosMeasure;
//---------------------------------------------------------
// findElementWidths
// determines the max width for each element types
//---------------------------------------------------------
// The first pass serves to get the maximum width for each elements
qreal lineWidthName = 0;
qreal _widthClef = 0;
qreal _widthKeySig = 0;
qreal _widthTimeSig = 0;
qreal _xPosTimeSig = 0;
for (const Element* e : el) {
e->itemDiscovered = 0;
if (!e->visible() && !_score->showInvisible())
continue;
if (e->isRest() && toRest(e)->isGap())
continue;
if (e->isStaffLines()) {
Staff* currentStaff = _score->staff(e->staffIdx());
Segment* parent = _score->tick2segment(tick);
// Find maximum width for the staff name
QList<StaffName>& staffNamesLong = currentStaff->part()->instrument()->longNames();
QString staffName = staffNamesLong.isEmpty() ? " " : staffNamesLong[0].name();
if (staffName == "") {
QList<StaffName>& staffNamesShort = currentStaff->part()->instrument()->shortNames();
staffName = staffNamesShort.isEmpty() ? "" : staffNamesShort[0].name();
}
Text* newName = new Text(_score);
newName->setXmlText(staffName);
newName->setParent(parent);
newName->setTrack(e->track());
newName->setFamily("FreeSans");
newName->setSizeIsSpatiumDependent(true);
newName->layout();
newName->setPlainText(newName->plainText());
newName->layout();
// Find maximum width for the current Clef
Clef* newClef = new Clef(_score);
ClefType currentClef = currentStaff->clef(tick);
newClef->setClefType(currentClef);
newClef->setParent(parent);
newClef->setTrack(e->track());
newClef->layout();
if (newClef->width() > _widthClef)
_widthClef = newClef->width();
// Find maximum width for the current KeySignature
KeySig* newKs = new KeySig(_score);
KeySigEvent currentKeySigEvent = currentStaff->keySigEvent(tick);
newKs->setKeySigEvent(currentKeySigEvent);
// The Parent and the Track must be set to have the key signature layout adjusted to different clefs
// This also adds naturals to the key signature (if set in the score style)
newKs->setParent(parent);
newKs->setTrack(e->track());
newKs->setHideNaturals(true);
newKs->layout();
if (newKs->width() > _widthKeySig)
_widthKeySig = newKs->width();
// Find maximum width for the current TimeSignature
TimeSig* newTs = new TimeSig(_score);
// Try to get local time signature, if not, get the current measure one
TimeSig* currentTimeSig = currentStaff->timeSig(tick);
if (currentTimeSig)
newTs->setFrom(currentTimeSig);
else
newTs->setSig(Fraction(_currentTimeSig.numerator(), _currentTimeSig.denominator()), TimeSigType::NORMAL);
newTs->setParent(parent);
newTs->setTrack(e->track());
newTs->layout();
if ((newName->width() > lineWidthName) && (newName->xmlText() != ""))
lineWidthName = newName->width();
if (newTs->width() > _widthTimeSig)
_widthTimeSig = newTs->width();
delete newClef;
delete newName;
delete newKs;
delete newTs;
}
}
_leftMarginTotal = _score->styleP(StyleIdx::clefLeftMargin);
_leftMarginTotal += _score->styleP(StyleIdx::keysigLeftMargin);
_leftMarginTotal += _score->styleP(StyleIdx::timesigLeftMargin);
_newWidth = _widthClef + _widthKeySig + _widthTimeSig + _leftMarginTotal + _panelRightPadding;
_xPosMeasure -= _offsetPanel;
lineWidthName += _score->spatium() + _score->styleP(StyleIdx::clefLeftMargin) + _widthClef;
if (_newWidth < lineWidthName) {
_newWidth = lineWidthName;
_oldWidth = 0;
}
if (_oldWidth == 0) {
_oldWidth = _newWidth;
_width = _newWidth;
}
else if (_newWidth > 0) {
if (_newWidth == _width) {
_oldWidth = _width;
_width = _newWidth;
}
else if (((_xPosMeasure <= _newWidth) && (_xPosMeasure >= _oldWidth)) ||
((_xPosMeasure >= _newWidth) && (_xPosMeasure <= _oldWidth)))
_width = _xPosMeasure;
else if (((_xPosMeasure+_measureWidth <= _newWidth) && (_xPosMeasure+_measureWidth >= _oldWidth)) ||
((_xPosMeasure+_measureWidth >= _newWidth) && (_xPosMeasure+_measureWidth <= _oldWidth)))
_width = _xPosMeasure+_measureWidth;
else {
_oldWidth = _width;
_width = _newWidth;
}
}
_rect = QRect(0, _y, _width, _height);
//====================
painter.save();
// Draw colored rectangle
painter.setClipping(false);
QPointF pos(_offsetPanel, 0);
painter.translate(pos);
QPen pen;
pen.setWidthF(0.0);
pen.setStyle(Qt::NoPen);
painter.setPen(pen);
painter.setBrush(preferences.fgColor);
QRectF bg(_rect);
bg.setWidth(_widthClef + _widthKeySig + _widthTimeSig + _leftMarginTotal + _panelRightPadding);
QPixmap* fgPixmap = _sv->fgPixmap();
if (fgPixmap == 0 || fgPixmap->isNull())
painter.fillRect(bg, preferences.fgColor);
else {
painter.setMatrixEnabled(false);
painter.drawTiledPixmap(bg, *fgPixmap, bg.topLeft()
- QPoint(lrint(_sv->matrix().dx()), lrint(_sv->matrix().dy())));
painter.setMatrixEnabled(true);
}
painter.setClipRect(_rect);
painter.setClipping(true);
QColor color(MScore::layoutBreakColor);
// Draw measure text number
// TODO: simplify (no Text element)
QString text = QString("#%1").arg(_currentMeasure->no()+1);
Text* newElement = new Text(_score);
newElement->setFlag(ElementFlag::MOVABLE, false);
newElement->setXmlText(text);
newElement->setFamily("FreeSans");
newElement->setSizeIsSpatiumDependent(true);
newElement->setColor(color);
newElement->layout1();
pos = QPointF(_score->styleP(StyleIdx::clefLeftMargin) + _widthClef, _y + newElement->height());
painter.translate(pos);
newElement->draw(&painter);
pos += QPointF(_offsetPanel, 0);
painter.translate(-pos);
delete newElement;
// This second pass draws the elements spaced evently using the width of the largest element
for (const Element* e : el) {
if (!e->visible() && !_score->showInvisible())
continue;
if (e->isRest() && toRest(e)->isGap())
continue;
if (e->isStaffLines()) {
painter.save();
Staff* currentStaff = _score->staff(e->staffIdx());
Segment* parent = _score->tick2segmentMM(tick);
pos = QPointF (_offsetPanel, e->pagePos().y());
painter.translate(pos);
// Draw staff lines
StaffLines newStaffLines(*toStaffLines(e));
newStaffLines.setParent(parent);
newStaffLines.setTrack(e->track());
newStaffLines.layout();
newStaffLines.setColor(color);
newStaffLines.setWidth(bg.width());
newStaffLines.draw(&painter);
// Draw barline
BarLine barLine(_score);
barLine.setBarLineType(BarLineType::NORMAL);
barLine.setParent(parent);
barLine.setTrack(e->track());
barLine.setSpanStaff(currentStaff->barLineSpan());
barLine.setSpanFrom(currentStaff->barLineFrom());
barLine.setSpanTo(currentStaff->barLineTo());
barLine.layout();
barLine.setColor(color);
barLine.draw(&painter);
// Draw the current staff name
QList<StaffName>& staffNamesLong = currentStaff->part()->instrument()->longNames();
QString staffName = staffNamesLong.isEmpty() ? " " : staffNamesLong[0].name();
if (staffName == "") {
QList<StaffName>& staffNamesShort = currentStaff->part()->instrument()->shortNames();
staffName = staffNamesShort.isEmpty() ? "" : staffNamesShort[0].name();
}
Text* newName = new Text(_score);
newName->setXmlText(staffName);
newName->setParent(parent);
newName->setTrack(e->track());
newName->setColor(color);
newName->setFamily("FreeSans");
newName->setSizeIsSpatiumDependent(true);
newName->layout();
newName->setPlainText(newName->plainText());
newName->layout();
if (currentStaff->part()->staff(0) == currentStaff) {
double _spatium = _score->spatium();
pos = QPointF (_score->styleP(StyleIdx::clefLeftMargin) + _widthClef, -_spatium * 2);
painter.translate(pos);
newName->draw(&painter);
painter.translate(-pos);
}
delete newName;
qreal posX = 0.0;
// Draw the current Clef
Clef clef(_score);
clef.setClefType(currentStaff->clef(tick));
clef.setParent(parent);
clef.setTrack(e->track());
clef.setColor(color);
clef.layout();
posX += _score->styleP(StyleIdx::clefLeftMargin);
clef.drawAt(&painter, QPointF(posX, clef.pos().y()));
posX += _widthClef;
// Draw the current KeySignature
KeySig newKs(_score);
newKs.setKeySigEvent(currentStaff->keySigEvent(tick));
// The Parent and the track must be set to have the key signature layout adjusted to different clefs
// This also adds naturals to the key signature (if set in the score style)
newKs.setParent(parent);
newKs.setTrack(e->track());
newKs.setColor(color);
newKs.setHideNaturals(true);
newKs.layout();
posX += _score->styleP(StyleIdx::keysigLeftMargin);
newKs.drawAt(&painter, QPointF(posX, 0.0));
posX += _widthKeySig + _xPosTimeSig;
// Draw the current TimeSignature
TimeSig newTs(_score);
// Try to get local time signature, if not, get the current measure one
TimeSig* currentTimeSig = currentStaff->timeSig(tick);
if (currentTimeSig) {
newTs.setFrom(currentTimeSig);
newTs.setParent(parent);
newTs.setTrack(e->track());
newTs.setColor(color);
newTs.layout();
posX += _score->styleP(StyleIdx::timesigLeftMargin);
newTs.drawAt(&painter, QPointF(posX, 0.0));
}
painter.restore();
}
}
painter.restore();
_visible = true;
}
}