MuseScore/mscore/excerptsdialog.cpp
2019-06-21 14:55:03 +02:00

609 lines
21 KiB
C++

//=============================================================================
// MuseScore
// Linux Music Score Editor
//
// Copyright (C) 2008 Werner Schweer and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "excerptsdialog.h"
#include "musescore.h"
#include "libmscore/score.h"
#include "libmscore/part.h"
#include "libmscore/undo.h"
#include "icons.h"
namespace Ms {
//---------------------------------------------------------
// ExcerptItem
//---------------------------------------------------------
ExcerptItem::ExcerptItem(Excerpt* e, QListWidget* parent)
: QListWidgetItem(parent)
{
_excerpt = e;
setText(e->title());
}
//---------------------------------------------------------
// PartItem
//---------------------------------------------------------
PartItem::PartItem(Part* p, QTreeWidget* parent)
: QTreeWidgetItem(parent)
{
setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable));
_part = p;
setText(0, p->partName().replace("/", "_"));
}
//---------------------------------------------------------
// InstrumentItem
//---------------------------------------------------------
InstrumentItem::InstrumentItem(PartItem* p, QListWidget* parent)
: QListWidgetItem(parent)
{
setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable));
_partItem = p;
setText(p->part()->partName().replace("/", "_"));
}
//---------------------------------------------------------
// StaffItem
//---------------------------------------------------------
StaffItem::StaffItem(PartItem* li)
: QTreeWidgetItem(li)
{
setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable));
for (int i = 1; i <= VOICES; i++) {
setCheckState(i, Qt::Checked);
}
}
void StaffItem::setData(int column, int role, const QVariant& value)
{
const bool isCheckChange = column > 0
&& role == Qt::CheckStateRole
&& data(column, role).isValid() // Don't "change" during initialization
&& checkState(column) != value;
QTreeWidgetItem::setData(column, role, value);
if (isCheckChange) {
int unchecked = 0;
for (int i = 1; i <= VOICES; i++) {
if (checkState(i) == Qt::Unchecked)
unchecked += 1;
}
if (unchecked == VOICES)
setCheckState(column, Qt::Checked);
}
}
//---------------------------------------------------------
// ExcerptsDialog
//---------------------------------------------------------
ExcerptsDialog::ExcerptsDialog(MasterScore* s, QWidget* parent)
: QDialog(parent)
{
setObjectName("PartEditor");
setupUi(this);
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
setModal(true);
score = s->masterScore();
for (Excerpt* e : score->excerpts()) {
ExcerptItem* ei = new ExcerptItem(e);
excerptList->addItem(ei);
}
QMultiMap<int, int> t;
for (Part* p : score->parts()) {
PartItem* pI = new PartItem(p);
InstrumentItem* item = new InstrumentItem(pI);
instrumentList->addItem(item);
}
connect(newButton, SIGNAL(clicked()), SLOT(newClicked()));
connect(newAllButton, SIGNAL(clicked()), SLOT(newAllClicked()));
connect(deleteButton, SIGNAL(clicked()), SLOT(deleteClicked()));
connect(moveUpButton, SIGNAL(clicked()), SLOT(moveUpClicked()));
connect(moveDownButton, SIGNAL(clicked()), SLOT(moveDownClicked()));
connect(addButton, SIGNAL(clicked()), SLOT(addButtonClicked()));
connect(removeButton, SIGNAL(clicked()), SLOT(removeButtonClicked()));
connect(excerptList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)),
SLOT(excerptChanged(QListWidgetItem*, QListWidgetItem*)));
connect(partList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
SLOT(partDoubleClicked(QTreeWidgetItem*, int)));
connect(partList, SIGNAL(itemClicked(QTreeWidgetItem*,int)), SLOT(partClicked(QTreeWidgetItem*,int)));
connect(instrumentList, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
SLOT(addButtonClicked()));
connect(title, SIGNAL(textChanged(const QString&)), SLOT(titleChanged(const QString&)));
moveUpButton->setIcon(*icons[int(Icons::arrowUp_ICON)]);
moveDownButton->setIcon(*icons[int(Icons::arrowDown_ICON)]);
for (int i = 1; i <= VOICES; i++) {
//partList->model()->setHeaderData(i, Qt::Horizontal, MScore::selectColor[i-1], Qt::BackgroundRole);
partList->header()->resizeSection(i, 30);
}
int n = score->excerpts().size();
if (n > 0)
excerptList->setCurrentRow(0);
moveDownButton->setEnabled(n > 1);
moveUpButton->setEnabled(false);
bool flag = excerptList->currentItem() != 0;
deleteButton->setEnabled(flag);
}
//---------------------------------------------------------
// startExcerptsDialog
//---------------------------------------------------------
void MuseScore::startExcerptsDialog()
{
if (cs == 0)
return;
ExcerptsDialog ed(cs->masterScore(), 0);
MuseScore::restoreGeometry(&ed);
ed.exec();
MuseScore::saveGeometry(&ed);
cs->setLayoutAll();
cs->update();
}
//---------------------------------------------------------
// newClicked
//---------------------------------------------------------
void ExcerptsDialog::newClicked()
{
QString name = createName("Part");
Excerpt* e = new Excerpt(score);
e->setTitle(name);
ExcerptItem* ei = new ExcerptItem(e);
excerptList->addItem(ei);
excerptList->selectionModel()->clearSelection();
excerptList->setCurrentItem(ei, QItemSelectionModel::SelectCurrent);
for (int i = 0; i < excerptList->count(); ++i) {
ExcerptItem* eii = (ExcerptItem*)excerptList->item(i);
if (eii->excerpt()->title() != eii->text()) {
// if except score not created yet, change the UI title
// if already created, change back(see createName) the excerpt title
if (!eii->excerpt()->partScore())
eii->setText(eii->excerpt()->title());
else
eii->excerpt()->setTitle(eii->text());
}
}
}
//---------------------------------------------------------
// newAllClicked
//---------------------------------------------------------
void ExcerptsDialog::newAllClicked()
{
QList<Excerpt*> excerpts = Excerpt::createAllExcerpt(score);
ExcerptItem* ei = 0;
for (Excerpt* e : excerpts) {
ei = new ExcerptItem(e);
excerptList->addItem(ei);
}
if (ei) {
excerptList->selectionModel()->clearSelection();
excerptList->setCurrentItem(ei, QItemSelectionModel::SelectCurrent);
}
}
//---------------------------------------------------------
// deleteClicked
//---------------------------------------------------------
void ExcerptsDialog::deleteClicked()
{
QListWidgetItem* cur = excerptList->currentItem();
if (cur == 0)
return;
delete cur;
}
//---------------------------------------------------------
// moveUpClicked
//---------------------------------------------------------
void ExcerptsDialog::moveUpClicked()
{
QListWidgetItem* cur = excerptList->currentItem();
if (cur == 0)
return;
int currentRow = excerptList->currentRow();
if (currentRow <= 0)
return;
QListWidgetItem* currentItem = excerptList->takeItem(currentRow);
excerptList->insertItem(currentRow - 1, currentItem);
excerptList->setCurrentRow(currentRow - 1);
}
//---------------------------------------------------------
// moveDownClicked
//---------------------------------------------------------
void ExcerptsDialog::moveDownClicked()
{
QListWidgetItem* cur = excerptList->currentItem();
if (cur == 0)
return;
int currentRow = excerptList->currentRow();
int nbRows = excerptList->count();
if (currentRow >= nbRows - 1)
return;
QListWidgetItem* currentItem = excerptList->takeItem(currentRow);
excerptList->insertItem(currentRow + 1, currentItem);
excerptList->setCurrentRow(currentRow + 1);
}
//---------------------------------------------------------
// addButtonClicked
// add instrument to excerpt
//---------------------------------------------------------
void ExcerptsDialog::addButtonClicked()
{
if (!excerptList->currentItem() || !partList->isEnabled())
return;
Excerpt* cur = ((ExcerptItem*)(excerptList->currentItem()))->excerpt();
for (QListWidgetItem* i : instrumentList->selectedItems()) {
InstrumentItem* item = static_cast<InstrumentItem*>(i);
const PartItem* it = item->partItem();
if (it == 0)
continue;
PartItem* pi = new PartItem(it->part(), 0);
pi->setText(0, pi->part()->name());
cur->parts().append(pi->part());
partList->addTopLevelItem(pi);
for (Staff* s : *pi->part()->staves()) {
StaffItem* sli = new StaffItem(pi);
sli->setStaff(s);
for (int j = 0; j < VOICES; j++)
sli->setCheckState(j + 1, Qt::Checked);
}
pi->setText(0, pi->part()->partName());
}
cur->setTracks(mapTracks());
partList->resizeColumnToContents(0);
}
//---------------------------------------------------------
// removeButtonClicked
// remove instrument from score
//---------------------------------------------------------
void ExcerptsDialog::removeButtonClicked()
{
QList<QTreeWidgetItem*> wi = partList->selectedItems();
if (wi.isEmpty())
return;
Excerpt* cur = ((ExcerptItem*)(excerptList->currentItem()))->excerpt();
QTreeWidgetItem* item = wi.first();
cur->parts().removeAt(partList->indexOfTopLevelItem(item));
delete item;
cur->setTracks(mapTracks());
partList->resizeColumnToContents(0);
}
//---------------------------------------------------------
// excerptChanged
//---------------------------------------------------------
void ExcerptsDialog::excerptChanged(QListWidgetItem* cur, QListWidgetItem*)
{
bool b = true;
if (cur) {
ExcerptItem* curItem = static_cast<ExcerptItem*>(cur);
Excerpt* e = curItem->excerpt();
title->setText(curItem->text());
b = e->partScore() == 0;
// set selection:
QList<Part*>& pl = e->parts();
QMultiMap<int, int> tracks = e->tracks();
partList->clear();
for (Part* p: pl) {
PartItem* pi = new PartItem(p, partList);
partList->addTopLevelItem(pi);
for (Staff* s : *p->staves()) {
StaffItem* sli = new StaffItem(pi);
sli->setStaff(s);
sli->setDisabled(!b);
}
pi->setText(0, p->partName());
partList->setItemExpanded(pi, false);
}
assignTracks(tracks);
}
else {
title->setText("");
partList->clear();
b = false;
}
instrumentList->setEnabled(b);
title->setEnabled(true);
addButton->setEnabled(b);
removeButton->setEnabled(b);
bool flag = excerptList->currentItem() != 0;
int n = excerptList->count();
int idx = excerptList->currentIndex().row();
moveUpButton->setEnabled(idx > 0);
moveDownButton->setEnabled(idx < (n-1));
deleteButton->setEnabled(flag);
}
//---------------------------------------------------------
// partDoubleClicked
//---------------------------------------------------------
void ExcerptsDialog::partDoubleClicked(QTreeWidgetItem* item, int)
{
if (!item->parent()) { // top level items are PartItem
PartItem* pi = (PartItem*)item;
QString s = pi->part()->partName();
title->setText(s);
titleChanged(s);
}
}
//---------------------------------------------------------
// partClicked
//---------------------------------------------------------
void ExcerptsDialog::partClicked(QTreeWidgetItem*, int)
{
QListWidgetItem* cur = excerptList->currentItem();
if (cur == 0)
return;
Excerpt* excerpt = static_cast<ExcerptItem*>(cur)->excerpt();
excerpt->setTracks(mapTracks());
}
//---------------------------------------------------------
// doubleClickedInstrument
//---------------------------------------------------------
void ExcerptsDialog::doubleClickedInstrument(QTreeWidgetItem*)
{
addButtonClicked();
}
//---------------------------------------------------------
// titleChanged
//---------------------------------------------------------
void ExcerptsDialog::titleChanged(const QString& s)
{
ExcerptItem* cur = static_cast<ExcerptItem*>(excerptList->currentItem());
if (cur == 0)
return;
cur->excerpt()->setTitle(s);
cur->setText(s);
}
//---------------------------------------------------------
// createName
//---------------------------------------------------------
QString ExcerptsDialog::createName(const QString& partName)
{
int count = excerptList->count();
QList<Excerpt*> excerpts;
for (int i = 0; i < count; ++i) {
Excerpt* ee = static_cast<ExcerptItem*>(excerptList->item(i))->excerpt();
excerpts.append(ee);
}
return Excerpt::createName(partName, excerpts);
}
//---------------------------------------------------------
// mapTracks
//---------------------------------------------------------
QMultiMap<int, int> ExcerptsDialog::mapTracks()
{
QMultiMap<int, int> tracks;
int track = 0;
for (QTreeWidgetItem* pwi = partList->itemAt(0,0); pwi; pwi = partList->itemBelow(pwi)) {
PartItem* pi = (PartItem*)pwi;
Part* p = pi->part();
for (int j = 0; j < pwi->childCount(); j++) {
for (int k = 0; k < VOICES; k++) {
if (pwi->child(j)->checkState(k+1) == Qt::Checked) {
int voiceOff = 0;
int srcTrack = p->startTrack() + j * VOICES + k;
for (int i = srcTrack & ~3; i < srcTrack; i++) {
QList<int> t = tracks.values(i);
for (int ti : t) {
if (ti >= (track & ~3))
voiceOff++;
}
}
tracks.insert(srcTrack, (track & ~3) + voiceOff);
}
track++;
}
}
}
return tracks;
}
//---------------------------------------------------------
// assignTracks
//---------------------------------------------------------
void ExcerptsDialog::assignTracks(QMultiMap<int, int> tracks)
{
int track = 0;
for (QTreeWidgetItem* pwi = partList->itemAt(0,0); pwi; pwi = partList->itemBelow(pwi)) {
for (int j = 0; j < pwi->childCount(); j++) {
for (int k = 0; k < VOICES; k++) {
int checkTrack = tracks.key(track, -1);
if (checkTrack != -1)
pwi->child(j)->setCheckState((checkTrack % VOICES) + 1, Qt::Checked);
else
pwi->child(j)->setCheckState((track % VOICES) + 1, Qt::Unchecked);
track++;
}
}
}
}
//---------------------------------------------------------
// isInPartList
//---------------------------------------------------------
ExcerptItem* ExcerptsDialog::isInPartsList(Excerpt* e)
{
int n = excerptList->count();
for (int i = 0; i < n; ++i) {
excerptList->setCurrentRow(i);
// ExcerptItem* cur = static_cast<ExcerptItem*>(excerptList->currentItem());
ExcerptItem* cur = static_cast<ExcerptItem*>(excerptList->item(i));
if (cur == 0)
continue;
// if (((ExcerptItem*)cur)->excerpt() == ExcerptItem(e).excerpt())
if (cur->excerpt() == e)
return cur;
}
return 0;
}
//---------------------------------------------------------
// createExcerpt
//---------------------------------------------------------
void ExcerptsDialog::createExcerptClicked(QListWidgetItem* cur)
{
Excerpt* e = static_cast<ExcerptItem*>(cur)->excerpt();
e->setTitle(title->text());
if (e->partScore())
return;
if (e->parts().isEmpty()) {
qDebug("no parts");
return;
}
Score* nscore = new Score(e->oscore());
e->setPartScore(nscore);
qDebug() << " + Add part : " << e->title();
score->undo(new AddExcerpt(e));
Excerpt::createExcerpt(e);
// a new excerpt is created in AddExcerpt, make sure the parts are filed
for (Excerpt* ee : e->oscore()->excerpts()) {
if (ee->partScore() == nscore && ee != e) {
ee->parts().clear();
ee->parts().append(e->parts());
}
}
partList->setEnabled(false);
title->setEnabled(false);
}
//---------------------------------------------------------
// accept
//---------------------------------------------------------
void ExcerptsDialog::accept()
{
score->startCmd();
// first pass : see if actual parts needs to be deleted
foreach (Excerpt* e, score->excerpts()) {
Score* partScore = e->partScore();
ExcerptItem* item = isInPartsList(e);
if (!isInPartsList(e) && partScore) // Delete it because not in the list anymore
score->deleteExcerpt(e);
else {
if (item->text() != e->title())
score->undo(new ChangeExcerptTitle(e, item->text()));
}
}
// Second pass : Create new parts
int n = excerptList->count();
for (int i = 0; i < n; ++i) {
excerptList->setCurrentRow(i);
QListWidgetItem* cur = excerptList->currentItem();
if (cur == 0)
continue;
createExcerptClicked(cur);
}
// Third pass : Remove empty parts.
int i = 0;
while (i < excerptList->count()) {
// This new part is empty, so we don't create an excerpt but remove it from the list.
// Necessary to order the parts later on.
excerptList->setCurrentRow(i);
QListWidgetItem* cur = excerptList->currentItem();
Excerpt* e = static_cast<ExcerptItem*>(cur)->excerpt();
if (e->parts().isEmpty() && !e->partScore()) {
qDebug() << " - Deleting empty parts : " << cur->text();
delete cur;
}
else
i++;
}
// Update the score parts order following excerpList widget
// The reference is the excerpt list. So we iterate following it and swap parts in the score accordingly
for (int j = 0; j < excerptList->count(); ++j) {
excerptList->setCurrentRow(j);
QListWidgetItem* cur = excerptList->currentItem();
if (cur == 0)
continue;
int position = 0; // Actual order position in score
bool found = false;
// Looks for the excerpt and its position.
foreach(Excerpt* e, score->excerpts()) {
if (((ExcerptItem*)cur)->excerpt() == ExcerptItem(e).excerpt()) {
found = true;
break;
}
position++;
}
if ((found) && (position != j))
score->undo(new SwapExcerpt(score, j, position));
}
score->endCmd();
QDialog::accept();
}
}