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

void CAMainWin::pasteAt ( const QPoint  coords,
CAScoreViewPort v 
)

Backend for Edit->Paste.

Definition at line 4906 of file mainwin.cpp.

References CASheet::addContext(), CALyricsContext::addEmptySyllable(), CALyricsContext::addSyllable(), CAScoreViewPort::addToSelection(), CAStaff::addVoice(), CAScoreViewPort::clearSelection(), CASyllable::clone(), CATuplet::clone(), CAMusElement::clone(), CASlur::clone(), CAVoice::contains(), CAMusElement::context(), CASheet::contextAt(), CASheet::contextCount(), CASheet::contextIndex(), CASheet::contextList(), CAContext::contextType(), currentContext(), CAScoreViewPort::currentContext(), CAVoice::insert(), CASheet::insertContextAfter(), CAMusElement::isPlayable(), CAVoice::lastNote(), CAVoice::lyricsContextList(), CAMusElement::musElementType(), CAScoreViewPort::nearestRightElement(), CASlur::noteEnd(), CATuplet::noteList(), CANote::phrasingSlurEnd(), CANote::phrasingSlurStart(), CAVoice::previous(), CALyricsContext::repositSyllables(), CAScoreViewPort::setLastMousePressCoordsAfter(), CANote::setPhrasingSlurStart(), CANote::setSlurStart(), CANote::setTieStart(), CAMusElement::setTimeStart(), CAScoreViewPort::sheet(), CAContext::sheet(), CANote::slurEnd(), CANote::slurStart(), CASlur::slurType(), CASheet::staffCount(), CALyricsContext::syllableAt(), CALyricsContext::syllableList(), CAStaff::synchronizeVoices(), CANote::tieEnd(), CANote::tieStart(), CAMusElement::timeStart(), CAPlayable::tuplet(), CAStaff::voiceAt(), and CAStaff::voiceCount().

