    //=============================================================================
    //  MuseScore
    //  Linux Music Score Editor
    //  $Id: instrdialog.cpp 5580 2012-04-27 15:36:57Z wschweer $
    //
    //  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 {

    void filterInstruments(QTreeWidget *instrumentList, const QString &searchPhrase = QString());

    //---------------------------------------------------------
    //   filterInstruments
    //---------------------------------------------------------

    void filterInstruments(QTreeWidget* instrumentList, const QString &searchPhrase)
          {
          QTreeWidgetItem* item = 0;

          for (int idx = 0; (item = instrumentList->topLevelItem(idx)); ++idx) {
                int numMatchedChildren = 0;
                QTreeWidgetItem* ci = 0;

                for (int cidx = 0; (ci = item->child(cidx)); ++cidx) {
                      // replace the unicode b (accidential) so a search phrase of "bb" would give Bb Trumpet...
                      QString text = ci->text(0).replace(QChar(0x266d), QChar('b'));
                      bool isMatch = text.contains(searchPhrase, Qt::CaseInsensitive);
                      ci->setHidden(!isMatch);

                      if (isMatch)
                            numMatchedChildren++;
                      }

                item->setHidden(numMatchedChildren == 0);
                item->setExpanded(numMatchedChildren > 0 && !searchPhrase.isEmpty());
                }
          }

    //---------------------------------------------------------
    //   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
          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->strings() > 0;
                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)) {
                      _staffTypeCombo->addItem(st.name(), idx);
                      }
                ++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;
                            }
                      }
                qDebug("StaffListItem::setStaffType: not found\n");
                _staffTypeCombo->setCurrentIndex(0);      // if none found, default to standard staff type
                }
          }

    //---------------------------------------------------------
    //   setStaffType
    //---------------------------------------------------------

    void StaffListItem::setStaffType(int idx)
          {
          int i = _staffTypeCombo->findData(idx);
          if (i != -1)
                _staffTypeCombo->setCurrentIndex(i);
          }

    //---------------------------------------------------------
    //   staffType
    //---------------------------------------------------------

    const StaffType* StaffListItem::staffType() const
          {
          return StaffType::preset(StaffTypes((staffTypeIdx())));
          }

    //---------------------------------------------------------
    //   staffTypeIdx
    //---------------------------------------------------------

    int StaffListItem::staffTypeIdx() const
          {
          return _staffTypeCombo->itemData(_staffTypeCombo->currentIndex()).toInt();
          }

    //---------------------------------------------------------
    //   staffTypeChanged
    //---------------------------------------------------------

    void StaffListItem::staffTypeChanged(int idx)
          {
          // check current clef matches new staff type
          int staffTypeIdx = _staffTypeCombo->itemData(idx).toInt();
          const StaffType* stfType = StaffType::preset(StaffTypes(staffTypeIdx));

          PartListItem* pli = static_cast<PartListItem*>(QTreeWidgetItem::parent());
          pli->updateClefs();

          if (_staff && _staff->staffType(0)->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);
          belowButton->setEnabled(false);
          linkedButton->setEnabled(false);

          connect(instrumentList, SIGNAL(clicked(const QModelIndex &)), SLOT(expandOrCollapse(const QModelIndex &)));
          }

    //---------------------------------------------------------
    //   populateGenreCombo
    //---------------------------------------------------------

    void populateGenreCombo(QComboBox* combo)
          {
          combo->clear();
          combo->addItem(qApp->translate("InstrumentsDialog", "All instruments"), "all");
          int i = 1;
          int defaultIndex = 0;
          foreach (InstrumentGenre *ig, instrumentGenres) {
                combo->addItem(ig->name, ig->id);
                if (ig->id == "common")
                      defaultIndex = i;
                ++i;
                }
          combo->setCurrentIndex(defaultIndex);
          }

    //---------------------------------------------------------
     //   populateInstrumentList
    //---------------------------------------------------------

    void populateInstrumentList(QTreeWidget* instrumentList)
          {
          instrumentList->clear();
          // TODO: memory leak
          foreach(InstrumentGroup* g, instrumentGroups) {
                InstrumentTemplateListItem* group = new InstrumentTemplateListItem(g->name, instrumentList);
                group->setFlags(Qt::ItemIsEnabled);
                foreach(InstrumentTemplate* t, g->instrumentTemplates) {
                      new InstrumentTemplateListItem(t, group);
                      }
                }
          }

    //---------------------------------------------------------
    //   buildTemplateList
    //---------------------------------------------------------

    void InstrumentsWidget::buildTemplateList()
          {
          // clear search if instrument list is updated
          search->clear();
          filterInstruments(instrumentList, search->text());

          populateInstrumentList(instrumentList);
          populateGenreCombo(instrumentGenreFilter);
          }

    //---------------------------------------------------------
    //   expandOrCollapse
    //---------------------------------------------------------

    void InstrumentsWidget::expandOrCollapse(const QModelIndex &model)
          {
          if(instrumentList->isExpanded(model))
                instrumentList->collapse(model);
          else
                instrumentList->expand(model);
          }

    //---------------------------------------------------------
    //   genPartList
    //---------------------------------------------------------

    void InstrumentsWidget::genPartList(Score* cs)
          {
          partiturList->clear();

          foreach (Part* p, cs->parts()) {
                PartListItem* pli = new PartListItem(p, partiturList);
                pli->setVisible(p->show());
                foreach (Staff* s, *p->staves()) {
                      StaffListItem* sli = new StaffListItem(pli);
                      sli->setStaff(s);
                      sli->setClefType(s->clefType(0));
                      sli->setDefaultClefType(s->defaultClefType());
                      sli->setPartIdx(s->rstaff());
                      const LinkedStaves* ls = s->linkedStaves();
                      bool bLinked = false;
                      if (ls && !ls->empty()) {
                            foreach(Staff* ps, ls->staves()) {
                                  if (ps != s && ps->score() == s->score()) {
                                        bLinked = true;
                                        break;
                                        }
                                  }
                            }
                      sli->setLinked(bLinked);
                      sli->setStaffType(s->staffType(0));    // 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);
                linkedButton->setEnabled(false);
                belowButton->setEnabled(false);
                return;
                }
          QTreeWidgetItem* item = wi.front();
          bool flag = item != 0;

          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);
          linkedButton->setEnabled(item && item->type() == STAFF_LIST_ITEM);
          belowButton->setEnabled(item && item->type() == STAFF_LIST_ITEM);
          }

    //---------------------------------------------------------
    //   on_instrumentList
    //---------------------------------------------------------

    void InstrumentsWidget::on_instrumentList_itemDoubleClicked(QTreeWidgetItem*, int)
          {
          on_addButton_clicked();
          }

    //---------------------------------------------------------
    //   on_addButton_clicked
    //    add instrument to partitur
    //---------------------------------------------------------

    void InstrumentsWidget::on_addButton_clicked()
          {
          foreach(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 i = 0; i < n; ++i) {
                      StaffListItem* sli = new StaffListItem(pli);
                      sli->setOp(ListItemOp::ADD);
                      sli->setStaff(0);
                      sli->setPartIdx(i);
                      sli->setDefaultClefType(it->clefType(i));
                      sli->setStaffType(it->staffTypePreset);
                      }
                pli->updateClefs();
                partiturList->setItemExpanded(pli, true);
                partiturList->clearSelection();     // should not be necessary
                partiturList->setItemSelected(pli, true);
                }
          emit completeChanged(true);
          }

    //---------------------------------------------------------
    //   on_removeButton_clicked
    //    remove instrument from partitur
    //---------------------------------------------------------

    void InstrumentsWidget::on_removeButton_clicked()
          {
          QList<QTreeWidgetItem*> wi = partiturList->selectedItems();
          if (wi.isEmpty())
                return;
          QTreeWidgetItem* item   = wi.front();
          QTreeWidgetItem* parent = item->parent();

          if (parent) {
                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->setItemSelected(parent, true);
                }
          else {
                int idx = partiturList->indexOfTopLevelItem(item);
                if (((PartListItem*)item)->op == ListItemOp::ADD)
                      delete item;
                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) { // could find after, check before
                      plusIdx = 1;
                      nextParent = partiturList->topLevelItem(idx - plusIdx);
                      while (nextParent && nextParent->isHidden()) {
                           plusIdx++;
                           nextParent = partiturList->topLevelItem(idx - plusIdx);
                      }
                      }
                partiturList->setItemSelected(nextParent, true);
                }
          }

    //---------------------------------------------------------
    //   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* item = 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 = item->childCount();
                      int staffIdx[numOfStaffListItems];
                      for (int itemIdx=0; itemIdx < numOfStaffListItems; ++itemIdx)
                            staffIdx[itemIdx] = (static_cast<StaffListItem*>(item->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, item);
                      // after-re-insertion, recreate each combo and set its index
                      for (int itemIdx=0; itemIdx < numOfStaffListItems; ++itemIdx) {
                            StaffListItem* staffItem = static_cast<StaffListItem*>(item->child(itemIdx));
                            staffItem->initStaffTypeCombo(true);
                            staffItem->setStaffType(staffIdx[itemIdx]);
                            }
                      partiturList->setItemExpanded(item, isExpanded);
                      partiturList->setItemSelected(item, true);
                      }
                }
          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* item = 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 = item->staffTypeIdx();
                      parent->insertChild(idx - 1, item);
                      // after item has been inserted into the tree, create a new QComboBox and set its index
                      item->initStaffTypeCombo(true);
                      item->setStaffType(staffTypeIdx);
                      partiturList->setItemSelected(item, true);
                      }
                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->setItemSelected(sli, true);
    //                        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* item = 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 = item->childCount();
                      int staffIdx[numOfStaffListItems];
                      int itemIdx;
                      for (itemIdx=0; itemIdx < numOfStaffListItems; ++itemIdx)
                            staffIdx[itemIdx] = (static_cast<StaffListItem*>(item->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, item);
                      // after-re-insertion, recreate each combo and set its index
                      for (itemIdx=0; itemIdx < numOfStaffListItems; ++itemIdx) {
                            StaffListItem* staffItem = static_cast<StaffListItem*>(item->child(itemIdx));
                            staffItem->initStaffTypeCombo(true);
                            staffItem->setStaffType(staffIdx[itemIdx]);
                            }
                      partiturList->setItemExpanded(item, isExpanded);
                      partiturList->setItemSelected(item, true);
                      }
                }
          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* item = 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 = item->staffTypeIdx();
                      parent->insertChild(idx+1, item);
                      // after item has been inserted into the tree, create a new QComboBox and set its index
                      item->initStaffTypeCombo(true);
                      item->setStaffType(staffTypeIdx);
                      partiturList->setItemSelected(item, true);
                      }
                else {
                      // if staff item is last of its part...
                      int parentIdx = partiturList->indexOfTopLevelItem(parent);
                      int n = partiturList->topLevelItemCount();
                      //..and there is a next part, move to next part
                      if (parentIdx < (n-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->setItemSelected(sli, true);
    //                        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_belowButton_clicked
    //    (actually "Add Staff" button)
    //---------------------------------------------------------

    StaffListItem* InstrumentsWidget::on_belowButton_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->setItemSelected(nsli, true);
          pli->updateClefs();
          return nsli;
          }

    //---------------------------------------------------------
    //   on_linkedButton_clicked
    //---------------------------------------------------------

    void InstrumentsWidget::on_linkedButton_clicked()
          {
          StaffListItem* nsli = on_belowButton_clicked();
          if (nsli)
                nsli->setLinked(true);
          }

    //---------------------------------------------------------
    //   on_search_textChanged
    //---------------------------------------------------------

    void InstrumentsWidget::on_search_textChanged(const QString &searchPhrase)
          {
          if (searchPhrase.isEmpty())
                return;

          filterInstruments(instrumentList, searchPhrase);
          instrumentGenreFilter->blockSignals(true);
          instrumentGenreFilter->setCurrentIndex(0);
          instrumentGenreFilter->blockSignals(false);
          }

    //---------------------------------------------------------
    //   on_clearSearch_clicked
    //---------------------------------------------------------

    void InstrumentsWidget::on_clearSearch_clicked()
          {
          search->clear();
          QString genre = instrumentGenreFilter->currentData().toString();
          filterInstrumentsByGenre(instrumentList, genre);
          }

    //---------------------------------------------------------
    //   on_instrumentGenreFilter_currentTextChanged
    //---------------------------------------------------------

    void InstrumentsWidget::on_instrumentGenreFilter_currentIndexChanged(int index)
          {
          QString id = instrumentGenreFilter->itemData(index).toString();
          // Redisplay tree, only showing items from the selected genre
          filterInstrumentsByGenre(instrumentList, id);
          }

    //---------------------------------------------------------
    //   filterInstrumentsByGenre
    //---------------------------------------------------------

    void InstrumentsWidget::filterInstrumentsByGenre(QTreeWidget *instrumentList, QString genre)
          {
          QTreeWidgetItemIterator iList(instrumentList);
          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);
          linkedButton->setEnabled(false);
          belowButton->setEnabled(false);
          emit completeChanged(false);
          }

    //---------------------------------------------------------
    //   getPartiturList
    //---------------------------------------------------------

    QTreeWidget* InstrumentsWidget::getPartiturList()
          {
          return partiturList;
          }
    }

↑ V595 The 'item' pointer was utilized before it was verified against nullptr. Check lines: 521, 536.