Logo Search packages:      
Sourcecode: canorus version File versions  Download package

void CAMainWin::deleteSelection ( CAScoreViewPort v,
bool  deleteSyllables,
bool  deleteNotes,
bool  doUndo 
)

Delete action.

If deleteSyllables or deleteNotes is True, it completely removes the element. Otherwise it only replaces it with rest.

If doUndo is True, it also creates undo. doUndo should be False when cutting.

Definition at line 4700 of file mainwin.cpp.

References CATuplet::addNote(), CATuplet::assignTimes(), CAScoreViewPort::clearSelection(), CARest::composeRests(), CAMusElement::context(), CAPlayback::curPlaying(), CAStaff::getChord(), CAVoice::insert(), CAVoice::lyricsContextList(), CAMusElement::musElementType(), CAVoice::next(), CAVoice::nextPlayable(), CAPlayable::playableLength(), CAPlayableLength::playableLengthToTimeLength(), CAContext::remove(), CAVoice::remove(), CATuplet::removeNote(), CAFunctionMarkContext::repositFunctions(), CALyricsContext::repositSyllables(), CAScoreViewPort::selection(), CAPlayable::setTuplet(), CAScoreViewPort::sheet(), CAPlayable::staff(), CAPlayback::stopNow(), CAMusElement::timeEnd(), CAMusElement::timeLength(), CAMusElement::timeStart(), CAPlayable::tuplet(), and CAPlayable::voice().