Referenced by on_uiPaste_triggered().

                                                                 {
      if ( QApplication::clipboard()->mimeData() &&
           dynamic_cast<const CAMimeData*>(QApplication::clipboard()->mimeData()) &&
           v->currentContext() ) {
            CACanorus::undo()->createUndoCommand( document(), tr("paste", "undo") );

            CAContext* currentContext = v->currentContext()->context();
            CASheet* currentSheet = currentContext->sheet();

            QList<CAMusElement*> newEltList;
            QList<CAContext*> contexts = static_cast<const CAMimeData*>(QApplication::clipboard()->mimeData())->contexts();
            QHash<CAVoice*, CAVoice*> voiceMap; // MimeData -> paste
            foreach( CAContext* context, contexts ) {
                  // create a new context if there isn't one of the right type.
                  // exception: if the context is a staff, skip lyrics contexts instead of inserting a staff before a lyrics context.
                  if(context->contextType() == CAContext::Staff)
                        while(currentContext && currentContext->contextType() == CAContext::LyricsContext)
                              if(currentContext != currentSheet->contextList().last())
                                    currentContext = currentSheet->contextAt(currentSheet->contextIndex(currentContext)+1);
                              else
                                    currentContext = 0;

                  if(!currentContext || context->contextType() != currentContext->contextType())
                  {
                        CAContext* newContext = 0;
                        switch(context->contextType())
                        {
                              case CAContext::Staff: {
                                    CAStaff* s = static_cast<CAStaff*>(context), *newStaff;
                                    newContext = newStaff =  new CAStaff(tr("Staff%1").arg(v->sheet()->staffCount()+1), currentSheet);
                                    break;
                              }
                              case CAContext::LyricsContext: {
                                    /* Two cases:
                                     * - Some notes were copied together with the lyrics below them: in this case the linked voice would've already been pasted (as the context list is ordered top to bottom), so we find the new voice using voiceMap.
                                     * - Lyrics were copied without the notes. If currentContext is a staff, we'll use the current voice. Otherwise lyrics will not be pasted.
                                     */
                                    CAVoice* voice = voiceMap[static_cast<CALyricsContext*>(context)->associatedVoice()];
                                    if(!voice && currentContext && currentContext->contextType() == CAContext::Staff)
                                          voice = static_cast<CAStaff*>(currentContext)->voiceAt( (currentContext == v->currentContext()->context())? (uiVoiceNum->getRealValue()?uiVoiceNum->getRealValue()-1:uiVoiceNum->getRealValue()) : 1); // That is, if the currentContext is still the context that the user last clicked before pasting, use the current voice number. Otherwise, use the first voice.
                                    if(!voice)
                                          continue; // skipping lyrics - can't find a staff.

                                    newContext = new CALyricsContext(tr("LyricsContext%1").arg(v->sheet()->contextCount()+1), 1, voice);
                                    break;
                              }
                              case CAContext::FunctionMarkContext: {
                                    newContext = new CAFunctionMarkContext(tr("FunctionMarkContext%1").arg(v->sheet()->contextCount()+1), currentSheet);
                                    break;
                              }
                        }
                        if(currentContext)
                              currentSheet->insertContextAfter(currentContext, newContext);
                        else
                              currentSheet->addContext(newContext);
                        currentContext = newContext;
                  }
                  if(context->contextType()==CAContext::Staff) {
                        CAStaff* staff = static_cast<CAStaff*>(currentContext), *cbstaff = static_cast<CAStaff*>(context);
                        int voice = uiVoiceNum->getRealValue() ? uiVoiceNum->getRealValue()-1 : uiVoiceNum->getRealValue();
                        for(int i=staff->voiceCount()-1; i < voice+cbstaff->voiceCount()-1; i++) {
                              staff->addVoice();
                        }
                        for(int i=voice; i<voice+cbstaff->voiceCount(); i++) {
                              int cbi = i-voice;
                              CADrawableMusElement *drawable = v->nearestRightElement(coords.x(), coords.y(), staff->voiceAt(i));
                              voiceMap[cbstaff->voiceAt(cbi)] = staff->voiceAt(i);
                              CAMusElement* right = (drawable)?drawable->musElement():0;

                              // Can't have playables between two notes linked by a tie. Remove the tie in this case.
                              // FIXME this should be the behavior for insert as well.
                              CAMusElement* leftPl = right;
                            while((leftPl = staff->voiceAt(i)->previous(leftPl)) && !leftPl->isPlayable());
                              CANote* leftNote = (leftPl&&leftPl->musElementType()==CAMusElement::Note)?static_cast<CANote*>(leftPl):0;
                              CASlur* tie = leftNote?leftNote->tieStart():0;

                              if(tie)
                              {
                                    if(tie->noteEnd() && staff->voiceAt(i)->contains(tie->noteEnd()))
                                          // pasting between two tied notes - remove tie
                                          delete tie; // resets notes' tieStart/tieEnd;
                                    else {
                                          // pasting after an "open" tie - if the first paste element is a note, connect them. Otherwise delete the tie.
                                          int idx = 0;
                                          for(;idx < cbstaff->voiceAt(cbi)->musElementCount() && !cbstaff->voiceAt(cbi)->musElementAt(idx)->isPlayable();idx++);
                                          CAPlayable* first = (idx!=cbstaff->voiceAt(cbi)->musElementCount())?static_cast<CAPlayable*>(cbstaff->voiceAt(cbi)->musElementAt(idx)):0;
                                          if(first && first->musElementType() == CAMusElement::Note)
                                                static_cast<CANote*>(first)->setTieEnd(tie);
                                          else
                                                delete tie;
                                    }
                              }

                              QHash<CATuplet*, QList<CAPlayable*> > tupletMap;
                              QHash<CASlur*, CANote*> slurMap;
                              foreach(CAMusElement* elt, cbstaff->voiceAt(cbi)->musElementList()) {
                                    CAMusElement* cloned = (elt->isPlayable())?static_cast<CAPlayable*>(elt)->clone(staff->voiceAt(i)):elt->clone(staff);
                                    CANote* n = (elt->musElementType() == CAMusElement::Note)?static_cast<CANote*>(elt):0;
                                    CAMusElement* prev = cbstaff->voiceAt(cbi)->previous(n);
                                    CANote* prevNote = (prev&&prev->musElementType() == CAMusElement::Note)?static_cast<CANote*>(prev):0;
                                    bool chord = n && prevNote && prevNote->timeStart() == n->timeStart();
                                    if(n)
                                    {
                                          QList<CASlur*> slurs;
                                          slurs << n->tieStart() << n->tieEnd() << n->slurStart() << n->slurEnd() << n->phrasingSlurStart() << n->phrasingSlurEnd();
                                          slurs.removeAll(0);
                                          foreach(CASlur* s, slurs)
                                          {
                                                if(!slurMap.contains(s))
                                                      slurMap[s] = static_cast<CANote*>(cloned);
                                                else {
                                                      CANote* noteStart = slurMap[s], *noteEnd = static_cast<CANote*>(cloned);
                                                      CASlur* newSlur = s->clone(noteStart->context(), noteStart, noteEnd);
                                                      switch (s->slurType()) {
                                                            case CASlur::TieType:
                                                                  noteStart->setTieStart( newSlur );
                                                                  noteEnd->setTieEnd( newSlur );
                                                                  break;
                                                            case CASlur::SlurType:
                                                                  noteStart->setSlurStart( newSlur );
                                                                  noteEnd->setSlurEnd( newSlur );
                                                                  break;
                                                            case CASlur::PhrasingSlurType:
                                                                  noteStart->setPhrasingSlurStart( newSlur );
                                                                  noteEnd->setPhrasingSlurEnd( newSlur );
                                                                  break;
                                                      }
                                                }
                                          }

                                    }
                                    staff->voiceAt(i)->insert(chord?newEltList.last():right, cloned, chord);
                                    newEltList << cloned;
                                    if(elt->isPlayable())
                                    {
                                          CAPlayable* pl = static_cast<CAPlayable*>(elt);
                                          if(pl->tuplet())
                                          {
                                                tupletMap[pl->tuplet()] << static_cast<CAPlayable*>(cloned);
                                                if(tupletMap[pl->tuplet()].size() == pl->tuplet()->noteList().size())
                                                      pl->tuplet()->clone(tupletMap[pl->tuplet()]);
                                          }
                                    }
                                    // FIXME duplicated from CAMusElementFactory::configureNote.
                                    if(n && staff->voiceAt(i)->lastNote() != static_cast<CANote*>(cloned)) {
                                          foreach( CALyricsContext* context, staff->voiceAt(i)->lyricsContextList() )
                                                context->addEmptySyllable(cloned->timeStart(), cloned->timeLength());
                                          foreach( CAContext* context, currentSheet->contextList() )
                                                if(context->contextType()==CAContext::FunctionMarkContext)
                                                      static_cast<CAFunctionMarkContext*>(context)->addEmptyFunction(cloned->timeStart(), cloned->timeLength());
                                    }
                              }
                              foreach( CALyricsContext* context, staff->voiceAt(i)->lyricsContextList() )
                                    context->repositSyllables();
                              foreach( CAContext* context, currentSheet->contextList() )
                                    if(context->contextType()==CAContext::FunctionMarkContext)
                                          static_cast<CAFunctionMarkContext*>(context)->repositFunctions();
                        }
                        staff->synchronizeVoices();
                  } else {
                        // \todo function mark copy&paste unimplemented
                        if(context->contextType() == CAContext::LyricsContext) {
                              CALyricsContext* lc = static_cast<CALyricsContext*>(context);
                              CALyricsContext* currentLc = static_cast<CALyricsContext*>(currentContext);
                              CADrawableMusElement *drawable = 0;
                              if(currentContext == v->currentContext()->context()) // pasting where the user has clicked
                        drawable = v->nearestRightElement(coords.x(), coords.y(), v->currentContext() );
                              int offset = lc->syllableAt(0)->timeStart() - (drawable ? drawable->musElement()->timeStart() : 0);
                              // Add syllables until there are no more notes, [popping existing syllables on the right end?].
                              foreach(CASyllable* syl, lc->syllableList()) {
                                    CASyllable* clone = syl->clone(currentLc);
                                    clone->setTimeStart(clone->timeStart()-offset);
                                    currentLc->addSyllable(clone, false);
                                    newEltList << clone;
                              }
                        }
                  }
                  int idx = currentSheet->contextList().indexOf(currentContext);
                  currentContext = (idx+1 < currentSheet->contextCount()) ? currentSheet->contextAt(idx+1) : 0;
            }

            CACanorus::undo()->pushUndoCommand();
            CACanorus::rebuildUI( document(), currentSheet );

            // select pasted elements
            currentScoreViewPort()->clearSelection();
            for (int i=0; i<newEltList.size(); i++)
                  currentScoreViewPort()->addToSelection( newEltList[i] );
            currentScoreViewPort()->setLastMousePressCoordsAfter( newEltList );
            currentScoreViewPort()->repaint();
      }
}


Generated by  Doxygen 1.6.0   Back to index