code: //=============================================================================
  //  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);
        }
  PartListItem::PartListItem(const InstrumentTemplate* i, QTreeWidget* lv, QTreeWidgetItem* prv)
     : QTreeWidgetItem(lv, prv, 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);
        }
  
  //---------------------------------------------------------
  //   updatePartIdx
  //---------------------------------------------------------
  
  void InstrumentsWidget::updatePartIdx()
        {
        for (int i = 0; i < partiturList->topLevelItemCount(); ++i) {
              PartListItem* tli = static_cast<PartListItem*>(partiturList->topLevelItem(i));
              int partIdx = -1;
              for (int j = 0; j < tli->childCount(); ++j) {
                    StaffListItem* sli = static_cast<StaffListItem*>(tli->child(j));
                    if (!sli->isHidden()) {
                          partIdx++;
                          sli->setPartIdx(partIdx);
                          }
                    }
              }
        }
  
  //---------------------------------------------------------
  //   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()
        {
        QTreeWidgetItem* prvItem = nullptr;
        QList<QTreeWidgetItem*> wi = partiturList->selectedItems();
        if (!wi.isEmpty())
              prvItem = wi.front()->parent() ? wi.front()->parent() : wi.front();
  
        for (QTreeWidgetItem* i : instrumentList->selectedItems()) {
              InstrumentTemplateListItem* item = static_cast<InstrumentTemplateListItem*>(i);
              const InstrumentTemplate* it     = item->instrumentTemplate();
              if (it == 0)
                    continue;
              PartListItem* pli = prvItem ? new PartListItem(it, partiturList, prvItem) : 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);
              updatePartIdx();
              }
        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!
                          //
                          }
                    }
              }
        updatePartIdx();
        }
  
  //---------------------------------------------------------
  //   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!
                          //
                          }
                    }
              }
        updatePartIdx();
        }
  
  //---------------------------------------------------------
  //   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();
        updatePartIdx();
        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;
              }
              numberInstrumentNames(cs);
  #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();
        }
  
  //---------------------------------------------------------
  //   numberInstrumentNames
  //---------------------------------------------------------
  
  void InstrumentsWidget::numberInstrumentNames(Score* cs)
        {
        vector<QString> names;
        vector<QString> firsts;
  
        for (auto i = cs->parts().begin(); i != cs->parts().end(); ++i) {
              auto p = *i;
  
              QString name = p->partName();
  
              names.push_back(name);
              int n = 1;
  
              for (auto j = i + 1; j != cs->parts().end(); ++j) {
                    auto part = *j;
                    // number 2nd and subsequent instances of instrument
                    if (std::find(names.begin(), names.end(), part->partName()) != names.end())  {
                          firsts.push_back(name);
                          n++;
                          part->setPartName((part->partName() + QStringLiteral(" %1").arg(n)));
                          part->setLongName((part->longName() + QStringLiteral(" %1").arg(n)));
                          part->setShortName((part->shortName() + QStringLiteral(" %1").arg(n)));
                          }
                    }
  
              // now finish by adding first instances
              if (std::find(firsts.begin(), firsts.end(), p->partName()) != firsts.end()) {
                    p->setPartName(p->partName() + " 1");
                    p->setLongName(p->longName() + " 1");
                    p->setShortName(p->shortName() + " 1");
                    }
              }
        }
  
  //---------------------------------------------------------
  //   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;
        }
  }
  