Skip to content
Snippets Groups Projects
tmainscore.cpp 33.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • SeeLook's avatar
    SeeLook committed
    /***************************************************************************
    
     *   Copyright (C) 2013-2016 by Tomasz Bojczuk                             *
    
    SeeLook's avatar
    SeeLook committed
     *   tomaszbojczuk@gmail.com                                               *
     *                                                                         *
     *   This program is free software; you can redistribute it and/or modify  *
     *   it under the terms of the GNU General Public License as published by  *
     *   the Free Software Foundation; either version 3 of the License, or     *
     *   (at your option) any later version.                                   *
     *                                                                         *
     *   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, see <http://www.gnu.org/licenses/>.  *
     ***************************************************************************/
    
    #include "tmainscore.h"
    
    #include "tcornerproxy.h"
    
    SeeLook's avatar
    SeeLook committed
    #include <score/tscorestaff.h>
    #include <score/tscorenote.h>
    #include <score/tscorekeysignature.h>
    #include <score/tscoreclef.h>
    #include <score/tscorescene.h>
    
    #include <score/tnotecontrol.h>
    
    SeeLook's avatar
    SeeLook committed
    #include <music/ttune.h>
    
    #include <tscoreparams.h>
    
    SeeLook's avatar
    SeeLook committed
    #include <graphics/tgraphicstexttip.h>
    
    SeeLook's avatar
    SeeLook committed
    #include <animations/tstrikedoutitem.h>
    #include <animations/tblinkingitem.h>
    #include <tcolor.h>
    
    #include <notename/tnotename.h>
    
    SeeLook's avatar
    SeeLook committed
    #include <QtWidgets>
    
    SeeLook's avatar
    SeeLook committed
    
    
    
    #define SENDER_TO_STAFF static_cast<TscoreStaff*>(sender())
    
    SeeLook's avatar
    SeeLook committed
    
    
    TmainScore::TmainScore(QMainWindow* mw, QWidget* parent) :
    
    SeeLook's avatar
    SeeLook committed
    	m_questMark(0),
    	m_questKey(0),
    	m_strikeOut(0),
    	m_bliking(0), m_keyBlinking(0),
    
    	m_corrStyle(Tnote::defaultStyle),
    
      m_scoreIsPlayed(false),
      m_emitExpertNoteClicked(true)
    
    SeeLook's avatar
    SeeLook committed
    {
    
    	scoreScene()->setNameColor(Tcore::gl()->S->nameColor);
    
    	addStaff(staff());
    
    SeeLook's avatar
    SeeLook committed
    // set preferred clef
    
    SeeLook's avatar
    SeeLook committed
    // set note colors
    
    SeeLook's avatar
    SeeLook committed
    	setScordature();
    
    	setAnimationsEnabled(Tcore::gl()->useAnimations);
    
      enableAccidToKeyAnim(Tcore::gl()->useAnimations);
    
    	setEnabledDblAccid(Tcore::gl()->S->doubleAccidentalsEnabled);
    	setEnableKeySign(Tcore::gl()->S->keySignatureEnabled);
    
      setScoreScale(Tcore::gl()->S->scoreScale);
    
    		staff()->scoreKey()->showKeyName(Tcore::gl()->S->showKeySignName);
    
    SeeLook's avatar
    SeeLook committed
    	
    
    	connect(scoreScene()->right(), SIGNAL(nameMenu(TscoreNote*)), SLOT(showNameMenu(TscoreNote*)));
    
    //     setAmbitus(Tnote(Tcore::gl()->loString().chromatic()-1),
    //                Tnote(Tcore::gl()->hiString().chromatic()+Tcore::gl()->GfretsNumber+1));
    
    SeeLook's avatar
    SeeLook committed
    
    
    SeeLook's avatar
    SeeLook committed
    	isExamExecuting(false);
    
      setNote(0, Tnote()); // To display fake empty note properly
    
      viewport()->setStyleSheet(QStringLiteral("border-radius: 10px;"));
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    TmainScore::~TmainScore()
    {}
    
    
    //####################################################################################################
    //########################################## PUBLIC ##################################################
    //####################################################################################################
    
    void TmainScore::setEnableEnharmNotes(bool isEnabled) {
    
    			staff()->noteSegment(1)->setColor(Tcore::gl()->S->enharmNotesColor);
    			staff()->noteSegment(2)->setColor(Tcore::gl()->S->enharmNotesColor);	
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    void TmainScore::acceptSettings() {
    
    	bool refreshNoteNames = false; // This is CPU consuming operation - we try to avoid it
    // Update note name style
    
    	if  (Tnote::defaultStyle != Tcore::gl()->S->nameStyleInNoteName) {
    		Tnote::defaultStyle = Tcore::gl()->S->nameStyleInNoteName;
    
    	if ((Tcore::gl()->S->isSingleNoteMode && insertMode() != e_single) || (!Tcore::gl()->S->isSingleNoteMode && insertMode() == e_single))
    
    		refreshNoteNames = true;
    
    	if (Tcore::gl()->S->isSingleNoteMode) {
    
    		setInsertMode(e_single);
    
        setEnableEnharmNotes(Tcore::gl()->S->showEnharmNotes);
    
    	} else
    		setInsertMode(e_multi);
    
    	setEnabledDblAccid(Tcore::gl()->S->doubleAccidentalsEnabled);
    
    	scoreScene()->left()->addAccidentals();
    
    	setClef(Tclef(Tcore::gl()->S->clef));
    
    	if ((bool)staff()->scoreKey() != Tcore::gl()->S->keySignatureEnabled) {
    		setEnableKeySign(Tcore::gl()->S->keySignatureEnabled);
    
    		staff()->scoreKey()->showKeyName(Tcore::gl()->S->showKeySignName);
    
    	if (Tcore::gl()->S->nameColor != scoreScene()->nameColor()) {
    
    			scoreScene()->setNameColor(Tcore::gl()->S->nameColor);
    
    	if (Tcore::gl()->S->namesOnScore != m_acts->noteNames()->isChecked() || refreshNoteNames) {
    		m_acts->noteNames()->setChecked(Tcore::gl()->S->namesOnScore);
    
    		for (int st = 0; st < staffCount(); st++) {
    			for (int no = 0; no < staves(st)->count(); no++) {
    
    				if (m_acts->noteNames()->isChecked()) {
    
    					if (refreshNoteNames) // remove name to pain it with new highlight
    
    							staves(st)->noteSegment(no)->removeNoteName();
    					staves(st)->noteSegment(no)->showNoteName();
    
    					staves(st)->noteSegment(no)->removeNoteName();
    
    	if (Tcore::gl()->instrument == e_classicalGuitar || Tcore::gl()->instrument == e_electricGuitar)
    
    SeeLook's avatar
    SeeLook committed
    			setScordature();
    	else
    
    SeeLook's avatar
    SeeLook committed
    				staff()->removeScordatute();
    
    	if (Tcore::gl()->S->keySignatureEnabled) // refreshKeySignNameStyle();
    
    SeeLook's avatar
    SeeLook committed
    		if (staff()->scoreKey())
    
    			staff()->scoreKey()->showKeyName(Tcore::gl()->S->showKeySignName);
    //     setAmbitus(Tnote(Tcore::gl()->loString().chromatic()-1),
    //                Tnote(Tcore::gl()->hiString().chromatic()+Tcore::gl()->GfretsNumber+1));
    
    SeeLook's avatar
    SeeLook committed
    	enableAccidToKeyAnim(false); // prevent accidental animation to empty note
    
    	if (Tcore::gl()->S->keySignatureEnabled) {
    		TkeySignature::setNameStyle(Tcore::gl()->S->nameStyleInKeySign, Tcore::gl()->S->majKeyNameSufix, Tcore::gl()->S->minKeyNameSufix);
    
    SeeLook's avatar
    SeeLook committed
    		setKeySignature(keySignature());
    
    SeeLook's avatar
    SeeLook committed
    	enableAccidToKeyAnim(true);
    
    	if (m_nameMenu) {
    
    			m_nameMenu->setEnabledDblAccid(Tcore::gl()->S->doubleAccidentalsEnabled);
    			m_nameMenu->setEnabledEnharmNotes(Tcore::gl()->S->showEnharmNotes);
    			m_nameMenu->setNoteNamesOnButt(Tcore::gl()->S->nameStyleInNoteName);
    			m_nameMenu->setStyle(Tcore::gl()->S->nameStyleInNoteName);
    // 			m_nameMenu->setAmbitus(Tcore::gl()->loString(), Tnote(Tcore::gl()->hiString().chromatic() + Tcore::gl()->GfretsNumber));
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    
    void TmainScore::setNote(const Tnote& note) {
    
    	if (insertMode() == e_single && !isExam()) {
        blockSignals(true);
        whenNoteWasChanged(0, note);
        blockSignals(false);
      }
    
    void TmainScore::setMelody(Tmelody* mel) {
    	bool animEnabled = ainmationsEnabled();
    
      bool accidAnimEnabled = isAccidToKeyAnimEnabled();
    
    	setClef(Tclef(mel->clef()));
    	if (staff()->scoreKey())
    			setKeySignature(mel->key());
    
    	for (int i = 0; i < mel->length(); ++i) {
    		if (i > notesCount() - 1) {
    
    			staves(i / staff()->maxNoteCount())->addNote(mel->note(i)->p(), isScoreDisabled());
    
    	if (mel->length() < notesCount()) { // remove automatically added empty note(s)
    		for (int i = 0; i < notesCount() - mel->length(); ++i)
    
    			lastStaff()->removeNote(lastStaff()->count() - 1);
    	}
    
    	enableAccidToKeyAnim(accidAnimEnabled);
    
    }
    
    
    void TmainScore::getMelody(Tmelody* mel, const QString& title) {
    	mel->setTitle(title);
    
    	mel->setTempo(Tcore::gl()->S->tempo);
    
    	for (int i = 0; i < notesCount(); ++i) {
    
    		Tchunk n(getNote(i), Trhythm(Trhythm::e_none));
    
        if (i == notesCount() - 1 && !getNote(i).isValid())
          continue; // skip last note when empty - it avoids adding temporary last note to exam melody
    
    void TmainScore::setInsertMode(TmainScore::EinMode mode) {
    
    		bool ignoreThat = false;
    		if ((mode == e_record && insertMode() == e_multi) || (mode == e_multi && insertMode() == e_record))
    			ignoreThat = true;
    
    		TmultiScore::setInsertMode(mode);
    
            m_delCorner->hideSpot();
    
            m_acts->mainAction()->setVisible(false);
    
    				staff()->noteSegment(0)->removeNoteName();
    
    				m_currentNameSegment = staff()->noteSegment(0);
    
    				enableCorners(false);
    				m_nameMenu->show();
    
            setEnableEnharmNotes(Tcore::gl()->S->showEnharmNotes);
    
            m_acts->mainAction()->setVisible(true);
    
    				m_nameMenu->enableArrows(true);
    				m_nameMenu->hide();
    				enableCorners(true);
    
    					staff()->noteSegment(0)->showNoteName();
    
    void TmainScore::noteWasClickedMain(int index) {
    
    	if (!isExam() && insertMode() == e_single)
    		m_nameMenu->setNoteName(*(st->getNote(index)));
    
    void TmainScore::playScore() {
    	if (m_scoreIsPlayed) {
    		m_scoreIsPlayed = false;
    		if (m_playTimer) {
    			m_playTimer->stop();
    			delete m_playTimer;
    		}
    	} else {
    
    		m_scoreIsPlayed = true;
    		m_playTimer = new QTimer(this);
    
        m_playTimer->setTimerType(Qt::PreciseTimer);
    
    		connect(m_playTimer, SIGNAL(timeout()), this, SLOT(playSlot()));
    
    		m_playedIndex = currentIndex() - 1;
    
    	if (staff()->hasScordature())
    			performScordatureSet();
    
    SeeLook's avatar
    SeeLook committed
    void TmainScore::setScordature() {
    
    	if (Tcore::gl()->instrument == e_classicalGuitar || Tcore::gl()->instrument == e_electricGuitar) {
    
    SeeLook's avatar
    SeeLook committed
    			performScordatureSet();
    	}
    }
    
    
    SeeLook's avatar
    SeeLook committed
    void TmainScore::unLockScore() {
    	setScoreDisabled(false);
    
    		setNoteDisabled(1, true);
    		setNoteDisabled(2, true);
    	}
    
    		setBGcolor(Tcolor::merge(Tcore::gl()->EanswerColor, mainWindow()->palette().window().color()));
    
    			setNoteViewBg(0, Tcore::gl()->EanswerColor);
    
      setClefDisabled(true); // setScoreDisabled() unlock it
    
    	QPointF nPos = staff()->noteSegment(0)->mapFromScene(mapToScene(mapFromParent(mapFromGlobal(cursor().pos()))));
    
    SeeLook's avatar
    SeeLook committed
    	if (nPos.x() > 0.0 && nPos.x() < 7.0) {
    		staff()->noteSegment(0)->moveWorkNote(nPos);
    	}
    }
    
    SeeLook's avatar
    SeeLook committed
    
    
    void TmainScore::setScoreDisabled(bool disabled) {
    	TmultiScore::setScoreDisabled(disabled);
    
      if (insertMode() != e_single) {
        m_delCorner->setVisible(!disabled);
        m_acts->clearScore()->blockSignals(disabled);
        m_acts->clearScore()->setVisible(!disabled);
        m_acts->deleteCurrentNote()->setVisible(!disabled);
        m_acts->deleteCurrentNote()->blockSignals(disabled);
      }
    
    }
    
    /** When Key signature is enabled (exists) it is disabled/enabled by @p setScoreDisabled().
     * This method gives independent control on it. */
    void TmainScore::lockKeySignature(bool lock) {
      if (staff()->scoreKey()) {
        for (int i = 0; i < staffCount(); ++i)
          staves(i)->scoreKey()->setReadOnly(lock);
      }
    
    SeeLook's avatar
    SeeLook committed
    QRectF TmainScore::noteRect(int noteNr) {
    
    	return QRectF(0, 0, staff()->noteSegment(noteNr)->mainNote()->rect().width() * transform().m11(), 
    		staff()->noteSegment(noteNr)->mainNote()->rect().height() * transform().m11());
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    QPoint TmainScore::notePos(int noteNr) {
    	QPointF nPos;
    
    	if (staff()->noteSegment(noteNr)->mainNote()->isVisible())
    
    SeeLook's avatar
    SeeLook committed
    		nPos = staff()->noteSegment(noteNr)->mainNote()->mapToScene(staff()->noteSegment(noteNr)->mainNote()->pos());
    
    	QPoint vPos = mapFromScene(staff()->pos().x() + staff()->noteSegment(noteNr)->pos().x() + 
    																				staff()->noteSegment(noteNr)->mainNote()->pos().x(), 
    														 staff()->noteSegment(noteNr)->mainNote()->pos().y());
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    void TmainScore::enableAccidToKeyAnim(bool enable) {
    
      scoreScene()->enableAccidsAnimation(enable);
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    bool TmainScore::isAccidToKeyAnimEnabled() {
    
      return scoreScene()->isAccidAnimated();
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    int TmainScore::widthToHeight(int hi) {
    
    	return qRound((qreal)hi / height()) * width();
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    //####################################################################################################
    //############################## METHODS RELATED TO EXAMS ############################################
    //####################################################################################################
    
    
    SeeLook's avatar
    SeeLook committed
    void TmainScore::isExamExecuting(bool isIt) {
    	if (isIt) {
    			disconnect(this, SIGNAL(noteWasChanged(int,Tnote)), this, SLOT(whenNoteWasChanged(int,Tnote)));
    
    			disconnect(m_nameMenu, SIGNAL(noteNameWasChanged(Tnote)), this, SLOT(menuChangedNote(Tnote)));
    
    SeeLook's avatar
    SeeLook committed
    			connect(this, SIGNAL(noteWasChanged(int,Tnote)), this, SLOT(expertNoteChanged()));
    
    SeeLook's avatar
    SeeLook committed
    			setScoreDisabled(true);
    			setClefDisabled(true);
    
    			m_questMark = new QGraphicsSimpleTextItem();
    			m_questMark->hide();
    			scoreScene()->addItem(m_questMark);
    
          if (insertMode() == e_single) {
    
            staff()->noteSegment(1)->setColor(qApp->palette().text().color()); // it can have color of enharmonic notes
            staff()->noteSegment(2)->setColor(qApp->palette().text().color());
    
    	} else {
    			connect(this, SIGNAL(noteWasChanged(int,Tnote)), this, SLOT(whenNoteWasChanged(int,Tnote)));
    			connect(m_nameMenu, SIGNAL(noteNameWasChanged(Tnote)), this, SLOT(menuChangedNote(Tnote)));
    			disconnect(this, SIGNAL(noteWasChanged(int,Tnote)), this, SLOT(expertNoteChanged()));
    			if (m_questMark) {
    				delete m_questMark;
    				m_questMark = 0;
    			}
    			delete m_questKey;
    			m_questKey = 0;
    			setClefDisabled(false);
    			setNoteNameEnabled(true);
    
          enableAccidToKeyAnim(Tcore::gl()->useAnimations);
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    SeeLook's avatar
    SeeLook committed
    void TmainScore::clearScore() {
    	bool enableAnim = isAccidToKeyAnimEnabled();
    	enableAccidToKeyAnim(false); // prevent animations to empty score
    
    			staff()->noteSegment(i)->removeNoteName();
    
    		staff()->noteSegment(1)->removeString(); // so far string number to remove occurs only on this view
    		staff()->noteSegment(0)->hideWorkNote();
    
    			m_emitExpertNoteClicked = false; // don't emit noteClicked() in expert exam mode
    
    			m_emitExpertNoteClicked = true; // better single bool than blockSignal()
    
    			selectNote(-1);
    			staff()->noteSegment(0)->markNote(-1);
    
    			staff()->noteSegment(0)->removeNoteName();
    
          staff()->noteSegment(0)->removeString();
    
    // 	for (int st = 0; st < staffCount(); st++) {
    // 		for (int no = 0; no < staves(st)->count(); no++)
    // 				staves(st)->noteSegment(no)->removeNoteName();
    // 	}
    
    SeeLook's avatar
    SeeLook committed
    	if (staff()->scoreKey()) {
    			setKeySignature(TkeySignature());
    			if (m_questKey) {
    				delete m_questKey;
    				m_questKey = 0;
    			}
        }
    
    SeeLook's avatar
    SeeLook committed
    	for(int i = 0; i < m_bgRects.size(); i++)
    		delete m_bgRects[i];
    	m_bgRects.clear();
    
    	setBGcolor(mainWindow()->palette().base().color());
    
    SeeLook's avatar
    SeeLook committed
    	enableAccidToKeyAnim(enableAnim);
    }
    
    
    void TmainScore::askQuestion(Tnote note, char realStr) {
    
    	setQuestionMarkPos();
    	setBGcolor(Tcolor::merge(Tcore::gl()->EquestionColor, mainWindow()->palette().window().color()));
    
      staff()->noteSegment(1)->setColor(qApp->palette().text().color()); // Otherwise note is strange light
    
    	if (realStr) 
    		setStringNumber(1, realStr);
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    void TmainScore::askQuestion(Tnote note, TkeySignature key, char realStr) {
    	setKeySignature(key);
    	askQuestion(note, realStr);
    }
    
    SeeLook's avatar
    SeeLook committed
    
    
    void TmainScore::askQuestion(Tmelody* mel) {
    
    	setBGcolor(Tcolor::merge(Tcore::gl()->EquestionColor, mainWindow()->palette().window().color()));
    	setQuestionMarkPos();
    
    SeeLook's avatar
    SeeLook committed
    void TmainScore::expertNoteChanged() {
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    void TmainScore::forceAccidental(Tnote::Ealter accid) {
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    void TmainScore::markAnswered(QColor blurColor, int noteNr) {
    
    	if (noteNr < notesCount()) {
    
    		TscoreNote *sn;
    		if (insertMode() == e_single)
    			sn = staff()->noteSegment(noteNr);
    		else
    			sn = noteFromId(noteNr);
    
    		sn->setEmptyLinesVisible(true);
    
    		if (blurColor == -1)
    
    			sn->markNote(QColor(blurColor.lighter().name()));
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    
    void TmainScore::markQuestion(QColor blurColor, int noteNr) {
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    void TmainScore::prepareKeyToAnswer(TkeySignature fakeKey, QString expectKeyName) {
    
    	setKeySignature(fakeKey);
    	m_questKey = new QGraphicsTextItem();
    	m_questKey->setParentItem(staff()->scoreKey()); // we are sure that key exist - exam checked that
    	m_questKey->setHtml(QString("<span style=\"color: %1;\"><span style=\"font-family: nootka;\">?</span><br>").
    
    				arg(Tcore::gl()->EquestionColor.name()) + expectKeyName + "</span>");
    
    	TgraphicsTextTip::alignCenter(m_questKey);
    	TscoreKeySignature::setKeyNameScale(m_questKey);
    	m_questKey->setPos(
    				(staff()->scoreKey()->boundingRect().width() - m_questKey->boundingRect().width() * m_questKey->scale()) / 2 - 2.5,
    
    				 - 3.0 - m_questKey->boundingRect().height() * m_questKey->scale());
    
    	setKeyViewBg(Tcore::gl()->EanswerColor);
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    void TmainScore::setKeyViewBg(QColor C) {
    	if (staff()->scoreKey()) {
    			createBgRect(C, staff()->scoreKey()->boundingRect().width() + 6.0, 
    
    							QPointF(staff()->scoreKey()->pos().x() - 6.0, 0.0));
    
    SeeLook's avatar
    SeeLook committed
    	}
    }
    
    
    void TmainScore::setNoteViewBg(int id, QColor C) {
    		for (int i = 0; i < m_bgRects.size(); i++) { // check for the same rectangles
    			if (staff()->noteSegment(id)->pos() == m_bgRects[i]->pos()) // the same position
    				if (m_bgRects[i]->brush().color() == C) // the same color
    					return; // means exist - ignore 
    		}
    		createBgRect(C, staff()->noteSegment(id)->boundingRect().width(), staff()->noteSegment(id)->pos());
    }
    
    
    
    void TmainScore::correctNote(Tnote& goodNote, const QColor& color, int noteNr) {
    	if (noteNr >= notesCount()) {
    		qDebug() << "Correction of not existing note" << noteNr;
    		return;
    	}
    	if (m_correctNoteNr != -1) {
    		qDebug() << "Correction in progress";
    		return;
    	}
    	TscoreNote *corrN = noteFromId(noteNr);
    	m_goodNote = goodNote;
    	if (corrN->mainNote()->isVisible())
    			m_strikeOut = new TstrikedOutItem(staff()->noteSegment(noteNr)->mainNote());
    	else {
    		m_strikeOut = new TstrikedOutItem(QRectF(0.0, 0.0, corrN->boundingRect().width() - 3.0, 8.0), corrN);
    		m_strikeOut->setPos((corrN->boundingRect().width() - m_strikeOut->boundingRect().width()) / 2, 
    														(corrN->boundingRect().height() - m_strikeOut->boundingRect().height()) / 2.0);
    	}
    
    	QPen pp(QColor(color.lighter().name()), 0.5);
    
    	m_strikeOut->setPen(pp);
    	m_correctNoteNr = noteNr;
    	connect(m_strikeOut, SIGNAL(strikedFInished()), this, SLOT(strikeBlinkingFinished()));
    	m_strikeOut->startBlinking();
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    
    /** As long as correctAccidental() is used in single mode only 
     * it is sufficient to set m_correctNoteNr = 0 here
     * but number of note will be necessary if melody will want it */
    
    SeeLook's avatar
    SeeLook committed
    void TmainScore::correctAccidental(Tnote& goodNote) {
    
    	m_correctNoteNr = 0;
    	m_goodNote = goodNote;
    
    	QPen pp(QColor(Tcore::gl()->EnotBadColor.name()), 0.5);
    
    	if (getNote(0).alter != m_goodNote.alter) {
    
    			m_bliking = new TblinkingItem(staff()->noteSegment(0)->mainAccid());
    	} else {
    				m_bliking = new TblinkingItem(staff()->noteSegment(0));
    				staff()->noteSegment(0)->mainNote()->setBrush(QBrush(pp.color()));
    	}
    	staff()->noteSegment(0)->mainAccid()->setBrush(QBrush(pp.color()));
    	m_bliking->startBlinking(3);
    	connect(m_bliking, SIGNAL(finished()), this, SLOT(strikeBlinkingFinished()));
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    void TmainScore::correctKeySignature(TkeySignature newKey) {
    
    	if (staff()->scoreKey())
    			m_keyBlinking = new TblinkingItem(staff()->scoreKey());
    	else
    			return;
    	m_goodKey = newKey;
    	connect(m_keyBlinking, SIGNAL(finished()), this, SLOT(keyBlinkingFinished()));
    	m_keyBlinking->startBlinking(2);
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    void TmainScore::showNames(Tnote::EnameStyle st) {
    	Tnote::EnameStyle tmpStyle = Tnote::defaultStyle;
    	Tnote::defaultStyle = st;
    	for (int st = 0; st < staffCount(); st++) {
    
    		for (int no = 0; no < staves(st)->count(); no++)
    			staves(st)->noteSegment(no)->showNoteName(staves(st)->noteSegment(no)->mainNote()->pen().color());
    
    
    void TmainScore::deleteNoteName(int id) {
    
    	if (insertMode() == e_single ) {
    		if (id < staff()->count())
    
    			staff()->noteSegment(id)->removeNoteName();
    
    
    void TmainScore::setBGcolor(const QColor& bgColor) {
      if (m_bgColor != bgColor) {
        m_bgColor = bgColor;
        m_bgColor.setAlpha(230);
      }
    }
    
    
    void TmainScore::paintEvent(QPaintEvent* event) {
      QPainter painter(viewport());
      if (!BG_PIX->isNull() && event->rect().bottomRight().x() >= BG_PIX->globalPos().x()) {
          painter.drawPixmap(BG_PIX->globalPos().x() , BG_PIX->globalPos().y() - y(), *BG_PIX);
      }
      painter.setBrush(m_bgColor);
      painter.setPen(Qt::NoPen);
      if (event->rect().x() <= ROUNDNESS || painter.viewport().width() - (event->rect().x() + event->rect().width()) <= ROUNDNESS)
            // repaint entire background only if it contains margins with roundness
          painter.drawRoundedRect(0, 0, painter.viewport().width(), painter.viewport().height(), 10, 10);
      else // otherwise repaint only fragment
          painter.drawRect(event->rect());
    
      QGraphicsView::paintEvent(event);
    }
    
    
    
    
    SeeLook's avatar
    SeeLook committed
    //####################################################################################################
    //########################################## PUBLIC SLOTS ############################################
    //####################################################################################################
    
    void TmainScore::whenNoteWasChanged(int index, Tnote note) {
    	//We are sure that index is 0, cause others are disabled :-)
    
        if (insertMode() == e_single && Tcore::gl()->S->showEnharmNotes) {
    
          if (note.isValid()) {
    
            TnotesList enharmList = note.getTheSameNotes(Tcore::gl()->S->doubleAccidentalsEnabled);
    
    SeeLook's avatar
    SeeLook committed
            TnotesList::iterator it = enharmList.begin();
            ++it;
            if (it != enharmList.end())
    
                TsimpleScore::setNote(1, *(it));
    
    SeeLook's avatar
    SeeLook committed
            else
                clearNote(1);
    
            if (Tcore::gl()->S->doubleAccidentalsEnabled) {
    
    SeeLook's avatar
    SeeLook committed
                ++it;
                if (it != enharmList.end())
    
                    TsimpleScore::setNote(2, *(it));
    
    SeeLook's avatar
    SeeLook committed
                else
                    clearNote(2);
            }
    
            m_nameMenu->setNoteName(enharmList);
    
          } else
              m_nameMenu->setNoteName(note);
    
    SeeLook's avatar
    SeeLook committed
        }
        emit noteChanged(index, note);
    }
    
    //####################################################################################################
    //########################################## PROTECTED ###############################################
    //####################################################################################################
    
    
    void TmainScore::showNameMenu(TscoreNote* sn) {
    
    	m_nameMenu->setNoteName(*sn->note());
    	m_currentNameSegment = sn;
    	changeCurrentIndex(sn->staff()->number() * staff()->maxNoteCount() + sn->index());
    
    	QPointF notePos = sn->staff()->mapToScene(sn->pos());
    	QPoint mPos = mapFromScene(notePos.x() + 8.0, notePos.y() + 6.0);
    	mPos.setX(x() + mPos.x());
    	mPos.setY(y() + mPos.y());
    
    	m_nameMenu->exec(mPos, transform().m11());
    
    }
    
    
    void TmainScore::menuChangedNote(Tnote n) {
    	if (m_currentNameSegment) {
    		m_currentNameSegment->staff()->setNote(m_currentNameSegment->index(), n);
    
    		m_currentNameSegment->update(); // Menu above covers focus
    
    		emit noteWasChanged(m_currentNameSegment->index(), n);
    
    		if (insertMode() == e_single && Tcore::gl()->S->showEnharmNotes && !isExam()) {
    
    			staff()->setNote(1, m_nameMenu->getNoteName(1));
    			staff()->setNote(2, m_nameMenu->getNoteName(2));
    		}
    
    	for (int st = 0; st < staffCount(); st++) {
    		staves(st)->setExtraAccids(m_acts->extraAccids()->isChecked());
    		for (int no = 0; no < staves(st)->count(); no++) {
    
    			if (staves(st)->getNote(no)->alter == -1 || staves(st)->getNote(no)->alter == 1)
    
    				staves(st)->setNote(no, *staves(st)->getNote(no));
    
    	for (int st = 0; st < staffCount(); st++) {
    		for (int no = 0; no < staves(st)->count(); no++) {
    
    				staves(st)->noteSegment(no)->showNoteName();
    
    				staves(st)->noteSegment(no)->removeNoteName();
    
    	}
    	Tcore::gl()->S->namesOnScore = m_acts->noteNames()->isChecked();
    
    	qreal newScale = Tcore::gl()->S->scoreScale;
    
    			newScale = qMin(Tcore::gl()->S->scoreScale + 0.25, 2.0);
    
    			newScale = qMax(Tcore::gl()->S->scoreScale - 0.25, 1.0);
    
    	if (newScale != Tcore::gl()->S->scoreScale) {
    		Tcore::gl()->S->scoreScale = newScale;
    
        if (m_questMark) {
          m_questMark->setPos(0, 0); // reset position to enable positioning again
          setQuestionMarkPos();
        }
    
    void TmainScore::moveSelectedNote(TmainScore::EmoveNote nDir) {
    
    	if (nDir == e_doNotMove) { // determine action by sender which invoked this slot
    
    			if (sender() == m_acts->firstNote())
    
    			else if (sender() == m_acts->lastNote())
    
    			else if (sender() == m_acts->staffUp())
    
    			else if (sender() == m_acts->staffDown())
    
    			else if (sender() == m_acts->nextNote())
    
    			else if (sender() == m_acts->prevNote())
    
    	}
    	switch(nDir) {
    		case e_first:
    			changeCurrentIndex(0); break;
    		case e_last:
    
    			changeCurrentIndex(notesCount() - 1);
    
    			break;
    		case e_prevNote: {
    
    			if (currentIndex() > 0)
    					changeCurrentIndex(currentIndex() - 1);
    
    			if (currentIndex() < notesCount() - 1)
    				changeCurrentIndex(currentIndex() + 1);
    			else if (insertMode() == e_record && currentIndex() == notesCount() - 1) { // record mode:
    				checkAndAddNote(currentStaff(), currentIndex()); // first to add new note at the end
    				changeCurrentIndex(currentIndex() + 1); // then change current index
    
    			if (currentStaff() != lastStaff())
    
    				changeCurrentIndex((currentStaff()->number() + 1) * staff()->maxNoteCount() );
    			break;
    		}
    		case e_prevStaff: {
    			if (currentStaff() != staff())
    				changeCurrentIndex((currentStaff()->number() - 1) * staff()->maxNoteCount());
    			break;
    		}
    
    	if (prevIndex != currentIndex()) {
    			emit noteWasChanged(currentIndex() % staff()->maxNoteCount(), 
    												*currentStaff()->getNote(currentIndex() % staff()->maxNoteCount()));
    
          if (readOnlyReacting())
            emit lockedNoteClicked(currentIndex());
    
    
    void TmainScore::playSlot() {
    	m_playedIndex++;
    	if (m_playedIndex < notesCount()) {
    
    		// by emitting that signal note is played and shown on the guitar
    			emit noteWasChanged(m_playedIndex % staff()->maxNoteCount(), 
    
    												*currentStaff()->getNote(m_playedIndex % staff()->maxNoteCount()));
    
          if (!m_playTimer->isActive()) // timer is started here to skip out stream initialization delay
            m_playTimer->start(60000 / Tcore::gl()->S->tempo);
    
    	} else
    			emit playbackFinished();
    }
    
    
    
    SeeLook's avatar
    SeeLook committed
    void TmainScore::strikeBlinkingFinished() {
    	if (m_strikeOut) {
    		m_strikeOut->deleteLater();
    		m_strikeOut = 0;
    	}
      delete m_bliking;
    
    	if (m_correctNoteNr < 0) {
    		qDebug() << "TmainScore::strikeBlinkingFinished has wrong note number. Fix it!";
    		return;
    	}
    
    	deleteNoteName(m_correctNoteNr);
    	TscoreNote *sn = noteFromId(m_correctNoteNr);
    
    	sn->setColor(qApp->palette().text().color());
    
    	sn->enableNoteAnim(true, 300);
    	sn->markNote(-1);
    
    SeeLook's avatar
    SeeLook committed
    	bool animEnabled = isAccidToKeyAnimEnabled();
    	enableAccidToKeyAnim(false); // prevent animations - it looks ugly with correction animations
    
    	staves(m_correctNoteNr / staff()->maxNoteCount())->setNote(m_correctNoteNr % staff()->maxNoteCount(), m_goodNote);
    
    SeeLook's avatar
    SeeLook committed
    	enableAccidToKeyAnim(animEnabled);
    	QTimer::singleShot(320, this, SLOT(finishCorrection()));	
    }
    
    
    void TmainScore::keyBlinkingFinished() {
    	if (m_goodKey.value() != keySignature().value()) { // finished 1st time
    			bool animEnabled = isAccidToKeyAnimEnabled();
    			enableAccidToKeyAnim(false); // prevent animations - it looks ugly with correction animations
    			setKeySignature(m_goodKey); // set proper key
    			enableAccidToKeyAnim(animEnabled);
    			if (m_questKey) // desired key name make green and replace ? for !
    
    				m_questKey->setHtml(m_questKey->toHtml().replace("?", "!").replace(Tcore::gl()->EquestionColor.name(), Tcore::gl()->EanswerColor.name()));
    
    SeeLook's avatar
    SeeLook committed
    			m_keyBlinking->startBlinking(3); // and blink again
    	} else { // finished 2nd time
    			delete m_keyBlinking;
    
          m_keyBlinking = 0;
          if (!m_strikeOut && !m_bliking) // no other animations that will emit correctingFinished()
            QTimer::singleShot(100, this, SLOT(finishCorrection())); // it might be invoked even immediately
    
    SeeLook's avatar
    SeeLook committed
    	}
    }
    
    
    void TmainScore::finishCorrection() {
    
      if (m_correctNoteNr > -1) {
        noteFromId(m_correctNoteNr)->enableNoteAnim(false);
        noteFromId(m_correctNoteNr)->markNote(QColor(Tcore::gl()->EanswerColor.lighter().name()));
        m_correctNoteNr = -1;
      }
    
    SeeLook's avatar
    SeeLook committed
    }
    
    SeeLook's avatar
    SeeLook committed
    
    
    void TmainScore::resizeEvent(QResizeEvent* event) {
    
    	if (width() < 300 || height() < 200)
    
    Tomasz Bojczuk's avatar
    Tomasz Bojczuk committed
          return;
    
    SeeLook's avatar
    SeeLook committed
    	performScordatureSet(); // To keep scordature size up to date with score size
    }
    
    
    void TmainScore::performScordatureSet() {
    
    	if (Tcore::gl()->instrument == e_classicalGuitar || Tcore::gl()->instrument == e_electricGuitar) {
    			Ttune tmpTune(*Tcore::gl()->Gtune());
    
    SeeLook's avatar
    SeeLook committed
    			staff()->setScordature(tmpTune);
    	}
    }
    
    
    //####################################################################################################
    //########################################## PRIVATE #################################################
    //####################################################################################################
    
    
    void TmainScore::createActions() {
      m_acts->noteNames()->setChecked(Tcore::gl()->S->namesOnScore);
    
    	m_clearBar = new QToolBar();
    
    	m_clearBar->addAction(m_acts->clearScore());
    
    	m_delCorner = new TcornerProxy(scoreScene(), m_clearBar, Qt::TopRightCorner);
    
    SeeLook's avatar
    SeeLook committed
    void TmainScore::restoreNotesSettings() {
    
      if (Tcore::gl()->S->enharmNotesColor == -1)
          Tcore::gl()->S->enharmNotesColor = QColor(0, 162, 162); // turquoise	scoreScene()->right()->adjustSize();
    
    	if (Tcore::gl()->S->pointerColor == -1) {
    				Tcore::gl()->S->pointerColor = Tcolor::invert(palette().highlight().color());
    				Tcore::gl()->S->pointerColor.setAlpha(200);
    
    	scoreScene()->setPointedColor(Tcore::gl()->S->pointerColor);
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    
    void TmainScore::setQuestionMarkPos() {
    	if (m_questMark && m_questMark->pos().isNull()) {
    
    // 		#if defined(Q_OS_MACX)
    // 			m_questMark->setFont(TnooFont(10));
    // 		#else
    		m_questMark->setFont(TnooFont(8));
    // 		#endif
    
    		QColor c = Tcore::gl()->EquestionColor;
    
    		c.setAlpha(30);
    		m_questMark->setBrush(QBrush(c));
    		m_questMark->setText("?");
    		m_questMark->setScale(scene()->height() / m_questMark->boundingRect().height());
    		m_questMark->setPos(staff()->pos().x() + (staff()->width() - m_questMark->boundingRect().width() * m_questMark->scale()) / 2, 
    												(scene()->height() - m_questMark->boundingRect().height() * m_questMark->scale()) / 2 );
    	}
    }
    
    
    
    SeeLook's avatar
    SeeLook committed
    void TmainScore::createBgRect(QColor c, qreal width, QPointF pos) {
    
      QGraphicsRectItem* bgRect = new QGraphicsRectItem;
      bgRect->setParentItem(staff());
      bgRect->setRect(0, 0, width, staff()->boundingRect().height());
      bgRect->setPos(pos);
      bgRect->setZValue(1);
      bgRect->setPen(QPen(Qt::NoPen));
      bgRect->setBrush(c);
      m_bgRects << bgRect;
    
    SeeLook's avatar
    SeeLook committed
    }
    
    
    
    	QSize ss(mainWindow()->height() / 10, mainWindow()->height() / 10);
    
    	QSize ss(mainWindow()->height() / 15, mainWindow()->height() / 15);
    
    	m_clearBar->setIconSize(ss);
    	m_clearBar->adjustSize();
    }
    
    
    
    void TmainScore::createNoteName() {
    	if (!m_nameMenu) {
    
        m_nameMenu = new TnoteName(mainWindow());
    
        connect(m_nameMenu, &TnoteName::nextNote, this, &TmainScore::moveNameForward);
        connect(m_nameMenu, &TnoteName::prevNote, this, &TmainScore::moveNameBack);
        connect(m_nameMenu, &TnoteName::statusTipRequired, this, &TmainScore::statusTipChanged);
    
        m_nameMenu->setEnabledDblAccid(Tcore::gl()->S->doubleAccidentalsEnabled);
        m_nameMenu->hide();
    
    void TmainScore::enableCorners(bool enable) {
    	if (enable) {
    			m_delCorner->show();
    	} else {
    			m_delCorner->hide();
    	}
    }
    
    
    void TmainScore::moveName(TmainScore::EmoveNote moveDir) {
    
    	moveSelectedNote(moveDir);
    
    	if (oldIndex != currentIndex()) {
    		showNameMenu(currentStaff()->noteSegment(currentIndex() % staff()->maxNoteCount()));
    
    void TmainScore::addStaff(TscoreStaff* st) {
    
    	TmultiScore::addStaff(st);
    	connect(lastStaff(), SIGNAL(noteChanged(int)), this, SLOT(noteWasClickedMain(int)));