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

1090 lines
43 KiB
C++

//=============================================================================
// MuseScore
// Linux Music Score Editor
//
// Copyright (C) 2002-2009 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 "config.h"
#include "icons.h"
#include "instrwidget.h"
#include "libmscore/clef.h"
#include "libmscore/instrtemplate.h"
#include "libmscore/line.h"
#include "libmscore/measure.h"
#include "libmscore/part.h"
#include "libmscore/score.h"
#include "libmscore/segment.h"
#include "libmscore/staff.h"
#include "libmscore/stafftype.h"
#include "libmscore/style.h"
#include "libmscore/system.h"
#include "libmscore/stringdata.h"
#include "libmscore/undo.h"
#include "libmscore/keysig.h"
namespace Ms {
int StaffListItem::customStandardIdx;
int StaffListItem::customPercussionIdx;
int StaffListItem::customTablatureIdx;
//---------------------------------------------------------
// StaffListItem
//---------------------------------------------------------
StaffListItem::StaffListItem(PartListItem* li)
: QTreeWidgetItem(li, STAFF_LIST_ITEM)
{
setPartIdx(0);
setLinked(false);
setClefType(ClefTypeList(ClefType::G));
initStaffTypeCombo();
}
StaffListItem::StaffListItem()
: QTreeWidgetItem(STAFF_LIST_ITEM)
{
setPartIdx(0);
setLinked(false);
setClefType(ClefTypeList(ClefType::G));
}
//---------------------------------------------------------
// initStaffTypeCombo
//---------------------------------------------------------
void StaffListItem::initStaffTypeCombo(bool forceRecreate)
{
if (_staffTypeCombo && !forceRecreate) // do not init more than once
return;
// NOTE: DO NOT DELETE the old _staffTypeCombo if already created:
// a bug in Qt looses track of (and presumably deletes) the combo set into the item
// if the item is repositioned in the tree; in this case, the pointer to the combo
// is no longer valid and cannot be used to delete it
// Call initStaffTypeCombo(true) ONLY if the item has been repositioned
// or a memory leak may result
bool canUseTabs = false; // assume only normal staves are applicable
int numFrettedStrings = 0;
bool canUsePerc = false;
PartListItem* part = static_cast<PartListItem*>(QTreeWidgetItem::parent());
// PartListItem has different members filled out if used in New Score Wizard
// or in Instruments Wizard
if (part) {
const StringData* stringData = part->it ? &(part->it->stringData) :
( (part->part && part->part->instrument()) ? part->part->instrument()->stringData() : 0);
canUseTabs = stringData && stringData->frettedStrings() > 0;
if (canUseTabs)
numFrettedStrings = stringData->frettedStrings();
canUsePerc = part->it ? part->it->useDrumset :
( (part->part && part->part->instrument()) ? part->part->instrument()->useDrumset() : false);
}
_staffTypeCombo = new QComboBox();
_staffTypeCombo->setAutoFillBackground(true);
int idx = 0;
for (const StaffType& st : StaffType::presets()) {
if ( (st.group() == StaffGroup::STANDARD && (!canUsePerc)) // percussion excludes standard
|| (st.group() == StaffGroup::PERCUSSION && canUsePerc)
|| (st.group() == StaffGroup::TAB && canUseTabs && st.lines() <= numFrettedStrings)) {
_staffTypeCombo->addItem(st.name(), idx);
}
++idx;
}
customStandardIdx = _staffTypeCombo->count();
_staffTypeCombo->addItem(tr("Custom Standard"), CUSTOM_STAFF_TYPE_IDX);
customPercussionIdx = _staffTypeCombo->count();
_staffTypeCombo->addItem(tr("Custom Percussion"), CUSTOM_STAFF_TYPE_IDX);
customTablatureIdx = _staffTypeCombo->count();
_staffTypeCombo->addItem(tr("Custom Tablature"), CUSTOM_STAFF_TYPE_IDX);
treeWidget()->setItemWidget(this, 4, _staffTypeCombo);
connect(_staffTypeCombo, SIGNAL(currentIndexChanged(int)), SLOT(staffTypeChanged(int)) );
}
//---------------------------------------------------------
// setPartIdx
//---------------------------------------------------------
void StaffListItem::setPartIdx(int val)
{
_partIdx = val;
setText(0, InstrumentsWidget::tr("Staff %1").arg(_partIdx + 1));
}
//---------------------------------------------------------
// setClefType
//---------------------------------------------------------
void StaffListItem::setClefType(const ClefTypeList& val)
{
_clefType = val;
setText(2, qApp->translate("clefTable", ClefInfo::name(_clefType._transposingClef)));
}
//---------------------------------------------------------
// setLinked
//---------------------------------------------------------
void StaffListItem::setLinked(bool val)
{
_linked = val;
setIcon(3, _linked ? *icons[int(Icons::checkmark_ICON)] : QIcon() );
}
//---------------------------------------------------------
// setStaffType
//---------------------------------------------------------
void StaffListItem::setStaffType(const StaffType* st)
{
if (!st) // if no staff type given, dault to stadard
_staffTypeCombo->setCurrentIndex(0); // staff type (at combo box index 0)
else {
// if staff type given, look into combo box item data for a preset equal to staff type
for (int i = 0; i < _staffTypeCombo->count(); ++i) {
const StaffType* _st = StaffType::preset(StaffTypes(_staffTypeCombo->itemData(i).toInt()));
if (*_st == *st) {
_staffTypeCombo->setCurrentIndex(i);
return;
}
}
// try harder
for (int i = 0; i < _staffTypeCombo->count(); ++i) {
const StaffType* _st = StaffType::preset(StaffTypes(_staffTypeCombo->itemData(i).toInt()));
if (_st->isSameStructure(*st)) {
_staffTypeCombo->setCurrentIndex(i);
return;
}
}
int idx = 0;
switch (st->group()) {
case StaffGroup::STANDARD:
idx = customStandardIdx;
break;
case StaffGroup::PERCUSSION:
idx = customPercussionIdx;
break;
case StaffGroup::TAB:
idx = customTablatureIdx;
break;
}
_staffTypeCombo->setCurrentIndex(idx);
}
}
//---------------------------------------------------------
// setStaffType
//---------------------------------------------------------
void StaffListItem::setStaffType(int idx)
{
int i = _staffTypeCombo->findData(idx);
if (i != -1)
_staffTypeCombo->setCurrentIndex(i);
}
//---------------------------------------------------------
// staffType
//---------------------------------------------------------
const StaffType* StaffListItem::staffType() const
{
int typeIdx = staffTypeIdx();
Q_ASSERT(typeIdx != CUSTOM_STAFF_TYPE_IDX);
if (typeIdx == CUSTOM_STAFF_TYPE_IDX)
typeIdx = 0;
return StaffType::preset(StaffTypes(typeIdx));
}
//---------------------------------------------------------
// staffTypeIdx
//---------------------------------------------------------
int StaffListItem::staffTypeIdx(int idx) const
{
return _staffTypeCombo->itemData(idx).toInt();
}
//---------------------------------------------------------
// staffTypeIdx
//---------------------------------------------------------
int StaffListItem::staffTypeIdx() const
{
return staffTypeIdx(_staffTypeCombo->currentIndex());
}
//---------------------------------------------------------
// staffTypeChanged
//---------------------------------------------------------
void StaffListItem::staffTypeChanged(int idx)
{
// check current clef matches new staff type
const int typeIdx = staffTypeIdx(idx);
if (typeIdx == CUSTOM_STAFF_TYPE_IDX) // consider it not changed
return;
const StaffType* stfType = StaffType::preset(StaffTypes(typeIdx));
PartListItem* pli = static_cast<PartListItem*>(QTreeWidgetItem::parent());
pli->updateClefs();
if (_staff && _staff->staffType(Fraction(0,1))->name() != stfType->name()) {
if (_op != ListItemOp::I_DELETE && _op != ListItemOp::ADD)
_op = ListItemOp::UPDATE;
}
}
//---------------------------------------------------------
// setVisible
//---------------------------------------------------------
void PartListItem::setVisible(bool val)
{
setCheckState(1, val ? Qt::Checked : Qt::Unchecked);
}
//---------------------------------------------------------
// visible
//---------------------------------------------------------
bool PartListItem::visible() const
{
return checkState(1) == Qt::Checked;
}
//---------------------------------------------------------
// updateClefs
//---------------------------------------------------------
void PartListItem::updateClefs()
{
for (int i = 0; i < childCount(); ++i) {
StaffListItem* sli = static_cast<StaffListItem*>(child(i));
const StaffType* stfType = StaffType::preset(StaffTypes(sli->staffTypeIdx()));
ClefTypeList clefType;
switch (stfType->group()) {
case StaffGroup::STANDARD:
clefType = sli->defaultClefType();
break;
case StaffGroup::TAB:
clefType = ClefTypeList(ClefType::TAB);
break;
case StaffGroup::PERCUSSION:
clefType = ClefTypeList(ClefType::PERC);
break;
}
sli->setClefType(clefType);
}
}
//---------------------------------------------------------
// PartListItem
//---------------------------------------------------------
PartListItem::PartListItem(Part* p, QTreeWidget* lv)
: QTreeWidgetItem(lv, PART_LIST_ITEM)
{
part = p;
it = 0;
op = ListItemOp::KEEP;
setText(0, p->partName());
setFlags(flags() | Qt::ItemIsUserCheckable);
}
PartListItem::PartListItem(const InstrumentTemplate* i, QTreeWidget* lv)
: QTreeWidgetItem(lv, PART_LIST_ITEM)
{
part = 0;
it = i;
op = ListItemOp::ADD;
setText(0, it->trackName);
}
//---------------------------------------------------------
// InstrumentTemplateListItem
//---------------------------------------------------------
InstrumentTemplateListItem::InstrumentTemplateListItem(QString group, QTreeWidget* parent)
: QTreeWidgetItem(parent) {
_instrumentTemplate = 0;
_group = group;
setText(0, group);
}
InstrumentTemplateListItem::InstrumentTemplateListItem(InstrumentTemplate* i, InstrumentTemplateListItem* item)
: QTreeWidgetItem(item) {
_instrumentTemplate = i;
setText(0, i->trackName);
}
InstrumentTemplateListItem::InstrumentTemplateListItem(InstrumentTemplate* i, QTreeWidget* parent)
: QTreeWidgetItem(parent) {
_instrumentTemplate = i;
setText(0, i->trackName);
}
//---------------------------------------------------------
// text
//---------------------------------------------------------
QString InstrumentTemplateListItem::text(int col) const
{
switch (col) {
case 0:
return _instrumentTemplate ?
_instrumentTemplate->trackName : _group;
default:
return QString("");
}
}
//---------------------------------------------------------
// InstrumentsWidget
//---------------------------------------------------------
InstrumentsWidget::InstrumentsWidget(QWidget* parent)
: QWidget(parent)
{
setupUi(this);
splitter->setStretchFactor(0, 10);
splitter->setStretchFactor(1, 0);
splitter->setStretchFactor(2, 15);
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
instrumentList->setSelectionMode(QAbstractItemView::ExtendedSelection);
partiturList->setSelectionMode(QAbstractItemView::SingleSelection);
QStringList header = (QStringList() << tr("Staves") << tr("Visible") << tr("Clef") << tr("Linked") << tr("Staff type"));
partiturList->setHeaderLabels(header);
partiturList->resizeColumnToContents(1); // shrink "visible "and "linked" columns as much as possible
partiturList->resizeColumnToContents(3);
buildTemplateList();
addButton->setEnabled(false);
removeButton->setEnabled(false);
upButton->setEnabled(false);
downButton->setEnabled(false);
addStaffButton->setEnabled(false);
addLinkedStaffButton->setEnabled(false);
upButton->setIcon(*icons[int(Icons::arrowUp_ICON)]);
downButton->setIcon(*icons[int(Icons::arrowDown_ICON)]);
instrumentSearch->setFilterableView(instrumentList);
}
//---------------------------------------------------------
// populateGenreCombo
//---------------------------------------------------------
void populateGenreCombo(QComboBox* combo)
{
combo->blockSignals(true);
combo->clear();
combo->addItem(qApp->translate("InstrumentsDialog", "All instruments"), "all");
int i = 1;
int defaultIndex = 0;
for (InstrumentGenre *ig : instrumentGenres) {
combo->addItem(ig->name, ig->id);
if (ig->id == "common")
defaultIndex = i;
++i;
}
combo->blockSignals(false);
combo->setCurrentIndex(defaultIndex);
}
//---------------------------------------------------------
// populateInstrumentList
//---------------------------------------------------------
void populateInstrumentList(QTreeWidget* instrumentList)
{
instrumentList->clear();
// TODO: memory leak?
for (InstrumentGroup* g : instrumentGroups) {
InstrumentTemplateListItem* group = new InstrumentTemplateListItem(g->name, instrumentList);
// provide feedback to blind users that they have selected a group rather than an instrument
group->setData(0, Qt::AccessibleTextRole, QVariant(QObject::tr("%1 category").arg(g->name))); // spoken by screen readers
group->setFlags(Qt::ItemIsEnabled);
for (InstrumentTemplate* t : g->instrumentTemplates) {
InstrumentTemplateListItem* instrument = new InstrumentTemplateListItem(t, group);
instrument->setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren));
}
}
}
//---------------------------------------------------------
// buildTemplateList
//---------------------------------------------------------
void InstrumentsWidget::buildTemplateList()
{
// clear search if instrument list is updated
instrumentSearch->clear();
populateInstrumentList(instrumentList);
populateGenreCombo(instrumentGenreFilter);
}
//---------------------------------------------------------
// genPartList
//---------------------------------------------------------
void InstrumentsWidget::genPartList(Score* cs)
{
partiturList->clear();
for (Part* p : cs->parts()) {
PartListItem* pli = new PartListItem(p, partiturList);
pli->setVisible(p->show());
for (Staff* s : *p->staves()) {
StaffListItem* sli = new StaffListItem(pli);
sli->setStaff(s);
sli->setClefType(s->clefType(Fraction(0,1)));
sli->setDefaultClefType(s->defaultClefType());
sli->setPartIdx(s->rstaff());
const LinkedElements* ls = s->links();
bool bLinked = false;
if (ls && !ls->empty()) {
for (auto le : *ls) {
Staff* ps = toStaff(le);
if (ps != s && ps->score() == s->score()) {
bLinked = true;
break;
}
}
}
sli->setLinked(bLinked);
sli->setStaffType(s->staffType(Fraction(0,1))); // TODO
}
pli->updateClefs();
partiturList->setItemExpanded(pli, true);
}
partiturList->resizeColumnToContents(2); // adjust width of "Clef " and "Staff type" columns
partiturList->resizeColumnToContents(4);
}
//---------------------------------------------------------
// on_instrumentList_itemSelectionChanged
//---------------------------------------------------------
void InstrumentsWidget::on_instrumentList_itemSelectionChanged()
{
QList<QTreeWidgetItem*> wi = instrumentList->selectedItems();
bool flag = !wi.isEmpty();
addButton->setEnabled(flag);
}
//---------------------------------------------------------
// on_partiturList_itemSelectionChanged
//---------------------------------------------------------
void InstrumentsWidget::on_partiturList_itemSelectionChanged()
{
QList<QTreeWidgetItem*> wi = partiturList->selectedItems();
if (wi.isEmpty()) {
removeButton->setEnabled(false);
upButton->setEnabled(false);
downButton->setEnabled(false);
addLinkedStaffButton->setEnabled(false);
addStaffButton->setEnabled(false);
return;
}
QTreeWidgetItem* item = wi.front();
Q_ASSERT(partiturList->currentItem() == item);
bool flag = item != nullptr;
int count = 0; // item can be hidden
QTreeWidgetItem* it = 0;
QList<QTreeWidgetItem*> witems;
if(item->type() == PART_LIST_ITEM) {
for (int idx = 0; (it = partiturList->topLevelItem(idx)); ++idx) {
if (!it->isHidden()) {
count++;
witems.append(it);
}
}
}
else {
for (int idx = 0; (it = item->parent()->child(idx)); ++idx) {
if (!it->isHidden()){
count++;
witems.append(it);
}
}
}
bool onlyOne = (count == 1);
bool first = (witems.first() == item);
bool last = (witems.last() == item);
removeButton->setEnabled(flag && !onlyOne);
upButton->setEnabled(flag && !onlyOne && !first);
downButton->setEnabled(flag && !onlyOne && !last);
addLinkedStaffButton->setEnabled(item && item->type() == STAFF_LIST_ITEM);
addStaffButton->setEnabled(item && item->type() == STAFF_LIST_ITEM);
}
//---------------------------------------------------------
// on_instrumentList
//---------------------------------------------------------
void InstrumentsWidget::on_instrumentList_itemActivated(QTreeWidgetItem* item, int)
{
if (item->flags() & Qt::ItemIsSelectable)
on_addButton_clicked();
}
//---------------------------------------------------------
// on_addButton_clicked
// add instrument to partitur
//---------------------------------------------------------
void InstrumentsWidget::on_addButton_clicked()
{
for (QTreeWidgetItem* i : instrumentList->selectedItems()) {
InstrumentTemplateListItem* item = static_cast<InstrumentTemplateListItem*>(i);
const InstrumentTemplate* it = item->instrumentTemplate();
if (it == 0)
continue;
PartListItem* pli = new PartListItem(it, partiturList);
pli->setFirstColumnSpanned(true);
pli->op = ListItemOp::ADD;
int n = it->nstaves();
for (int i1 = 0; i1 < n; ++i1) {
StaffListItem* sli = new StaffListItem(pli);
sli->setOp(ListItemOp::ADD);
sli->setStaff(0);
sli->setPartIdx(i1);
sli->setDefaultClefType(it->clefType(i1));
sli->setStaffType(it->staffTypePreset);
}
pli->updateClefs();
partiturList->setItemExpanded(pli, true);
partiturList->clearSelection(); // should not be necessary
partiturList->setCurrentItem(pli);
}
emit completeChanged(true);
}
//---------------------------------------------------------
// on_removeButton_clicked
// remove instrument from partitur
//---------------------------------------------------------
void InstrumentsWidget::on_removeButton_clicked()
{
QList<QTreeWidgetItem*> wi = partiturList->selectedItems();
if (wi.isEmpty()) {
Q_ASSERT(false); // shouldn't get here (remove button disabled when no items selected)
removeButton->setEnabled(false); // nevertheless, handle gracefully in release builds
return;
}
QTreeWidgetItem* item = wi.front();
QTreeWidgetItem* parent = item->parent();
if (parent) {
if (parent->childCount() == 1) {
Q_ASSERT(false); // shouldn't get here (remove button disabled when one item left)
removeButton->setEnabled(false); // nevertheless, handle gracefully in release builds
return;
}
if (((StaffListItem*)item)->op() == ListItemOp::ADD) {
if (parent->childCount() == 1) {
partiturList->takeTopLevelItem(partiturList->indexOfTopLevelItem(parent));
delete parent;
}
else {
parent->takeChild(parent->indexOfChild(item));
delete item;
}
}
else {
((StaffListItem*)item)->setOp(ListItemOp::I_DELETE);
item->setHidden(true);
// check if a staff is linked to this staff
int idx = parent->indexOfChild(item);
StaffListItem* sli = static_cast<StaffListItem*>(parent->child(idx+1));
if (sli) {
StaffListItem* sli2 = static_cast<StaffListItem*>(parent->child(idx+2));
if (sli->linked() && !(sli2 && sli2->linked())) {
sli->setLinked(false);
partiturList->update();
}
}
}
static_cast<PartListItem*>(parent)->updateClefs();
partiturList->setCurrentItem(parent);
}
else {
if (partiturList->topLevelItemCount() == 1) {
Q_ASSERT(false); // shouldn't get here as (remove button disabled when one item left)
removeButton->setEnabled(false); // nevertheless, handle gracefully in release builds
emit completeChanged(false);
return;
}
int idx = partiturList->indexOfTopLevelItem(item);
if (((PartListItem*)item)->op == ListItemOp::ADD) {
partiturList->blockSignals(true);
delete item; // fires selectionChanged too early (item is still in view)
partiturList->blockSignals(false);
emit on_partiturList_itemSelectionChanged(); // fire manually (item gone by now)
}
else {
((PartListItem*)item)->op = ListItemOp::I_DELETE;
item->setHidden(true);
}
// select an item, do not consider hidden ones
int plusIdx = 0;
QTreeWidgetItem* nextParent = partiturList->topLevelItem(idx + plusIdx);
while (nextParent && nextParent->isHidden()) {
plusIdx++;
nextParent = partiturList->topLevelItem(idx + plusIdx);
}
if(!nextParent) { // couldn't find one after, check before
plusIdx = 1;
nextParent = partiturList->topLevelItem(idx - plusIdx);
while (nextParent && nextParent->isHidden()) {
plusIdx++;
nextParent = partiturList->topLevelItem(idx - plusIdx);
}
}
partiturList->setCurrentItem(nextParent);
}
}
//---------------------------------------------------------
// on_upButton_clicked
// move instrument up in partitur
//---------------------------------------------------------
void InstrumentsWidget::on_upButton_clicked()
{
QList<QTreeWidgetItem*> wi = partiturList->selectedItems();
if (wi.isEmpty())
return;
QTreeWidgetItem* item = wi.front();
if (item->type() == PART_LIST_ITEM) {
bool isExpanded = partiturList->isItemExpanded(item);
int idx = partiturList->indexOfTopLevelItem(item);
// if part item not first, move one slot up
if (idx) {
partiturList->selectionModel()->clear();
QTreeWidgetItem* item1 = partiturList->takeTopLevelItem(idx);
// Qt looses the QComboBox set into StaffListItem's when they are re-inserted into the tree:
// get the currently selected staff type of each combo and re-insert
int numOfStaffListItems = item1->childCount();
#if (!defined (_MSCVER) && !defined (_MSC_VER))
int staffIdx[numOfStaffListItems];
#else
// MSVC does not support VLA. Replace with std::vector. If profiling determines that the
// heap allocation is slow, an optimization might be used.
std::vector<int> staffIdx(numOfStaffListItems);
#endif
for (int itemIdx=0; itemIdx < numOfStaffListItems; ++itemIdx)
staffIdx[itemIdx] = (static_cast<StaffListItem*>(item1->child(itemIdx)))->staffTypeIdx();
// do not consider hidden ones
int minusIdx = 1;
QTreeWidgetItem* prevParent = partiturList->topLevelItem(idx - minusIdx);
while (prevParent && prevParent->isHidden()) {
minusIdx++;
prevParent = partiturList->topLevelItem(idx - minusIdx);
}
partiturList->insertTopLevelItem(idx - minusIdx, item1);
// after-re-insertion, recreate each combo and set its index
for (int itemIdx=0; itemIdx < numOfStaffListItems; ++itemIdx) {
StaffListItem* staffItem = static_cast<StaffListItem*>(item1->child(itemIdx));
staffItem->initStaffTypeCombo(true);
staffItem->setStaffType(staffIdx[itemIdx]);
}
partiturList->setItemExpanded(item1, isExpanded);
partiturList->setCurrentItem(item1);
}
}
else {
QTreeWidgetItem* parent = item->parent();
int idx = parent->indexOfChild(item);
// if staff item not first of its part, move one slot up
if (idx) {
partiturList->selectionModel()->clear();
StaffListItem* item1 = static_cast<StaffListItem*>(parent->takeChild(idx));
// Qt looses the QComboBox set into StaffListItem when it is re-inserted into the tree:
// get currently selected staff type and re-insert
int staffTypeIdx = item1->staffTypeIdx();
parent->insertChild(idx - 1, item1);
// after item has been inserted into the tree, create a new QComboBox and set its index
item1->initStaffTypeCombo(true);
item1->setStaffType(staffTypeIdx);
partiturList->setCurrentItem(item1);
}
else {
// if staff item first of its part...
int parentIdx = partiturList->indexOfTopLevelItem(parent);
// ...and there is a previous part, move staff item to previous part
if (parentIdx) {
partiturList->selectionModel()->clear();
QTreeWidgetItem* prevParent = partiturList->topLevelItem(parentIdx - 1);
StaffListItem* sli = static_cast<StaffListItem*>(parent->takeChild(idx));
int staffTypeIdx = sli->staffTypeIdx();
prevParent->addChild(sli);
sli->initStaffTypeCombo(true);
sli->setStaffType(staffTypeIdx);
partiturList->setCurrentItem(sli);
// PartListItem* pli = static_cast<PartListItem*>(prevParent);
// int idx = pli->part->nstaves();
//?? cs->undo(new MoveStaff(sli->staff(), pli->part, idx));
//
// TODO : if staff was linked to a staff of the old parent part, unlink it!
//
}
}
}
}
//---------------------------------------------------------
// on_downButton_clicked
// move instrument down in partitur
//---------------------------------------------------------
void InstrumentsWidget::on_downButton_clicked()
{
QList<QTreeWidgetItem*> wi = partiturList->selectedItems();
if (wi.isEmpty())
return;
QTreeWidgetItem* item = wi.front();
if (item->type() == PART_LIST_ITEM) {
bool isExpanded = partiturList->isItemExpanded(item);
int idx = partiturList->indexOfTopLevelItem(item);
int n = partiturList->topLevelItemCount();
// if part not last, move one slot down
if (idx < (n-1)) {
partiturList->selectionModel()->clear();
QTreeWidgetItem* item1 = partiturList->takeTopLevelItem(idx);
// Qt looses the QComboBox set into StaffListItem's when they are re-inserted into the tree:
// get the currently selected staff type of each combo and re-insert
int numOfStaffListItems = item1->childCount();
#if (!defined (_MSCVER) && !defined (_MSC_VER))
int staffIdx[numOfStaffListItems];
#else
// MSVC does not support VLA. Replace with std::vector. If profiling determines that the
// heap allocation is slow, an optimization might be used.
std::vector<int> staffIdx(numOfStaffListItems);
#endif
int itemIdx;
for (itemIdx=0; itemIdx < numOfStaffListItems; ++itemIdx)
staffIdx[itemIdx] = (static_cast<StaffListItem*>(item1->child(itemIdx)))->staffTypeIdx();
// do not consider hidden ones
int plusIdx = 1;
QTreeWidgetItem* nextParent = partiturList->topLevelItem(idx + plusIdx);
while (nextParent && nextParent->isHidden()) {
plusIdx++;
nextParent = partiturList->topLevelItem(idx + plusIdx);
}
partiturList->insertTopLevelItem(idx + plusIdx, item1);
// after-re-insertion, recreate each combo and set its index
for (itemIdx=0; itemIdx < numOfStaffListItems; ++itemIdx) {
StaffListItem* staffItem = static_cast<StaffListItem*>(item1->child(itemIdx));
staffItem->initStaffTypeCombo(true);
staffItem->setStaffType(staffIdx[itemIdx]);
}
partiturList->setItemExpanded(item1, isExpanded);
partiturList->setCurrentItem(item1);
}
}
else {
QTreeWidgetItem* parent = item->parent();
int idx = parent->indexOfChild(item);
int n = parent->childCount();
// if staff item is not last of its part, move one slot down in part
if (idx < (n-1)) {
partiturList->selectionModel()->clear();
StaffListItem* item1 = static_cast<StaffListItem*>(parent->takeChild(idx));
// Qt looses the QComboBox set into StaffListItem when it is re-inserted into the tree:
// get currently selected staff type and re-insert
int staffTypeIdx = item1->staffTypeIdx();
parent->insertChild(idx+1, item1);
// after item has been inserted into the tree, create a new QComboBox and set its index
item1->initStaffTypeCombo(true);
item1->setStaffType(staffTypeIdx);
partiturList->setCurrentItem(item1);
}
else {
// if staff item is last of its part...
int parentIdx = partiturList->indexOfTopLevelItem(parent);
int n1 = partiturList->topLevelItemCount();
//..and there is a next part, move to next part
if (parentIdx < (n1-1)) {
partiturList->selectionModel()->clear();
StaffListItem* sli = static_cast<StaffListItem*>(parent->takeChild(idx));
QTreeWidgetItem* nextParent = partiturList->topLevelItem(parentIdx - 1);
int staffTypeIdx = sli->staffTypeIdx();
nextParent->addChild(sli);
sli->initStaffTypeCombo(true);
sli->setStaffType(staffTypeIdx);
partiturList->setCurrentItem(sli);
// PartListItem* pli = static_cast<PartListItem*>(nextParent);
// cs->undo(new MoveStaff(sli->staff(), pli->part, 0));
//
// TODO : if staff was linked to a staff of the old parent part, unlink it!
//
}
}
}
}
//---------------------------------------------------------
// on_addStaffButton_clicked
//---------------------------------------------------------
StaffListItem* InstrumentsWidget::on_addStaffButton_clicked()
{
QList<QTreeWidgetItem*> wi = partiturList->selectedItems();
if (wi.isEmpty())
return 0;
QTreeWidgetItem* item = wi.front();
if (item->type() != STAFF_LIST_ITEM)
return 0;
StaffListItem* sli = static_cast<StaffListItem*>(item);
// Staff* staff = sli->staff();
PartListItem* pli = static_cast<PartListItem*>(sli->QTreeWidgetItem::parent());
StaffListItem* nsli = new StaffListItem();
// nsli->setStaff(staff);
nsli->setStaff(0);
// if (staff)
nsli->setOp(ListItemOp::ADD);
int ridx = pli->indexOfChild(sli) + 1;
pli->insertChild(ridx, nsli);
nsli->initStaffTypeCombo(); // StaffListItem needs to be inserted in the tree hierarchy
nsli->setStaffType(sli->staffType()); // before a widget can be set into it
ClefTypeList clefType;
if (pli->it)
clefType = pli->it->clefType(ridx);
else
clefType = pli->part->instrument()->clefType(ridx);
nsli->setDefaultClefType(clefType);
pli->updateClefs();
partiturList->clearSelection(); // should not be necessary
partiturList->setCurrentItem(nsli);
pli->updateClefs();
return nsli;
}
//---------------------------------------------------------
// on_addLinkedStaffButton_clicked
//---------------------------------------------------------
void InstrumentsWidget::on_addLinkedStaffButton_clicked()
{
StaffListItem* nsli = on_addStaffButton_clicked();
if (nsli)
nsli->setLinked(true);
}
//---------------------------------------------------------
// on_instrumentSearch_textChanged
//---------------------------------------------------------
void InstrumentsWidget::on_instrumentSearch_textChanged(const QString&)
{
// searching is done in Ms::SearchBox so here we just reset the
// genre dropdown to ensure that the search includes all genres
const int idxAllGenres = 0;
if (instrumentGenreFilter->currentIndex() != idxAllGenres) {
instrumentGenreFilter->blockSignals(true);
instrumentGenreFilter->setCurrentIndex(idxAllGenres);
instrumentGenreFilter->blockSignals(false);
}
}
//---------------------------------------------------------
// on_instrumentGenreFilter_currentTextChanged
//---------------------------------------------------------
void InstrumentsWidget::on_instrumentGenreFilter_currentIndexChanged(int index)
{
QSettings settings;
settings.beginGroup("selectInstrument"); // hard coded, since this is also used in selinstrument
settings.setValue("selectedGenre", instrumentGenreFilter->currentText());
settings.endGroup();
QString id = instrumentGenreFilter->itemData(index).toString();
// Redisplay tree, only showing items from the selected genre
filterInstrumentsByGenre(instrumentList, id);
}
//---------------------------------------------------------
// filterInstrumentsByGenre
//---------------------------------------------------------
void InstrumentsWidget::filterInstrumentsByGenre(QTreeWidget *instrList, QString genre)
{
QTreeWidgetItemIterator iList(instrList);
while (*iList) {
(*iList)->setHidden(true);
InstrumentTemplateListItem* itli = static_cast<InstrumentTemplateListItem*>(*iList);
InstrumentTemplate *it=itli->instrumentTemplate();
if(it) {
if (genre == "all" || it->genreMember(genre)) {
(*iList)->setHidden(false);
QTreeWidgetItem *iParent = (*iList)->parent();
while(iParent) {
if(!iParent->isHidden())
break;
iParent->setHidden(false);
iParent = iParent->parent();
}
}
}
++iList;
}
}
//---------------------------------------------------------
// createInstruments
//---------------------------------------------------------
void InstrumentsWidget::createInstruments(Score* cs)
{
//
// process modified partitur list
//
QTreeWidget* pl = partiturList;
Part* part = 0;
int staffIdx = 0;
QTreeWidgetItem* item = 0;
for (int idx = 0; (item = pl->topLevelItem(idx)); ++idx) {
PartListItem* pli = (PartListItem*)item;
if (pli->op != ListItemOp::ADD) {
qDebug("bad op");
continue;
}
const InstrumentTemplate* t = ((PartListItem*)item)->it;
part = new Part(cs);
part->initFromInstrTemplate(t);
pli->part = part;
QTreeWidgetItem* ci = 0;
int rstaff = 0;
for (int cidx = 0; (ci = pli->child(cidx)); ++cidx) {
if (ci->isHidden())
continue;
StaffListItem* sli = (StaffListItem*)ci;
Staff* staff = new Staff(cs);
staff->setPart(part);
sli->setStaff(staff);
++rstaff;
staff->init(t, sli->staffType(), cidx);
staff->setDefaultClefType(sli->defaultClefType());
if (sli->linked() && !part->staves()->isEmpty()) {
Staff* linkedStaff = part->staves()->back();
staff->linkTo(linkedStaff);
}
part->staves()->push_back(staff);
cs->staves().insert(staffIdx + rstaff, staff);
}
// if a staff was removed from instrument:
if (part->staff(0)->barLineSpan() > rstaff) {
//TODO part->staff(0)->setBarLineSpan(rstaff);
part->staff(0)->setBracketType(0, BracketType::NO_BRACKET);
}
// insert part
cs->insertPart(part, staffIdx);
int sidx = cs->staffIdx(part);
int eidx = sidx + part->nstaves();
for (Measure* m = cs->firstMeasure(); m; m = m->nextMeasure())
m->cmdAddStaves(sidx, eidx, true);
staffIdx += rstaff;
}
#if 0 // TODO
//
// check for bar lines
//
for (int staffIdx = 0; staffIdx < cs->nstaves();) {
Staff* staff = cs->staff(staffIdx);
int barLineSpan = staff->barLineSpan();
if (barLineSpan == 0)
staff->setBarLineSpan(1);
int nstaffIdx = staffIdx + barLineSpan;
for (int idx = staffIdx+1; idx < nstaffIdx; ++idx) {
Staff* tStaff = cs->staff(idx);
if (tStaff)
tStaff->setBarLineSpan(0);
}
staffIdx = nstaffIdx;
}
#endif
cs->setLayoutAll();
}
//---------------------------------------------------------
// init
//---------------------------------------------------------
void InstrumentsWidget::init()
{
partiturList->clear();
instrumentList->clearSelection();
addButton->setEnabled(false);
removeButton->setEnabled(false);
upButton->setEnabled(false);
downButton->setEnabled(false);
addLinkedStaffButton->setEnabled(false);
addStaffButton->setEnabled(false);
// get last saved, user-selected instrument genre and set filter to it
QSettings settings;
settings.beginGroup("selectInstrument");
if (!settings.value("selectedGenre").isNull()){
QString selectedGenre = settings.value("selectedGenre").value<QString>();
instrumentGenreFilter->setCurrentText(selectedGenre);
}
settings.endGroup();
emit completeChanged(false);
}
//---------------------------------------------------------
// getPartiturList
//---------------------------------------------------------
QTreeWidget* InstrumentsWidget::getPartiturList()
{
return partiturList;
}
}