Referenced by on_uiCut_triggered(), and scoreViewPortKeyPress().

                                                                                                         {
      if ( v->selection().size() ) {
            if (doUndo)
                  CACanorus::undo()->createUndoCommand( document(), tr("deletion of elements", "undo") );

            QSet<CAMusElement*> musElemSet;
            for (int i=0; i<v->selection().size(); i++)
                  musElemSet << v->selection().at(i)->musElement();

            // cleans up the set - removes empty elements and elements which get deleted automatically (eg. slurs, if both notes are deleted, marks)
            for (QSet<CAMusElement*>::iterator i=musElemSet.begin(); i!=musElemSet.end(); ) {
                  if (!(*i) ||
                       (*i)->musElementType()==CAMusElement::Slur && musElemSet.contains(static_cast<CASlur*>(*i)->noteStart()) ||
                       (*i)->musElementType()==CAMusElement::Slur && musElemSet.contains(static_cast<CASlur*>(*i)->noteEnd()) ||
                       (*i)->musElementType()==CAMusElement::Mark && musElemSet.contains(static_cast<CAMark*>(*i)->associatedElement()) ) {
                        i = musElemSet.erase(i);
                  } else {
                        i++;
                  }
            }

            for (QSet<CAMusElement*>::const_iterator i=musElemSet.constBegin(); i!=musElemSet.constEnd(); i++) {
                  if ( (*i)->isPlayable() ) {
                        CAPlayable *p = static_cast<CAPlayable*>(*i);
                        if ( (p->musElementType()==CAMusElement::Rest) || (!static_cast<CANote*>(p)->isPartOfChord()) ) {
                              // find out the status of the rests in other voices
                              QList<CAPlayable*> chord = p->staff()->getChord( p->timeStart() );
                              QList<CARest*> restsInOtherVoices;

                              // if deleting a rest, shift back by default
                              if (p->musElementType()==CAMusElement::Rest)
                                    deleteNotes = true;

                              int chordIdx;
                              for (chordIdx=0; chordIdx<chord.size(); chordIdx++) { // calculates chordIdx
                                    if ( chord[chordIdx]->voice()!=p->voice() ) {
                                          CAPlayable *current = chord[chordIdx];
                                          do {
                                                if ( current->musElementType() == CAMusElement::Rest )
                                                      restsInOtherVoices << static_cast<CARest*>(current);
                                                else if( !musElemSet.contains(current) ) {
                                                      deleteNotes = false; // note in other voice which is not going to be deleted, don't shift back
                                                      break;
                                                }
                                                // loop over following playables while they start before p ends. i.e., not shifting back in situations such as: << { c1 } \ { r4 r g g } >>.
                                          } while( (current = current->voice()->nextPlayable( current->timeStart() )) && (current->timeStart() < p->timeEnd()) );

                                          if ( !current && restsInOtherVoices.size() &&
                                                ( restsInOtherVoices.back()->voice()!=chord[chordIdx]->voice() ||
                                                  restsInOtherVoices.back()->voice()==chord[chordIdx]->voice() &&
                                                  restsInOtherVoices.back()->timeEnd() < p->timeEnd() )
                                             ) {
                                                deleteNotes = false;
                                                break;
                                          }
                                    }
                              }
                              if ( !deleteNotes ) {
                                    // replace note with rest

                                    QList<CARest*> rests = CARest::composeRests( CAPlayableLength::playableLengthToTimeLength( p->playableLength() ), p->timeStart(), p->voice(), CARest::Normal );
                                    for (int j=0; j<rests.size(); j++)
                                          p->voice()->insert( p, rests[j] );

                                    for (int j=0; j<p->voice()->lyricsContextList().size(); j++) { // remove syllables
                                          CASyllable *removedSyllable =
                                                p->voice()->lyricsContextList().at(j)->syllableAtTimeStart(p->timeStart());

                                          musElemSet.remove(removedSyllable); // only remove syllables from the selection
                                    }

                                    if ( p->tuplet() ) { // remove the note from tuplet and add a rest
                                          CATuplet *tuplet = p->tuplet();
                                          tuplet->removeNote(p);
                                          p->setTuplet(0);
                                          for (int j=0; j<rests.size(); j++) {
                                                tuplet->addNote(rests[j]);
                                          }

                                          p->voice()->remove( p, true );
                                          tuplet->assignTimes();
                                    }

                              } else {
                                    // Actually remove the note or rest and shift other elements back if only rests in other voices present.
                                    // This is allowed, if only rests are present below the deleted element in other voices.
                                    // Example - the most comprehensive deletion:
                                    // When deleting a half note in the first voice and there are two half rests in the second voice sticking
                                    // together just in the middle of our half note, the deletion is made in 3 steps:
                                    // 1. Convert the two rests below the deleted element to one quarter rest before the element, one half rest
                                    //    just below the deleted element (its timeStart and timeLength are now equal to deleted elt) and one
                                    //    quarter rest after the deleted element.
                                    // 2. Do step 1 for all other voices (we only have 2 voices in our case).
                                    // 3. Delete the newly generated rest right below the element in other voices and do not shift back
                                    //    shared elements (shared elements are shifted back in the next step).
                                    // 4. Delete the deleted element and shift back shared elements.

                                    QList<CARest*> restRightBelowDeletedElement;
                                    // remove any rests from other voices
                                    for (int j=0; j<restsInOtherVoices.size(); j++) {

                                          // gather rests in the current voice
                                          QList<CARest*> restsInCurVoice;
                                          restsInCurVoice << restsInOtherVoices[j];
                                          CAVoice *rVoice = restsInOtherVoices[j]->voice();
                                          while ( (++j < restsInOtherVoices.size()) && (restsInOtherVoices[j]->voice() == rVoice) ) {
                                                restsInCurVoice << restsInOtherVoices[j];
                                          }

                                          int firstTimeStart = restsInCurVoice.front()->timeStart();
                                          int fullTimeLength = restsInCurVoice.back()->timeEnd() - restsInCurVoice.front()->timeStart();
                                          int firstTimeLength = p->timeStart() - firstTimeStart;
                                          CARest::CARestType firstRestType = restsInCurVoice.front()->restType();
                                          CARest::CARestType lastRestType = restsInCurVoice.back()->restType();

                                          CAMusElement *nextElt = rVoice->next( restsInCurVoice.back() ); // needed later for insertion

                                          // convert the rests
                                          for ( int k=0; k<restsInCurVoice.size(); k++) {
                                                if (restsInCurVoice[k]->tuplet()) {
                                                      musElemSet.remove( restsInCurVoice[k]->tuplet() );
                                                }
                                                musElemSet.remove( restsInCurVoice[k] );
                                                rVoice->remove( restsInCurVoice[k], true );
                                          }

                                          // insert the first rests
                                          QList<CARest*> firstRests = CARest::composeRests( firstTimeLength, firstTimeStart, rVoice, firstRestType );
                                          for (int k=0; k<firstRests.size(); k++) {
                                                rVoice->insert( nextElt, firstRests[k] );
                                          }

                                          // insert the rests below the element - needed to correctly update timeStart of shared elements
                                          QList<CARest*> restsRightBelow = CARest::composeRests( p->timeLength(), firstTimeStart+firstTimeLength, rVoice, CARest::Normal );
                                          for (int k=0; k<restsRightBelow.size(); k++) {
                                                rVoice->insert( nextElt, restsRightBelow[k] );
                                          }

                                          // insert the rests after the element
                                          QList<CARest*> lastRests = CARest::composeRests( fullTimeLength-(firstTimeLength+p->timeLength()), firstTimeStart+firstTimeLength+p->timeLength(), rVoice, lastRestType );
                                          for (int k=0; k<lastRests.size(); k++) {
                                                rVoice->insert( nextElt, lastRests[k] );
                                          }

                                          // delete the rests below the element
                                          for (int k=0; k<restsRightBelow.size(); k++) {
                                                delete restsRightBelow[k]; // do not shift back shared elements
                                          }
                                    }

                                    for (int j=0; j<p->voice()->lyricsContextList().size(); j++) // delete and shift syllables
                                          musElemSet.remove( p->voice()->lyricsContextList().at(j)->removeSyllableAtTimeStart(p->timeStart()) );

                                    if ( p->tuplet() ) { // remove the tuplet from selection, if any, because it's deleted in playable destructor
                                          musElemSet.remove( p->tuplet() );
                                    }
                              }
                        }

                        // stop the playback before deleting the note
                        if (_playback && _playback->curPlaying().contains(p)) {
                              _playback->stopNow();
                        }

                        p->voice()->remove( p, true );
                        for (int j=0; j<p->voice()->lyricsContextList().size(); j++) {
                              p->voice()->lyricsContextList().at(j)->repositSyllables();
                        }
                        delete p;
                  } else if ( (*i)->musElementType()==CAMusElement::Syllable ) {
                        if ( deleteSyllables ) {
                              CALyricsContext *lc = static_cast<CALyricsContext*>((*i)->context());
                              (*i)->context()->remove(*i);  // actually removes the syllable if SHIFT is pressed
                              lc->repositSyllables();
                        } else {
                              static_cast<CASyllable*>(*i)->clear(); // only clears syllable's text
                        }
                  } else if ( (*i)->musElementType()==CAMusElement::FunctionMark ) {
                        if ( deleteSyllables ) {
                              CAFunctionMarkContext *fmc = static_cast<CAFunctionMarkContext*>((*i)->context());
                              (*i)->context()->remove(*i);  // actually removes the function if SHIFT is pressed
                              fmc->repositFunctions();
                        } else {
                              static_cast<CAFunctionMark*>(*i)->clear(); // only clears the function
                        }
                  } else if ( (*i)->musElementType()==CAMusElement::Mark ) {
                        delete *i; // also removes itself from associated elements
                  } else if ( (*i)->musElementType()==CAMusElement::Slur ) {
                        delete *i; // also removes itself from associated elements
                  } else if ( (*i)->musElementType()==CAMusElement::Tuplet ) {
                        delete *i; // also removes itself from associated elements
                  } else {
                        (*i)->context()->remove(*i);
                  }
            }
            if (doUndo)
                  CACanorus::undo()->pushUndoCommand();

            v->clearSelection();
            CACanorus::rebuildUI(document(), v->sheet());
      }
}


Generated by  Doxygen 1.6.0   Back to index