    //=============================================================================
    //  MuseScore
    //  Music Composition & Notation
    //  $Id: palette.cpp 5576 2012-04-24 19:15:22Z wschweer $
    //
    //  Copyright (C) 2011 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
    //  as published by the Free Software Foundation and appearing in
    //  the file LICENSE.GPL
    //=============================================================================

    #include "palette.h"
    #include "musescore.h"
    #include "libmscore/element.h"
    #include "libmscore/style.h"
    #include "globals.h"
    #include "libmscore/sym.h"
    #include "libmscore/symbol.h"
    #include "libmscore/score.h"
    #include "libmscore/image.h"
    #include "libmscore/xml.h"
    #include "scoreview.h"
    #include "libmscore/note.h"
    #include "libmscore/chord.h"
    #include "libmscore/clef.h"
    #include "libmscore/segment.h"
    #include "libmscore/measure.h"
    #include "libmscore/staff.h"
    #include "libmscore/system.h"
    #include "libmscore/page.h"
    #include "libmscore/keysig.h"
    #include "libmscore/timesig.h"
    #include "preferences.h"
    #include "seq.h"
    #include "libmscore/part.h"
    #include "libmscore/textline.h"
    #include "libmscore/measure.h"
    #include "libmscore/icon.h"
    #include "libmscore/mscore.h"
    #include "libmscore/imageStore.h"
    #include "thirdparty/qzip/qzipreader_p.h"
    #include "thirdparty/qzip/qzipwriter_p.h"
    #include "libmscore/slur.h"
    #include "paletteBoxButton.h"
    #include "palettebox.h"

    namespace Ms {

    PaletteCell::~PaletteCell()
          {
          delete element;
          }

    //---------------------------------------------------------
    //   needsStaff
    //    should a staff been drawn if e is used as icon in
    //    a palette
    //---------------------------------------------------------

    static bool needsStaff(Element* e)
          {
          if (e == 0)
                return false;
          switch(e->type()) {
                case ElementType::CHORD:
                case ElementType::BAR_LINE:
                case ElementType::CLEF:
                case ElementType::KEYSIG:
                case ElementType::TIMESIG:
                case ElementType::REST:
                case ElementType::BAGPIPE_EMBELLISHMENT:
                      return true;
                default:
                      return false;
                }
          }

    //---------------------------------------------------------
    //   Palette
    //---------------------------------------------------------

    Palette::Palette(QWidget* parent)
       : QWidget(parent)
          {
          extraMag      = 1.0;
          currentIdx    = -1;
          dragIdx       = -1;
          selectedIdx   = -1;
          _yOffset      = 0.0;
          setGrid(50, 60);
          _drawGrid     = false;
          _selectable   = false;
          setMouseTracking(true);
          setReadOnly(false);
          setSystemPalette(false);
          _moreElements = false;
          setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Ignored);
          }

    Palette::~Palette()
          {
          for (PaletteCell* cell : cells)
                delete cell;
          }

    //---------------------------------------------------------
    //   resizeEvent
    //---------------------------------------------------------

    void Palette::resizeEvent(QResizeEvent* e)
          {
          setFixedHeight(heightForWidth(e->size().width()));
          }

    //---------------------------------------------------------
    //   filter
    ///  return true if all filtered
    //---------------------------------------------------------

    bool Palette::filter(const QString& text)
          {
          filterActive = false;
          setMouseTracking(true);
          QString t = text.toLower();
          if (t == "") {
                mscore->getPaletteBox()->setKeyboardNavigation(false);
                for (Palette* p : mscore->getPaletteBox()->palettes()) {
                      if (p->getCurrentIdx() != -1) {
                            p->setCurrentIdx(-1);
                            p->update();
                            }
                      }
                }
          bool res = true;
          dragCells.clear();
          // if palette name is searched for, display all elements in the palette
          if (_name.startsWith(t, Qt::CaseInsensitive)) {
                PaletteCell* c  = cells.first();
                for (PaletteCell* cell : cells)
                      dragCells.append(cell);

                bool contains = t.isEmpty() || c;
                if (!contains)
                      filterActive = true;
                if (contains && res)
                      res = false;
                }

          for (PaletteCell* cell : cells) {
                QStringList h = cell->name.toLower().split(" ");
                bool c        = false;
                QStringList n = t.split(" ");
                for (QString hs : h) {
                      for (QString ns : n) {
                            if (!ns.trimmed().isEmpty())
                                  c = hs.trimmed().startsWith(ns.trimmed());
                            }
                      if (c)
                            break;
                      }
                if (t.isEmpty() || c)
                      dragCells.append(cell);
                bool contains = t.isEmpty() || c;
                if (!contains)
                      filterActive = true;
                if (contains && res)
                      res = false;
                }
          setFixedHeight(heightForWidth(width()));
          updateGeometry();
          return res;
          }

    //---------------------------------------------------------
    //   setMoreElements
    //---------------------------------------------------------

    void Palette::setMoreElements(bool val)
          {
          _moreElements = val;
          if (val && (cells.isEmpty() || cells.back()->tag != "ShowMore")) {
                PaletteCell* cell = new PaletteCell;
                cell->name      = "Show More";
                cell->tag       = "ShowMore";
                cells.append(cell);
                }
          else if (!val && !cells.isEmpty() && (cells.last()->tag == "ShowMore") ) {
                PaletteCell* cell = cells.takeLast();
                delete cell;
                }
          }

    //---------------------------------------------------------
    //   setSystemPalette
    //---------------------------------------------------------

    void Palette::setSystemPalette(bool val)
          {
          _systemPalette = val;
          if (val)
                setReadOnly(true);
          }

    //---------------------------------------------------------
    //   setReadOnly
    //---------------------------------------------------------

    void Palette::setReadOnly(bool val)
          {
          _readOnly = val;
          setAcceptDrops(!val);
          }

    //---------------------------------------------------------
    //   setMag
    //---------------------------------------------------------

    void Palette::setMag(qreal val)
          {
          extraMag = val;
          }

    //---------------------------------------------------------
    //   contextMenuEvent
    //---------------------------------------------------------

    void Palette::contextMenuEvent(QContextMenuEvent* event)
          {
          if (!_showContextMenu)
                return;
          int i = idx(event->pos());
          if (i == -1) {
                // palette context menu
                QMenu menu;
                QAction* moreAction = menu.addAction(tr("More Elements..."));
                moreAction->setEnabled(_moreElements);
                QAction* action = menu.exec(mapToGlobal(event->pos()));
                if (action == moreAction)
                      emit displayMore(_name);
                return;
                }

          QMenu menu;
          QAction* clearAction   = menu.addAction(tr("Clear"));
          QAction* contextAction = menu.addAction(tr("Properties..."));
          clearAction->setEnabled(!_readOnly);
          contextAction->setEnabled(!_readOnly);
          QAction* moreAction    = menu.addAction(tr("More Elements..."));
          moreAction->setEnabled(_moreElements);

          if (cellAt(i) && cellAt(i)->readOnly)
                clearAction->setEnabled(false);

          QAction* action = menu.exec(mapToGlobal(event->pos()));

          if (action == clearAction) {
                PaletteCell* cell = cellAt(i);
                if (cell)
                      delete cell;
                cells[i] = 0;
                emit changed();
                }
          else if (action == contextAction) {
                PaletteCell* c = cellAt(i);
                if (c == 0)
                      return;
                PaletteCellProperties props(c);
                if (props.exec())
                      emit changed();
                }
          else if (moreAction && (action == moreAction))
                emit displayMore(_name);

          bool sizeChanged = false;
          while (!cells.isEmpty() && cells.back() == 0) {
                cells.removeLast();
                sizeChanged = true;
                }
          if (sizeChanged) {
                setFixedHeight(heightForWidth(width()));
                updateGeometry();
                }
          update();
          }

    //---------------------------------------------------------
    //   setGrid
    //---------------------------------------------------------

    void Palette::setGrid(int hh, int vv)
          {
          hgrid = hh;
          vgrid = vv;
          QSize s(hgrid, vgrid);
          setSizeIncrement(s);
          setBaseSize(s);
          setMinimumSize(s);
          updateGeometry();
          }

    //---------------------------------------------------------
    //   element
    //---------------------------------------------------------

    Element* Palette::element(int idx)
          {
          if (idx < size() && cellAt(idx))
                return cellAt(idx)->element;
          else
                return 0;
          }

    //---------------------------------------------------------
    //   contentsMousePressEvent
    //---------------------------------------------------------

    void Palette::mousePressEvent(QMouseEvent* ev)
          {
          dragStartPosition = ev->pos();
          dragIdx           = idx(dragStartPosition);

          if (dragIdx == -1)
                return;
          if (_selectable) {
                if (dragIdx != selectedIdx) {
                      update(idxRect(dragIdx) | idxRect(selectedIdx));
                      selectedIdx = dragIdx;
                      }
                emit boxClicked(dragIdx);
                }
          PaletteCell* cell = cellAt(dragIdx);
          if (cell && (cell->tag == "ShowMore"))
                emit displayMore(_name);
          if (mscore->getPaletteBox()->getKeyboardNavigation()) {
                PaletteBox* pb = mscore->getPaletteBox();
                pb->mousePressEvent(ev, this);
                update();
                }
          }

    //---------------------------------------------------------
    //   mouseMoveEvent
    //---------------------------------------------------------

    void Palette::mouseMoveEvent(QMouseEvent* ev)
          {
          PaletteBox* pb = mscore->getPaletteBox();
          if (pb->getKeyboardNavigation()) {
                ev->ignore();
                return;
                }
          if ((currentIdx != -1) && (dragIdx == currentIdx) && (ev->buttons() & Qt::LeftButton)
             && (ev->pos() - dragStartPosition).manhattanLength() > QApplication::startDragDistance())
                {
                PaletteCell* cell = cellAt(currentIdx);
                if (cell && cell->element) {
                      QDrag* drag         = new QDrag(this);
                      QMimeData* mimeData = new QMimeData;
                      Element* el         = cell->element;

                      mimeData->setData(mimeSymbolFormat, el->mimeData(QPointF()));
                      drag->setMimeData(mimeData);

                      drag->setPixmap(pixmap(currentIdx));
                      QRect r = idxRect(currentIdx);

                      QPoint o(dragStartPosition - r.topLeft());
                      drag->setHotSpot(o);

                      Qt::DropActions da;
                      if (!(_readOnly || filterActive) && (ev->modifiers() & Qt::ShiftModifier)) {
                            dragCells = cells;      // backup
                            da = Qt::MoveAction;
                            }
                      else
                            da = Qt::CopyAction;
                      Qt::DropAction a = drag->exec(da);
                      if (da == Qt::MoveAction && a != da)
                            cells = dragCells;      // restore on a failed move action
                      update();
                      }
                }
          else {
                currentIdx = idx(ev->pos());
                if (currentIdx != -1 && cellAt(currentIdx) == 0)
                      currentIdx = -1;
                update();
                }
          }

    //---------------------------------------------------------
    //   applyDrop
    //---------------------------------------------------------

    static void applyDrop(Score* score, ScoreView* viewer, Element* target, Element* e, QPointF pt = QPointF())
          {
          EditData dropData(viewer);
          dropData.pos        = pt.isNull() ? target->pagePos() : pt;
          dropData.dragOffset = QPointF();
          dropData.modifiers  = 0;
          dropData.element    = e;

          if (target->acceptDrop(dropData)) {
                // use same code path as drag&drop

                QByteArray a = e->mimeData(QPointF());
                XmlReader e(gscore, a);
                Fraction duration;  // dummy
                QPointF dragOffset;
                ElementType type = Element::readType(e, &dragOffset, &duration);
                dropData.element = Element::create(type, score);

                dropData.element->read(e);
                dropData.element->styleChanged();   // update to local style

                Element* el = target->drop(dropData);
                if (el)
                      score->select(el, SelectType::SINGLE, 0);
                }
          }

    //---------------------------------------------------------
    //   applyPaletteElement
    //---------------------------------------------------------

    void Palette::applyPaletteElement(PaletteCell* cell)
          {
          Score* score = mscore->currentScore();
          if (score == 0)
                return;
          Selection sel = score->selection();       // make a copy of the list
          if (sel.isNone())
                return;

          Element* element = 0;
          if (cell)
                element = cell->element;
          if (element == 0)
                return;

          ScoreView* viewer = mscore->currentScoreView();
          if (viewer->mscoreState() != STATE_EDIT
             && viewer->mscoreState() != STATE_LYRICS_EDIT
             && viewer->mscoreState() != STATE_HARMONY_FIGBASS_EDIT
             && viewer->mscoreState() != STATE_TEXT_EDIT) { // Already in startCmd in this case
                score->startCmd();
                }
          if (sel.isList()) {
                ChordRest* cr1 = sel.firstChordRest();
                ChordRest* cr2 = sel.lastChordRest();
                bool addSingle = false;       // add a single line only
                if (cr1 && cr2 == cr1) {
                      // one chordrest selected, ok to add line
                      addSingle = true;
                      }
                else if (sel.elements().size() == 2 && cr1 && cr2 && cr1 != cr2) {
                      // two chordrests selected
                      // must be on same staff in order to add line, except for slur
                      if (element->isSlur() || cr1->staffIdx() == cr2->staffIdx())
                            addSingle = true;
                      }
                if (viewer->mscoreState() == STATE_NOTE_ENTRY_STAFF_DRUM && element->isChord()) {
                      // use input position rather than selection if possible
                      Element* e = score->inputState().cr();
                      if (!e)
                            e = sel.elements().first();
                      if (e) {
                            // get note if selection was full chord
                            if (e->isChord())
                                  e = toChord(e)->upNote();
                            // use voice of element being added to (otherwise we can might corrupt the measure)
                            element->setTrack(e->voice());
                            applyDrop(score, viewer, e, element);
                            // continue in same track
                            score->inputState().setTrack(e->track());
                            }
                      else
                            qDebug("nowhere to place drum note");
                      }
                else if (element->isSlur() && addSingle) {
                      viewer->addSlur();
                      }
                else if (element->isSLine() && element->type() != ElementType::GLISSANDO && addSingle) {
                      Segment* startSegment = cr1->segment();
                      Segment* endSegment = cr2->segment();
                      if (element->type() == ElementType::PEDAL && cr2 != cr1)
                            endSegment = endSegment->nextCR(cr2->track());
                      // TODO - handle cross-voice selections
                      int idx = cr1->staffIdx();

                      QByteArray a = element->mimeData(QPointF());
                      XmlReader e(gscore, a);
                      Fraction duration;  // dummy
                      QPointF dragOffset;
                      ElementType type = Element::readType(e, &dragOffset, &duration);
                      Spanner* spanner = static_cast<Spanner*>(Element::create(type, score));
                      spanner->read(e);
                      score->cmdAddSpanner(spanner, idx, startSegment, endSegment);
                      }
                else {
                      for (Element* e : sel.elements())
                            applyDrop(score, viewer, e, element);
                      }
                }
          else if (sel.isRange()) {
                if (element->type() == ElementType::BAR_LINE
                    || element->type() == ElementType::MARKER
                    || element->type() == ElementType::JUMP
                    || element->type() == ElementType::SPACER
                    || element->type() == ElementType::VBOX
                    || element->type() == ElementType::HBOX
                    || element->type() == ElementType::TBOX
                    || element->type() == ElementType::MEASURE
                    || element->type() == ElementType::BRACKET
                    || (element->type() == ElementType::ICON
                        && (toIcon(element)->iconType() == IconType::VFRAME
                            || toIcon(element)->iconType() == IconType::HFRAME
                            || toIcon(element)->iconType() == IconType::TFRAME
                            || toIcon(element)->iconType() == IconType::MEASURE
                            || toIcon(element)->iconType() == IconType::BRACKETS))) {
                      Measure* last = sel.endSegment() ? sel.endSegment()->measure() : nullptr;
                      for (Measure* m = sel.startSegment()->measure(); m; m = m->nextMeasureMM()) {
                            QRectF r = m->staffabbox(sel.staffStart());
                            QPointF pt(r.x() + r.width() * .5, r.y() + r.height() * .5);
                            pt += m->system()->page()->pos();
                            applyDrop(score, viewer, m, element, pt);
                            if (m == last)
                                  break;
                            }
                      }
                else if (element->type() == ElementType::LAYOUT_BREAK) {
                      LayoutBreak* breakElement = static_cast<LayoutBreak*>(element);
                      score->cmdToggleLayoutBreak(breakElement->layoutBreakType());
                      }
                else if (element->isClef() || element->isKeySig() || element->isTimeSig()) {
                      Measure* m1 = sel.startSegment()->measure();
                      Measure* m2 = sel.endSegment() ? sel.endSegment()->measure() : nullptr;
                      if (m2 == m1 && sel.startSegment()->rtick() == 0)
                            m2 = nullptr;     // don't restore original if one full measure selected
                      else if (m2)
                            m2 = m2->nextMeasureMM();
                      // for clefs, apply to each staff separately
                      // otherwise just apply to top staff
                      int staffIdx1 = sel.staffStart();
                      int staffIdx2 = element->type() == ElementType::CLEF ? sel.staffEnd() : staffIdx1 + 1;
                      for (int i = staffIdx1; i < staffIdx2; ++i) {
                            // for clefs, use mid-measure changes if appropriate
                            Element* e1 = nullptr;
                            Element* e2 = nullptr;
                            // use mid-measure clef changes as appropriate
                            if (element->type() == ElementType::CLEF) {
                                  if (sel.startSegment()->segmentType() == SegmentType::ChordRest && sel.startSegment()->rtick() != 0) {
                                        ChordRest* cr = static_cast<ChordRest*>(sel.startSegment()->nextChordRest(i * VOICES));
                                        if (cr && cr->isChord())
                                              e1 = static_cast<Chord*>(cr)->upNote();
                                        else
                                              e1 = cr;
                                        }
                                  if (sel.endSegment() && sel.endSegment()->segmentType() == SegmentType::ChordRest) {
                                        ChordRest* cr = static_cast<ChordRest*>(sel.endSegment()->nextChordRest(i * VOICES));
                                        if (cr && cr->isChord())
                                              e2 = static_cast<Chord*>(cr)->upNote();
                                        else
                                              e2 = cr;
                                        }
                                  }
                            if (m2 || e2) {
                                  // restore original clef/keysig/timesig
                                  Staff* staff = score->staff(i);
                                  int tick1 = sel.startSegment()->tick();
                                  Element* oelement = nullptr;
                                  switch (element->type()) {
                                        case ElementType::CLEF:
                                              {
                                              Clef* oclef = new Clef(score);
                                              oclef->setClefType(staff->clef(tick1));
                                              oelement = oclef;
                                              break;
                                              }
                                        case ElementType::KEYSIG:
                                              {
                                              KeySig* okeysig = new KeySig(score);
                                              okeysig->setKeySigEvent(staff->keySigEvent(tick1));
                                              if (!score->styleB(StyleIdx::concertPitch) && !okeysig->isCustom() && !okeysig->isAtonal()) {
                                                    Interval v = staff->part()->instrument(tick1)->transpose();
                                                    if (!v.isZero()) {
                                                          Key k = okeysig->key();
                                                          okeysig->setKey(transposeKey(k, v));
                                                          }
                                                    }
                                              oelement = okeysig;
                                              break;
                                              }
                                        case ElementType::TIMESIG:
                                              {
                                              TimeSig* otimesig = new TimeSig(score);
                                              otimesig->setFrom(staff->timeSig(tick1));
                                              oelement = otimesig;
                                              break;
                                              }
                                        default:
                                              break;
                                        }
                                  if (oelement) {
                                        if (e2) {
                                              applyDrop(score, viewer, e2, oelement);
                                              }
                                        else {
                                              QRectF r = m2->staffabbox(i);
                                              QPointF pt(r.x() + r.width() * .5, r.y() + r.height() * .5);
                                              pt += m2->system()->page()->pos();
                                              applyDrop(score, viewer, m2, oelement, pt);
                                              }
                                        delete oelement;
                                        }
                                  }
                            // apply new clef/keysig/timesig
                            if (e1) {
                                  applyDrop(score, viewer, e1, element);
                                  }
                            else {
                                  QRectF r = m1->staffabbox(i);
                                  QPointF pt(r.x() + r.width() * .5, r.y() + r.height() * .5);
                                  pt += m1->system()->page()->pos();
                                  applyDrop(score, viewer, m1, element, pt);
                                  }
                            }
                      }
                else if (element->isSlur()) {
                      viewer->addSlur();
                      }
                else if (element->isSLine() && element->type() != ElementType::GLISSANDO) {
                      Segment* startSegment = sel.startSegment();
                      Segment* endSegment = sel.endSegment();
                      int endStaff = sel.staffEnd();
                      for (int i = sel.staffStart(); i < endStaff; ++i) {
                            Spanner* spanner = static_cast<Spanner*>(element->clone());
                            spanner->setScore(score);
                            score->cmdAddSpanner(spanner, i, startSegment, endSegment);
                            }
                      }
                else {
                      int track1 = sel.staffStart() * VOICES;
                      int track2 = sel.staffEnd() * VOICES;
                      Segment* startSegment = sel.startSegment();
                      Segment* endSegment = sel.endSegment(); //keep it, it could change during the loop
                      for (Segment* s = startSegment; s && s != endSegment; s = s->next1()) {
                            for (int track = track1; track < track2; ++track) {
                                  Element* e = s->element(track);
                                  if (e == 0 || !score->selectionFilter().canSelect(e) || !score->selectionFilter().canSelectVoice(track))
                                        continue;
                                  if (e->type() == ElementType::CHORD) {
                                        Chord* chord = static_cast<Chord*>(e);
                                        for (Note* n : chord->notes())
                                              applyDrop(score, viewer, n, element);
                                        }
                                  else {
                                        // do not apply articulation to barline in a range selection
                                        if(e->type() != ElementType::BAR_LINE || element->type() != ElementType::ARTICULATION)
                                              applyDrop(score, viewer, e, element);
                                        }
                                  }
                            }
                      }
                }
          else
                qDebug("unknown selection state");
          if (viewer->mscoreState() != STATE_EDIT
             && viewer->mscoreState() != STATE_LYRICS_EDIT
             && viewer->mscoreState() != STATE_HARMONY_FIGBASS_EDIT
             && viewer->mscoreState() != STATE_TEXT_EDIT) { //Already in startCmd mode in this case
                score->endCmd();
                if (viewer->mscoreState() == STATE_NOTE_ENTRY_STAFF_DRUM)
                      viewer->moveCursor();
                }
          viewer->setDropTarget(0);
          mscore->endCmd();
          }

    //---------------------------------------------------------
    //   keyPressEvent
    //---------------------------------------------------------

    void PaletteScrollArea::keyPressEvent(QKeyEvent* event)
          {
          QWidget* w = this->widget();
          Palette* p = static_cast<Palette*>(w);
          if (event->key() == Qt::Key_Right) {
                int i = p->getSelectedIdx();
                if (i == -1)
                      p->setSelected(0);
                i++;
                if (i >= 0 && i < p->size())
                      p->setSelected(i);
                else
                      p->setSelected(0);

                p->update();
                }
          else if (event->key() == Qt::Key_Left) {
                int i = p->getSelectedIdx();
                if (i == -1)
                      p->setSelected(p->size()-1);
                i--;
                if (i >= 0 && i < p->size())
                      p->setSelected(i);
                else
                      p->setSelected(p->size()-1);

                p->update();
                }
          QScrollArea::keyPressEvent(event);
          }

    //---------------------------------------------------------
    //   mouseDoubleClickEvent
    //---------------------------------------------------------

    void Palette::mouseDoubleClickEvent(QMouseEvent* ev)
          {
          if (_disableDoubleClick)
                return;
          int i = idx(ev->pos());
          if (i == -1)
                return;
          Score* score = mscore->currentScore();
          if (score == 0)
                return;
          Selection sel = score->selection();       // make a copy of the list
          if (sel.isNone())
                return;

          applyPaletteElement(cellAt(i));
          }

    //---------------------------------------------------------
    //   idx
    //---------------------------------------------------------

    int Palette::idx(const QPoint& p) const
          {
          if (columns() == 0)
                return -1;
          int rightBorder = width() % hgrid;
          int hhgrid      = hgrid + (rightBorder / columns());

          int x = p.x();
          int y = p.y();

          int row = y / vgrid;
          int col = x / hhgrid;

          int nc = columns();
          if (col > nc)
                return -1;

          int idx = row * nc + col;
          if (idx < 0 || idx >= size())
                return -1;
          return idx;
          }

    //---------------------------------------------------------
    //   idx2
    //   returns indexes outside of cells.size()
    //---------------------------------------------------------

    int Palette::idx2(const QPoint& p) const
          {
          if (columns() == 0)
                return -1;
          int rightBorder = width() % hgrid;
          int hhgrid      = hgrid + (rightBorder / columns());

          int x = p.x();
          int y = p.y();

          int row = y / vgrid;
          int col = x / hhgrid;

          int nc = columns();
          if (col > nc)
                return -1;

          int idx = row * nc + col;
          if (idx < 0 || idx > columns()*rows())
                return -1;
          return idx;
          }

    //---------------------------------------------------------
    //   idxRect
    //---------------------------------------------------------

    QRect Palette::idxRect(int i) const
          {
          if (i == -1)
                return QRect();
         if (columns() == 0)
                return QRect();

          int rightBorder = width() % hgrid;
          int hhgrid = hgrid + (rightBorder / columns());

          int cc = i % columns();
          int cr = i / columns();
          return QRect(cc * hhgrid, cr * vgrid, hhgrid, vgrid);
          }

    //---------------------------------------------------------
    //   leaveEvent
    //---------------------------------------------------------

    void Palette::leaveEvent(QEvent*)
          {
          if (currentIdx != -1) {
                QRect r = idxRect(currentIdx);
                PaletteBox* pb = mscore->getPaletteBox();
                if (!pb->getKeyboardNavigation())
                      currentIdx = -1;
                update(r);
                }
          }

    //---------------------------------------------------------
    //   nextPaletteElement
    //---------------------------------------------------------
    void Palette::nextPaletteElement()
          {
          PaletteBox* pb = mscore->getPaletteBox();
          if (!pb->getKeyboardNavigation())
                return;
          int i = currentIdx;
          if (i == -1)
                return;
          i++;
          if (i < size() && cellAt(i))
                currentIdx = i;

          update();
          }

    //---------------------------------------------------------
    //   prevPaletteElement
    //---------------------------------------------------------

    void Palette::prevPaletteElement()
          {
          PaletteBox* pb = mscore->getPaletteBox();
          if (!pb->getKeyboardNavigation())
                return;
          int i = currentIdx;
          if (i == -1)
                return;
          i--;
          if (i >= 0 && cellAt(i))
                currentIdx = i;

          update();
          }

    //---------------------------------------------------------
    //   applyPaletteElement
    //---------------------------------------------------------

    void Palette::applyPaletteElement()
          {
          Score* score = mscore->currentScore();
          if (score == 0)
                return;
          const Selection& sel = score->selection();
          if (sel.isNone())
                return;
          // apply currently selected palette symbol to selected score elements
          int i = currentIdx;
          if (i < size() && cellAt(i))
                applyPaletteElement(cellAt(i));
          else
                return;
          }

    //---------------------------------------------------------
    //   append
    //    append element to palette
    //---------------------------------------------------------

    PaletteCell* Palette::append(Element* s, const QString& name, QString tag, qreal mag)
          {
          if (s == 0) {
                cells.append(0);
                return 0;
                }
          PaletteCell* cell = new PaletteCell;
          int idx;
          if (_moreElements) {
                cells.insert(cells.size() - 1, cell);
                idx = cells.size() - 2;
                }
          else {
                cells.append(cell);
                idx = cells.size() - 1;
                }
          PaletteCell* pc = add(idx, s, name, tag, mag);
          setFixedHeight(heightForWidth(width()));
          updateGeometry();
          return pc;
          }

    //---------------------------------------------------------
    //   add
    //---------------------------------------------------------

    PaletteCell* Palette::add(int idx, Element* s, const QString& name, QString tag, qreal mag)
          {
          if (s) {
                s->setPos(0.0, 0.0);
                s->setUserOff(QPointF());
                s->setReadPos(QPointF());
                }

          PaletteCell* cell = new PaletteCell;
          if (idx < cells.size()) {
                delete cells[idx];
                }
          else {
                for (int i = cells.size(); i <= idx; ++i)
                      cells.append(0);
                }
          cells[idx]      = cell;
          cell->element   = s;
          cell->name      = name;
          cell->tag       = tag;
          cell->drawStaff = needsStaff(s);
          cell->xoffset   = 0;
          cell->yoffset   = 0;
          cell->mag       = mag;
          cell->readOnly  = false;
          update();
          if (s && s->isIcon()) {
                Icon* icon = toIcon(s);
                connect(getAction(icon->action()), SIGNAL(toggled(bool)), SLOT(actionToggled(bool)));
                }
          updateGeometry();
          return cell;
          }

    //---------------------------------------------------------
    //   paintPaletteElement
    //---------------------------------------------------------

    static void paintPaletteElement(void* data, Element* e)
          {
          QPainter* p = static_cast<QPainter*>(data);
          p->save();
          p->translate(e->pos());
          e->draw(p);
          p->restore();
          }

    //---------------------------------------------------------
    //   paintEvent
    //---------------------------------------------------------

    void Palette::paintEvent(QPaintEvent* /*event*/)
          {
          qreal _spatium = gscore->spatium();
    //      qreal mag      = PALETTE_SPATIUM * extraMag * guiScaling / _spatium;
          qreal mag      = PALETTE_SPATIUM * extraMag / _spatium;
          gscore->setSpatium(SPATIUM20);

          QPainter p(this);
          p.setRenderHint(QPainter::Antialiasing, true);

          QColor bgColor(0xf6, 0xf0, 0xda);
          if (preferences.fgUseColor)
                bgColor = preferences.fgColor;
    #if 1
          p.setBrush(bgColor);
          p.drawRoundedRect(0, 0, width(), height(), 2, 2);
    #else
          p.fillRect(event->rect(), QColor(0xf6, 0xf0, 0xda));
    #endif
          //
          // draw grid
          //
          if (columns() == 0)
                return;
          int rightBorder = width() % hgrid;
          int hhgrid = hgrid + (rightBorder / columns());

          if (_drawGrid) {
                p.setPen(Qt::gray);
                for (int row = 1; row < rows(); ++row) {
                      int x2 = row < rows()-1 ? columns() * hhgrid : width();
                      int y  = row * vgrid;
                      p.drawLine(0, y, x2, y);
                      }
                for (int column = 1; column < columns(); ++column) {
                      int x = hhgrid * column;
                      p.drawLine(x, 0, x, rows() * vgrid);
                      }
                }

          qreal dy = lrint(2 * PALETTE_SPATIUM * extraMag);

          //
          // draw symbols
          //

          // QPen pen(palette().color(QPalette::Normal, QPalette::Text));
          QPen pen(Qt::black);
          pen.setWidthF(MScore::defaultStyle().value(StyleIdx::staffLineWidth).toDouble() * PALETTE_SPATIUM * extraMag);

          for (int idx = 0; idx < ccp()->size(); ++idx) {
                int yoffset  = gscore->spatium() * _yOffset;
                QRect r      = idxRect(idx);
                QRect rShift = r.translated(0, yoffset);
                p.setPen(pen);
                QColor c(MScore::selectColor[0]);
                if (idx == selectedIdx) {
                      c.setAlpha(100);
                      p.fillRect(r, c);
                      }
                else if (idx == currentIdx) {
                      c.setAlpha(50);
                      p.fillRect(r, c);
                      }
                if (ccp()->at(idx) == 0)
                      continue;
                PaletteCell* cc = ccp()->at(idx);      // current cell

                QString tag = cc->tag;
                if (!tag.isEmpty()) {
                      p.setPen(Qt::darkGray);
                      QFont f(p.font());
                      f.setPointSize(12);
                      p.setFont(f);
                      if (tag == "ShowMore")
                            p.drawText(idxRect(idx), Qt::AlignCenter, "???");
                      else
                            p.drawText(rShift, Qt::AlignLeft | Qt::AlignTop, tag);
                      }

                p.setPen(pen);

                Element* el = cc->element;
                if (el == 0)
                      continue;
                bool drawStaff = cc->drawStaff;
                int row    = idx / columns();
                int column = idx % columns();

                qreal cellMag = cc->mag * mag;
                if (el->isIcon()) {
                      toIcon(el)->setExtent((hhgrid < vgrid ? hhgrid : vgrid) - 4);
                      cellMag = 1.0;
                      }
                el->layout();

                if (drawStaff) {
                      qreal y = r.y() + vgrid * .5 - dy + _yOffset * _spatium * cellMag;
                      qreal x = r.x() + 3;
                      qreal w = hhgrid - 6;
                      for (int i = 0; i < 5; ++i) {
                            qreal yy = y + PALETTE_SPATIUM * i * extraMag;
                            p.drawLine(QLineF(x, yy, x + w, yy));
                            }
                      }
                p.save();
                p.scale(cellMag, cellMag);

                double gw = hhgrid / cellMag;
                double gh = vgrid / cellMag;
                double gx = column * gw + cc->xoffset * _spatium;
                double gy = row    * gh + cc->yoffset * _spatium;

                double sw = el->width();
                double sh = el->height();
                double sy;

                if (drawStaff)
                      sy = gy + gh * .5 - 2.0 * _spatium;
                else
                      sy  = gy + (gh - sh) * .5 - el->bbox().y();
                double sx  = gx + (gw - sw) * .5 - el->bbox().x();

                sy += _yOffset * _spatium;

                p.translate(sx, sy);
                cc->x = sx;
                cc->y = sy;

                QColor color;
                if (idx != selectedIdx) {
                      // show voice colors for notes
                      if (el->isChord())
                            color = el->curColor();
                      else
                            color = palette().color(QPalette::Normal, QPalette::Text);
                      }
                else
                      color = palette().color(QPalette::Normal, QPalette::HighlightedText);

                p.setPen(QPen(color));
                el->scanElements(&p, paintPaletteElement);
                p.restore();
                }
          }

    //---------------------------------------------------------
    //   pixmap
    //---------------------------------------------------------

    QPixmap Palette::pixmap(int paletteIdx) const
          {
          qreal _spatium = gscore->spatium();
          qreal mag      = PALETTE_SPATIUM * extraMag / _spatium;
          PaletteCell* c = cellAt(paletteIdx);
          if (!c || !c->element)
                return QPixmap();
          qreal cellMag = c->mag * mag;
          Element* e = c->element;
          e->layout();
          QRectF r = e->bbox();
          int w    = lrint(r.width()  * cellMag);
          int h    = lrint(r.height() * cellMag);

          if (w * h == 0) {
                qDebug("zero pixmap %d %d %s", w, h, e->name());
                return QPixmap();
                }

          QPixmap pm(w, h);
          pm.fill(Qt::transparent);
          QPainter p(&pm);
          p.setRenderHint(QPainter::Antialiasing, true);

          if (e->isIcon())
                toIcon(e)->setExtent(w < h ? w : h);
          p.scale(cellMag, cellMag);

          QPointF pos = e->ipos();
          e->setPos(-r.topLeft());

          QColor color;
           // show voice colors for notes
          if (e->isChord()) {
                 Chord* chord = toChord(e);
                 for (Note* n : chord->notes())
                       n->setSelected(true);
                 color = e->curColor();
                 }
           else
                 color = palette().color(QPalette::Normal, QPalette::Text);

          p.setPen(QPen(color));
          e->scanElements(&p, paintPaletteElement);

          e->setPos(pos);
          return pm;
          }

    //---------------------------------------------------------
    //   event
    //---------------------------------------------------------

    bool Palette::event(QEvent* ev)
          {
          PaletteBox* pb = mscore->getPaletteBox();
          // disable mouse hover when keyboard navigation is enabled
          if (filterActive && pb->getKeyboardNavigation() && (ev->type() == QEvent::MouseMove || ev->type() == QEvent::ToolTip
              || ev->type() == QEvent::WindowDeactivate)) {
                return true;
                }
          else if (columns() && (ev->type() == QEvent::ToolTip)) {
                int rightBorder = width() % hgrid;
                int hhgrid = hgrid + (rightBorder / columns());
                QHelpEvent* he = (QHelpEvent*)ev;
                int x = he->pos().x();
                int y = he->pos().y();

                int row = y / vgrid;
                int col = x / hhgrid;

                if (row < 0 || row >= rows())
                      return false;
                if (col < 0 || col >= columns())
                      return false;
                int idx = row * columns() + col;
                if (idx >= cells.size())
                      return false;
                if (cellAt(idx) == 0)
                      return false;
                QToolTip::showText(he->globalPos(),
                   qApp->translate("Palette", cellAt(idx)->name.toUtf8()), this);
                return false;
                }
          return QWidget::event(ev);
          }

    //---------------------------------------------------------
    //   write
    //---------------------------------------------------------

    void Palette::write(XmlWriter& xml) const
          {
          xml.stag(QString("Palette name=\"%1\"").arg(XmlWriter::xmlString(_name)));
          xml.tag("gridWidth", hgrid);
          xml.tag("gridHeight", vgrid);
          xml.tag("mag", extraMag);
          if (_drawGrid)
                xml.tag("grid", _drawGrid);

          xml.tag("moreElements", _moreElements);
          if (_yOffset != 0.0)
                xml.tag("yoffset", _yOffset);

          int n = cells.size();
          for (int i = 0; i < n; ++i) {
                if (cells[i] && cells[i]->tag == "ShowMore")
                      continue;
                if (cells[i] == 0 || cells[i]->element == 0) {
                      xml.tagE("Cell");
                      continue;
                      }
                if (!cells[i]->name.isEmpty())
                      xml.stag(QString("Cell name=\"%1\"").arg(XmlWriter::xmlString(cells[i]->name)));
                else
                      xml.stag("Cell");
                if (cells[i]->drawStaff)
                      xml.tag("staff", cells[i]->drawStaff);
                if (cells[i]->xoffset)
                      xml.tag("xoffset", cells[i]->xoffset);
                if (cells[i]->yoffset)
                      xml.tag("yoffset", cells[i]->yoffset);
                if (!cells[i]->tag.isEmpty())
                      xml.tag("tag", cells[i]->tag);
                if (cells[i]->mag != 1.0)
                      xml.tag("mag", cells[i]->mag);
                cells[i]->element->write(xml);
                xml.etag();
                }
          xml.etag();
          }

    //---------------------------------------------------------
    //   read
    //    return false on error
    //---------------------------------------------------------

    bool Palette::read(QFile* qf)
          {
          XmlReader e(gscore, qf);
          while (e.readNextStartElement()) {
                if (e.name() == "museScore") {
                      QString version = e.attribute("version");
                      QStringList sl = version.split('.');
                      int versionId = sl[0].toInt() * 100 + sl[1].toInt();
                      gscore->setMscVersion(versionId);

                      while (e.readNextStartElement()) {
                            const QStringRef& tag(e.name());
                            if (tag == "Palette") {
                                  QString name = e.attribute("name");
                                  setName(name);
                                  read(e);
                                  }
                            else
                                  e.unknown();
                            }
                      }
                }
          return true;
          }

    //---------------------------------------------------------
    //   read
    //---------------------------------------------------------

    bool Palette::read(const QString& p)
          {
          QString path(p);
          if (!path.endsWith(".mpal"))
                path += ".mpal";

          MQZipReader f(path);
          if (!f.exists()) {
                qDebug("palette <%s> not found", qPrintable(path));
                return false;
                }
          clear();

          QByteArray ba = f.fileData("META-INF/container.xml");

          XmlReader e(gscore, ba);
          // extract first rootfile
          QString rootfile = "";
          QList<QString> images;
          while (e.readNextStartElement()) {
                if (e.name() != "container") {
                      e.unknown();
                      break;;
                      }
                while (e.readNextStartElement()) {
                      if (e.name() != "rootfiles") {
                            e.unknown();
                            break;
                            }
                      while (e.readNextStartElement()) {
                            const QStringRef& tag(e.name());

                            if (tag == "rootfile") {
                                  if (rootfile.isEmpty())
                                        rootfile = e.attribute("full-path");
                                  e.readNext();
                                  }
                            else if (tag == "file")
                                  images.append(e.readElementText());
                            else
                                  e.unknown();
                            }
                      }
                }
          //
          // load images
          //
          foreach(const QString& s, images)
                imageStore.add(s, f.fileData(s));

          if (rootfile.isEmpty()) {
                qDebug("can't find rootfile in: %s", qPrintable(path));
                return false;
                }

          ba = f.fileData(rootfile);
          e.clear();
          e.addData(ba);
          while (e.readNextStartElement()) {
                if (e.name() == "museScore") {
                      QString version = e.attribute("version");
                      QStringList sl = version.split('.');
                      int versionId = sl[0].toInt() * 100 + sl[1].toInt();
                      gscore->setMscVersion(versionId);

                      while (e.readNextStartElement()) {
                            if (e.name() == "Palette") {
                                  QString name = e.attribute("name");
                                  setName(name);
                                  read(e);
                                  }
                            else
                                  e.unknown();
                            }
                      }
                else
                      e.unknown();
                }
          return true;
          }

    //---------------------------------------------------------
    //   writeFailed
    //---------------------------------------------------------

    static void writeFailed(const QString& path)
          {
          QString s = qApp->translate("Palette", "Writing Palette File\n%1\nfailed: ");
          QMessageBox::critical(mscore, qApp->translate("Palette", "Writing Palette File"), s.arg(path));
          }

    //---------------------------------------------------------
    //   write
    //    write as compressed zip file and include
    //    images as needed
    //---------------------------------------------------------

    void Palette::write(const QString& p)
          {
          QSet<ImageStoreItem*> images;
          int n = cells.size();
          for (int i = 0; i < n; ++i) {
                if (cells[i] == 0 || cells[i]->element == 0 || cells[i]->element->type() != ElementType::IMAGE)
                      continue;
                images.insert(static_cast<Image*>(cells[i]->element)->storeItem());
                }

          QString path(p);
          if (!path.endsWith(".mpal"))
                path += ".mpal";

          MQZipWriter f(path);
          // f.setCompressionPolicy(QZipWriter::NeverCompress);
          f.setCreationPermissions(
             QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
             | QFile::ReadUser | QFile::WriteUser | QFile::ExeUser
             | QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup
             | QFile::ReadOther | QFile::WriteOther | QFile::ExeOther);

          if (f.status() != MQZipWriter::NoError) {
                writeFailed(path);
                return;
                }
          QBuffer cbuf;
          cbuf.open(QIODevice::ReadWrite);
          XmlWriter xml(gscore, &cbuf);
          xml.header();
          xml.stag("container");
          xml.stag("rootfiles");
          xml.stag(QString("rootfile full-path=\"%1\"").arg(XmlWriter::xmlString("palette.xml")));
          xml.etag();
          foreach (ImageStoreItem* ip, images) {
                QString path = QString("Pictures/") + ip->hashName();
                xml.tag("file", path);
                }
          xml.etag();
          xml.etag();
          cbuf.seek(0);
          //f.addDirectory("META-INF");
          //f.addDirectory("Pictures");
          f.addFile("META-INF/container.xml", cbuf.data());

          // save images
          foreach(ImageStoreItem* ip, images) {
                QString path = QString("Pictures/") + ip->hashName();
                f.addFile(path, ip->buffer());
                }
          {
          QBuffer cbuf;
          cbuf.open(QIODevice::ReadWrite);
          XmlWriter xml(gscore, &cbuf);
          xml.header();
          xml.stag("museScore version=\"" MSC_VERSION "\"");
          write(xml);
          xml.etag();
          cbuf.close();
          f.addFile("palette.xml", cbuf.data());
          }
          f.close();
          if (f.status() != MQZipWriter::NoError)
                writeFailed(path);
          }

    //---------------------------------------------------------
    //   read
    //---------------------------------------------------------

    void Palette::read(XmlReader& e)
          {
          while (e.readNextStartElement()) {
                const QStringRef& t(e.name());
                if (t == "gridWidth")
                      hgrid = e.readDouble();
                else if (t == "gridHeight")
                      vgrid = e.readDouble();
                else if (t == "mag")
                      extraMag = e.readDouble();
                else if (t == "grid")
                      _drawGrid = e.readInt();
                else if (t == "moreElements")
                      setMoreElements(e.readInt());
                else if (t == "yoffset")
                      _yOffset = e.readDouble();
                else if (t == "drumPalette")      // obsolete
                      e.skipCurrentElement();
                else if (t == "Cell") {
                      PaletteCell* cell = new PaletteCell;
                      cell->name = e.attribute("name");
                      bool add = true;
                      while (e.readNextStartElement()) {
                            const QStringRef& t(e.name());
                            if (t == "staff")
                                  cell->drawStaff = e.readInt();
                            else if (t == "xoffset")
                                  cell->xoffset = e.readDouble();
                            else if (t == "yoffset")
                                  cell->yoffset = e.readDouble();
                            else if (t == "mag")
                                  cell->mag = e.readDouble();
                            else if (t == "tag")
                                  cell->tag = e.readElementText();
                            else {
                                  cell->element = Element::name2Element(t, gscore);
                                  if (cell->element == 0) {
                                        e.unknown();
                                        delete cell;
                                        return;
                                        }
                                  else {
                                        cell->element->read(e);
                                        if (cell->element->type() == ElementType::ICON) {
                                              Icon* icon = static_cast<Icon*>(cell->element);
                                              QAction* ac = getAction(icon->action());
                                              if (ac) {
                                                    QIcon qicon(ac->icon());
                                                    icon->setAction(icon->action(), qicon);
                                                    }
                                              else {
                                                    add = false; // action is not valid, don't add it to the palette.
                                                    }
                                              }
                                        }
                                  }
                            }
                      if (add) {
                            int idx = _moreElements ? cells.size() - 1 : cells.size();
                            cells.insert(idx, cell);
                            }
                      }
                else
                      e.unknown();
                }
                // make sure hgrid and vgrid are not 0, we divide by them later
                if (hgrid <= 0)
                      hgrid = 28;
                if (vgrid <= 0)
                      vgrid = 28;
          }

    //---------------------------------------------------------
    //   clear
    //---------------------------------------------------------

    void Palette::clear()
          {
          qDeleteAll(cells);
          cells.clear();
          }

    //---------------------------------------------------------
    //   rows
    //---------------------------------------------------------

    int Palette::rows() const
          {
          int c = columns();
          if (c == 0)
                return 0;
          return (size() + c - 1) / c;
          }

    //---------------------------------------------------------
    //   heightForWidth
    //---------------------------------------------------------

    int Palette::heightForWidth(int w) const
          {
          int c = w / hgrid;
          if (c <= 0)
                c = 1;
          int s = size();
          if (_moreElements)
                s += 1;
          int rows = (s + c - 1) / c;
          if (rows <= 0)
                rows = 1;
          qreal mag = PALETTE_SPATIUM * extraMag;
          int h = lrint(_yOffset * 2 * mag);
          return rows * vgrid + h;
          }

    //---------------------------------------------------------
    //   sizeHint
    //---------------------------------------------------------

    QSize Palette::sizeHint() const
          {
          int h = heightForWidth(width());
          return QSize((width() / hgrid) * hgrid, h);
          }

    //---------------------------------------------------------
    //   actionToggled
    //---------------------------------------------------------

    void Palette::actionToggled(bool /*val*/)
          {
          selectedIdx = -1;
          int nn = ccp()->size();
          for (int n = 0; n < nn; ++n) {
                Element* e = cellAt(n)->element;
                if (e && e->type() == ElementType::ICON) {
                      QAction* a = getAction(static_cast<Icon*>(e)->action());
                      if (a->isChecked()) {
                            selectedIdx = n;
                            break;
                            }
                      }
                }
          update();
          }

    //---------------------------------------------------------
    //   PaletteProperties
    //---------------------------------------------------------

    PaletteProperties::PaletteProperties(Palette* p, QWidget* parent)
       : QDialog(parent)
          {
          setObjectName("PaletteProperties");
          setupUi(this);
          setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);

          palette = p;

          name->setText(palette->name());
          cellWidth->setValue(palette->gridWidth());
          cellHeight->setValue(palette->gridHeight());
          showGrid->setChecked(palette->drawGrid());
          moreElements->setChecked(palette->moreElements());
          elementOffset->setValue(palette->yOffset());
          mag->setValue(palette->mag());

          MuseScore::restoreGeometry(this);
          }

    //---------------------------------------------------------
    //   accept
    //---------------------------------------------------------

    void PaletteProperties::accept()
          {
          palette->setName(name->text());
          palette->setGrid(cellWidth->value(), cellHeight->value());
          palette->setDrawGrid(showGrid->isChecked());
          palette->setMoreElements(moreElements->isChecked());
          palette->setYOffset(elementOffset->value());
          palette->setMag(mag->value());
          palette->emitChanged();
          QDialog::accept();
          }

    //---------------------------------------------------------
    //   hideEvent
    //---------------------------------------------------------

    void PaletteProperties::hideEvent(QHideEvent* event)
          {
          MuseScore::saveGeometry(this);
          QWidget::hideEvent(event);
          }

    //---------------------------------------------------------
    //   PaletteCellProperties
    //---------------------------------------------------------

    PaletteCellProperties::PaletteCellProperties(PaletteCell* p, QWidget* parent)
       : QDialog(parent)
          {
          setObjectName("PaletteCellProperties");
          setupUi(this);
          setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);

          cell = p;

          xoffset->setValue(cell->xoffset);
          yoffset->setValue(cell->yoffset);
          scale->setValue(cell->mag);
          drawStaff->setChecked(cell->drawStaff);
          name->setText(p->name);

          MuseScore::restoreGeometry(this);
          }

    //---------------------------------------------------------
    //   accept
    //---------------------------------------------------------

    void PaletteCellProperties::accept()
          {
          cell->xoffset = xoffset->value();
          cell->yoffset = yoffset->value();
          cell->mag     = scale->value();
          cell->name    = name->text();
          cell->drawStaff = drawStaff->isChecked();
          QDialog::accept();
          }

    //---------------------------------------------------------
    //   hideEvent
    //---------------------------------------------------------

    void PaletteCellProperties::hideEvent(QHideEvent* event)
          {
          MuseScore::saveGeometry(this);
          QWidget::hideEvent(event);
          }

    //---------------------------------------------------------
    // PaletteScrollArea
    //---------------------------------------------------------

    PaletteScrollArea::PaletteScrollArea(Palette* w, QWidget* parent)
       : QScrollArea(parent)
          {
          setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
          setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
          setWidget(w);
          setWidgetResizable(false);
          _restrictHeight = true;
          setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
          }

    //---------------------------------------------------------
    // resizeEvent
    //---------------------------------------------------------

    void PaletteScrollArea::resizeEvent(QResizeEvent* re)
          {
          QScrollArea::resizeEvent(re); // necessary?

          Palette* palette = static_cast<Palette*>(widget());
          int h = palette->heightForWidth(width());
          palette->resize(QSize(width() - 6, h));
          if (_restrictHeight) {
                setMaximumHeight(h+6);
                }
          }

    //---------------------------------------------------------
    //   dragEnterEvent
    //---------------------------------------------------------

    void Palette::dragEnterEvent(QDragEnterEvent* event)
          {
          const QMimeData* data = event->mimeData();
          if (data->hasUrls()) {
                QList<QUrl>ul = event->mimeData()->urls();
                QUrl u = ul.front();
                if (MScore::debugMode) {
                      qDebug("dragEnterEvent: Url: %s", qPrintable(u.toString()));
                      qDebug("   scheme <%s> path <%s>", qPrintable(u.scheme()), qPrintable(u.path()));
                      }
                if (u.scheme() == "file") {
                      QFileInfo fi(u.path());
                      QString suffix(fi.suffix().toLower());
                      if (suffix == "svg"
                         || suffix == "jpg"
                         || suffix == "jpeg"
                         || suffix == "png"
                         ) {
                            event->acceptProposedAction();
                            }
                      }
                }
          else if (data->hasFormat(mimeSymbolFormat)) {
                event->accept();
                update();
                }
          else {
                event->ignore();
    #ifndef NDEBUG
                qDebug("dragEnterEvent: formats:");
                for (const QString& s : event->mimeData()->formats())
                      qDebug("   %s", qPrintable(s));
    #endif
                }
          }

    //---------------------------------------------------------
    //   dragMoveEvent
    //---------------------------------------------------------

    void Palette::dragMoveEvent(QDragMoveEvent* event)
          {
          int i = idx(event->pos());
          if (event->source() == this) {
                if (i != -1) {
                      if (currentIdx != -1 && event->proposedAction() == Qt::MoveAction) {
                            if (i != currentIdx) {
                                  PaletteCell* c = cells.takeAt(currentIdx);
                                  cells.insert(i, c);
                                  currentIdx = i;
                                  update();
                                  }
                            event->accept();
                            return;
                            }
                      }
                event->ignore();
                }
          else
                event->accept();
          }

    //---------------------------------------------------------
    //   dropEvent
    //---------------------------------------------------------

    void Palette::dropEvent(QDropEvent* event)
          {
          Element* e = 0;
          QString name;

          const QMimeData* data = event->mimeData();
          if (data->hasUrls()) {
                QList<QUrl>ul = event->mimeData()->urls();
                QUrl u = ul.front();
                if (u.scheme() == "file") {
                      QFileInfo fi(u.path());
                      Image* s = new Image(gscore);
                      QString filePath(u.toLocalFile());
                      s->load(filePath);
                      e = s;
                      QFileInfo f(filePath);
                      name = f.completeBaseName();
                      }
                }
          else if (data->hasFormat(mimeSymbolFormat)) {
                QByteArray data(event->mimeData()->data(mimeSymbolFormat));
                XmlReader xml(gscore, data);
                QPointF dragOffset;
                Fraction duration;
                ElementType type = Element::readType(xml, &dragOffset, &duration);

                if (type == ElementType::SYMBOL) {
                      Symbol* symbol = new Symbol(gscore);
                      symbol->read(xml);
                      e = symbol;
                      }
                else {
                      e = Element::create(type, gscore);
                      if (e) {
                            e->read(xml);
                            e->setTrack(0);
                            if (e->isIcon()) {
                                  Icon* i = toIcon(e);
                                  const QByteArray& action = i->action();
                                  if (!action.isEmpty()) {
                                        const Shortcut* s = Shortcut::getShortcut(action);
                                        if (s) {
                                              QAction* a = s->action();
                                              QIcon icon(a->icon());
                                              i->setAction(action, icon);
                                              }
                                        }
                                  }
                            }
                      }
                }
          if (e == 0) {
                event->ignore();
                return;
                }
          if (event->source() == this) {
                delete e;
                if (event->proposedAction() == Qt::MoveAction) {
                      event->accept();
                      emit changed();
                      return;
                      }
                event->ignore();
                return;
                }
          e->setSelected(false);
          int i = idx(event->pos());
          if (i == -1 || cells[i])
                append(e, name);
          else
                add(i, e, name);
          event->accept();
          while (!cells.isEmpty() && cells.back() == 0)
                cells.removeLast();
          setFixedHeight(heightForWidth(width()));
          updateGeometry();
          update();
          emit changed();
          }

    }

↑ V595 The 'moreAction' pointer was utilized before it was verified against nullptr. Check lines: 251, 273.