// TnzCore includes #include "tconst.h" #include "tundo.h" // TnzBase includes #include "tfxattributes.h" // TnzLib includes #include "toonz/tcolumnfx.h" #include "toonz/fxcommand.h" #include "toonz/fxdag.h" #include "toonz/txsheet.h" #include "toonz/tfxhandle.h" #include "toonz/tcolumnfxset.h" #include "toonz/txsheethandle.h" #include "toonzqt/fxschematicscene.h" // TnzQt includes #include "toonzqt/schematicnode.h" #include "toonzqt/fxschematicnode.h" #include "toonzqt/selectioncommandids.h" #include "fxdata.h" // Qt includes #include #include #include "toonzqt/fxselection.h" namespace { bool canGroup(TFx *fx) { TXsheetFx *xfx = dynamic_cast(fx); TOutputFx *ofx = dynamic_cast(fx); return (!xfx && !ofx); } } // namespace //========================================================= // // FxSelection // //========================================================= FxSelection::FxSelection() : m_xshHandle(0) , m_fxHandle(0) , m_pastePosition(TConst::nowhere) , m_schematicScene(0) {} //--------------------------------------------------------- FxSelection::FxSelection(const FxSelection &src) : m_selectedFxs(src.m_selectedFxs) , m_selectedLinks(src.m_selectedLinks) , m_xshHandle(src.m_xshHandle) , m_fxHandle(src.m_fxHandle) , m_selectedColIndexes(src.m_selectedColIndexes) , m_pastePosition(TConst::nowhere) , m_schematicScene(src.m_schematicScene) {} //--------------------------------------------------------- FxSelection::~FxSelection() {} //--------------------------------------------------------- void FxSelection::enableCommands() { enableCommand(this, MI_Clear, &FxSelection::deleteSelection); enableCommand(this, MI_Cut, &FxSelection::cutSelection); enableCommand(this, MI_Copy, &FxSelection::copySelection); enableCommand(this, MI_Paste, &FxSelection::pasteSelection); enableCommand(this, MI_Group, &FxSelection::groupSelection); enableCommand(this, MI_Ungroup, &FxSelection::ungroupSelection); enableCommand(this, MI_Collapse, &FxSelection::collapseSelection); enableCommand(this, MI_ExplodeChild, &FxSelection::explodeChild); } //--------------------------------------------------------- TSelection *FxSelection::clone() const { assert(0); return new FxSelection(*this); } //--------------------------------------------------------- void FxSelection::select(TFxP fx) { m_selectedFxs.append(fx); } //--------------------------------------------------------- void FxSelection::select(int colIndex) { m_selectedColIndexes.append(colIndex); std::sort(m_selectedColIndexes.begin(), m_selectedColIndexes.end()); } //--------------------------------------------------------- void FxSelection::unselect(int colIndex) { m_selectedColIndexes.removeOne(colIndex); } //--------------------------------------------------------- void FxSelection::unselect(TFxP fx) { int index = m_selectedFxs.indexOf(fx); if (index >= 0) m_selectedFxs.removeAt(index); } //--------------------------------------------------------- void FxSelection::select(SchematicLink *link) { if (link->isLineShaped()) return; Link boundingFxs = getBoundingFxs(link); if (boundingFxs == Link()) return; m_selectedLinks.append(boundingFxs); } //--------------------------------------------------------- void FxSelection::unselect(SchematicLink *link) { Link boundingFxs = getBoundingFxs(link); int index = m_selectedLinks.indexOf(boundingFxs); if (index >= 0) m_selectedLinks.removeAt(index); } //--------------------------------------------------------- bool FxSelection::isSelected(TFxP fx) const { int i; for (i = 0; i < m_selectedFxs.size(); i++) { TFx *selectedFx = m_selectedFxs[i].getPointer(); TZeraryColumnFx *zfx = dynamic_cast(selectedFx); if (zfx && (fx.getPointer() == zfx || fx.getPointer() == zfx->getZeraryFx())) return true; if (fx.getPointer() == selectedFx) return true; } return false; } //--------------------------------------------------------- bool FxSelection::isSelected(int columnIndex) const { return m_selectedColIndexes.contains(columnIndex); } //--------------------------------------------------------- bool FxSelection::isSelected(SchematicLink *link) { Link boundingFxs = getBoundingFxs(link); return m_selectedLinks.contains(boundingFxs); } //--------------------------------------------------------- void FxSelection::deleteSelection() { emit doDelete(); // std::list> fxList = m_selectedFxs.toStdList(); // TFxCommand::deleteSelection(fxList, m_selectedLinks.toStdList(), // m_selectedColIndexes.toStdList(), m_xshHandle, // m_fxHandle); } //--------------------------------------------------------- void FxSelection::copySelection() { QClipboard *clipboard = QApplication::clipboard(); FxsData *fxsData = new FxsData(); fxsData->setFxs(m_selectedFxs, m_selectedLinks, m_selectedColIndexes, m_xshHandle->getXsheet()); clipboard->setMimeData(fxsData); } //--------------------------------------------------------- void FxSelection::cutSelection() { copySelection(); deleteSelection(); } //--------------------------------------------------------- void FxSelection::pasteSelection() { /*--- Fxノードを1つだけ選択していた場合、Replace paste ---*/ if (m_selectedFxs.size() >= 1 && m_selectedLinks.size() == 0 && m_selectedColIndexes.isEmpty()) replacePasteSelection(); /*--- Linkを1つだけ選択していた場合、Insert paste ---*/ else if (m_selectedFxs.size() == 0 && m_selectedLinks.size() >= 1 && m_selectedColIndexes.isEmpty()) insertPasteSelection(); else { QClipboard *clipboard = QApplication::clipboard(); const FxsData *fxsData = dynamic_cast(clipboard->mimeData()); if (!fxsData) return; QList fxs; QMap zeraryFxColumnSize; QList columns; fxsData->getFxs(fxs, zeraryFxColumnSize, columns); if (fxs.isEmpty() && columns.isEmpty()) return; // in case of the paste command triggered from short cut key if (m_pastePosition == TConst::nowhere && m_schematicScene) { SchematicSceneViewer *ssv = dynamic_cast(m_schematicScene->views().at(0)); if (ssv) m_pastePosition = TPointD(ssv->getOldScenePos().x(), ssv->getOldScenePos().y()); } TFxCommand::pasteFxs(fxs.toStdList(), zeraryFxColumnSize.toStdMap(), columns.toStdList(), m_pastePosition, m_xshHandle, m_fxHandle); if (m_schematicScene) { selectNone(); for (int i = 0; i < (int)fxs.size(); i++) select(fxs[i]); m_schematicScene->selectNodes(fxs); } } m_pastePosition = TConst::nowhere; } //--------------------------------------------------------- bool FxSelection::insertPasteSelection() { QClipboard *clipboard = QApplication::clipboard(); const FxsData *fxsData = dynamic_cast(clipboard->mimeData()); m_pastePosition = TConst::nowhere; if (!fxsData || !fxsData->isConnected()) return false; if (m_selectedLinks.isEmpty()) return true; // Start an undo block and ensure it is appropriately destroyed struct Auto { bool m_destruct; ~Auto() { if (m_destruct) TUndoManager::manager()->endBlock(); } } auto_ = {false}; // Need to make a temporary copy of selected links. It's necessary since the // selection will // be updated (cleared) after each insertion. QList selectedLinks(m_selectedLinks); int i, size = selectedLinks.size(); for (i = 0; i < size; ++i) { // Clone the fxs to be inserted QList fxs; QMap zeraryFxColumnSize; QList columns; fxsData->getFxs(fxs, zeraryFxColumnSize, columns); if (fxs.empty() && columns.empty()) return true; // auto ends the undo block in the destructor. if (!auto_.m_destruct) { auto_.m_destruct = true; TUndoManager::manager()->beginBlock(); } TFxCommand::insertPasteFxs(selectedLinks[i], fxs.toStdList(), zeraryFxColumnSize.toStdMap(), columns.toStdList(), m_xshHandle, m_fxHandle); } return true; } //--------------------------------------------------------- bool FxSelection::addPasteSelection() { QClipboard *clipboard = QApplication::clipboard(); const FxsData *fxsData = dynamic_cast(clipboard->mimeData()); m_pastePosition = TConst::nowhere; if (!fxsData || !fxsData->isConnected()) return false; if (m_selectedFxs.isEmpty()) return true; struct Auto { bool m_destruct; ~Auto() { if (m_destruct) TUndoManager::manager()->endBlock(); } } auto_ = {false}; QList selectedFxs(m_selectedFxs); int i, size = selectedFxs.size(); for (i = 0; i < size; ++i) { // Clone the fxs to be inserted QList fxs; QMap zeraryFxColumnSize; QList columns; fxsData->getFxs(fxs, zeraryFxColumnSize, columns); if (fxs.empty() && columns.empty()) return true; // auto ends the undo block in its destructor if (!auto_.m_destruct) auto_.m_destruct = true, TUndoManager::manager()->beginBlock(); TFx *inFx = selectedFxs[i].getPointer(); TFxCommand::addPasteFxs(inFx, fxs.toStdList(), zeraryFxColumnSize.toStdMap(), columns.toStdList(), m_xshHandle, m_fxHandle); } return true; } //-------------------------------------------------------- bool FxSelection::replacePasteSelection() { QClipboard *clipboard = QApplication::clipboard(); const FxsData *fxsData = dynamic_cast(clipboard->mimeData()); m_pastePosition = TConst::nowhere; if (!fxsData || !fxsData->isConnected()) return false; if (m_selectedFxs.isEmpty()) return true; struct Auto { bool m_destruct; ~Auto() { if (m_destruct) TUndoManager::manager()->endBlock(); } } auto_ = {false}; QList selectedFxs(m_selectedFxs); int i, size = selectedFxs.size(); for (i = 0; i < size; ++i) { // Clone the fxs to be inserted QList fxs; QMap zeraryFxColumnSize; QList columns; fxsData->getFxs(fxs, zeraryFxColumnSize, columns); if (fxs.empty() && columns.empty()) return true; // auto ends the undo block in its destructor if (!auto_.m_destruct) auto_.m_destruct = true, TUndoManager::manager()->beginBlock(); TFx *inFx = m_selectedFxs[i].getPointer(); TFxCommand::replacePasteFxs(inFx, fxs.toStdList(), zeraryFxColumnSize.toStdMap(), columns.toStdList(), m_xshHandle, m_fxHandle); } return true; } //--------------------------------------------------------- void FxSelection::groupSelection() { if (m_selectedFxs.size() <= 1) return; TFxCommand::groupFxs(m_selectedFxs.toStdList(), m_xshHandle); selectNone(); m_xshHandle->notifyXsheetChanged(); } //--------------------------------------------------------- void FxSelection::ungroupSelection() { if (isEmpty()) return; QSet idSet; int i; for (i = 0; i < m_selectedFxs.size(); i++) { int groupId = m_selectedFxs[i]->getAttributes()->getGroupId(); if (groupId > 0) idSet.insert(groupId); } TUndoManager::manager()->beginBlock(); QSet::iterator it; for (it = idSet.begin(); it != idSet.end(); it++) { TFxCommand::ungroupFxs(*it, m_xshHandle); } TUndoManager::manager()->endBlock(); selectNone(); m_xshHandle->notifyXsheetChanged(); } //--------------------------------------------------------- void FxSelection::collapseSelection() { if (!m_selectedFxs.isEmpty()) emit doCollapse(m_selectedFxs); } //--------------------------------------------------------- void FxSelection::explodeChild() { if (!m_selectedFxs.isEmpty()) emit doExplodeChild(m_selectedFxs); } //--------------------------------------------------------- Link FxSelection::getBoundingFxs(SchematicLink *link) { Link boundingFxs; if (link) { SchematicPort *port = link->getStartPort(); if (!port) return boundingFxs; if (port->getType() == 201 || port->getType() == 202 || port->getType() == 203) boundingFxs = getBoundingFxs(port, link->getOtherPort(port)); else if (port->getType() == 200 || port->getType() == 204) boundingFxs = getBoundingFxs(link->getOtherPort(port), port); } return boundingFxs; } //--------------------------------------------------------- Link FxSelection::getBoundingFxs(SchematicPort *inputPort, SchematicPort *outputPort) { Link boundingFxs; if (!inputPort || !outputPort) return boundingFxs; FxSchematicNode *inputNode = dynamic_cast(outputPort->getNode()); FxSchematicNode *outputNode = dynamic_cast(inputPort->getNode()); FxGroupNode *groupNode = dynamic_cast(inputNode); if (!inputNode || !outputNode || (groupNode && groupNode->getOutputConnectionsCount() != 1)) return boundingFxs; if (dynamic_cast(outputNode->getFx())) { if (!groupNode) boundingFxs.m_inputFx = inputNode->getFx(); else { TFxSet *terminals = m_xshHandle->getXsheet()->getFxDag()->getTerminalFxs(); QList roots = groupNode->getRootFxs(); int i; for (i = 0; i < roots.size(); i++) if (terminals->containsFx(roots[i].getPointer())) { boundingFxs.m_inputFx = roots[i]; break; } } boundingFxs.m_outputFx = outputNode->getFx(); return boundingFxs; } if (outputNode->isA(eGroupedFx)) { // devo prima trovare l'effetto interno al gruppo al quale inputNode e' // linkato. FxGroupNode *groupNode = dynamic_cast(outputNode); assert(groupNode); QList fxs; TFx *inputFx = inputNode->getFx(); int i; for (i = 0; i < inputFx->getOutputConnectionCount(); i++) { TFx *outputFx = inputFx->getOutputConnection(i)->getOwnerFx(); if (!outputFx) continue; if (groupNode->contains(outputFx)) fxs.push_back(outputFx); } if (fxs.size() != 1) // un nodo esterno al gruppo puo' essere linkato a // piu' nodi interni al gruppo return boundingFxs; TFx *outputFx = fxs[0]; // ho tovato l'effetto, ora devo trovare l'indice della porta a cui e' // linkato l'effetto in input for (i = 0; i < outputFx->getInputPortCount(); i++) { TFxPort *inputPort = outputFx->getInputPort(i); TFx *fx = inputPort->getFx(); if (fx == inputFx) break; } if (i >= outputFx->getInputPortCount()) return boundingFxs; boundingFxs.m_inputFx = inputFx; boundingFxs.m_outputFx = outputFx; boundingFxs.m_index = i; return boundingFxs; } else { bool found = false; int i, index = -1; for (i = 0; i < outputNode->getInputPortCount() && !found; i++) { FxSchematicPort *inputAppPort = outputNode->getInputPort(i); int j; for (j = 0; j < inputAppPort->getLinkCount(); j++) { FxSchematicNode *outputAppNode = dynamic_cast(inputAppPort->getLinkedNode(j)); if (!outputAppNode) continue; FxSchematicPort *outputAppPort = outputAppNode->getOutputPort(); if (inputAppPort == inputPort && outputPort == outputAppPort) { found = true; index = i; break; } } } if (index == -1) return boundingFxs; TFx *inputFx = inputNode->getFx(); TFx *outputFx = outputNode->getFx(); boundingFxs.m_inputFx = inputFx; boundingFxs.m_outputFx = outputFx; boundingFxs.m_index = index; return boundingFxs; } } //--------------------------------------------------------- bool FxSelection::isConnected() { if (m_selectedFxs.isEmpty()) return false; QList visitedFxs; visitFx(m_selectedFxs.at(0).getPointer(), visitedFxs); bool connected = true; QList::const_iterator it; TXsheet *xsh = m_xshHandle->getXsheet(); TFxSet *internalFxs = xsh->getFxDag()->getInternalFxs(); for (it = m_selectedFxs.begin(); it != m_selectedFxs.end(); it++) { TFx *selectedFx = it->getPointer(); TColumnFx *cfx = dynamic_cast(selectedFx); if (!cfx && !internalFxs->containsFx(selectedFx)) return false; TZeraryColumnFx *zfx = dynamic_cast(selectedFx); if (zfx) selectedFx = zfx->getZeraryFx(); connected = connected && visitedFxs.contains(selectedFx); } return connected; } //------------------------------------------------------- void FxSelection::visitFx(TFx *fx, QList &visitedFxs) { if (visitedFxs.contains(fx)) return; TZeraryColumnFx *zfx = dynamic_cast(fx); if (zfx) fx = zfx->getZeraryFx(); if (!canGroup(fx)) return; visitedFxs.append(fx); int i; for (i = 0; i < fx->getInputPortCount(); i++) { TFx *inputFx = fx->getInputPort(i)->getFx(); TZeraryColumnFx *onputZFx = dynamic_cast(inputFx); if (onputZFx) inputFx = onputZFx->getZeraryFx(); if (!inputFx) continue; bool canBeGrouped = !inputFx->getAttributes()->isGrouped() || (inputFx->getAttributes()->getEditingGroupId() == fx->getAttributes()->getEditingGroupId()); if (!visitedFxs.contains(inputFx) && isSelected(inputFx) && canBeGrouped) visitFx(inputFx, visitedFxs); } if (zfx) fx = zfx; if (fx->isZerary() && !zfx) { TXsheet *xsh = m_xshHandle->getXsheet(); int columnCount = xsh->getColumnCount(); int j; for (j = 0; j < columnCount; j++) { TZeraryColumnFx *zerary = dynamic_cast(xsh->getColumn(j)->getFx()); if (zerary && zerary->getZeraryFx() == fx) { fx = zerary; break; } } } for (i = 0; i < fx->getOutputConnectionCount(); i++) { TFx *outputFx = fx->getOutputConnection(i)->getOwnerFx(); if (!outputFx) continue; bool canBeGrouped = !outputFx->getAttributes()->isGrouped() || (outputFx->getAttributes()->getEditingGroupId() == fx->getAttributes()->getEditingGroupId()); if (!visitedFxs.contains(outputFx) && isSelected(outputFx) && canBeGrouped) visitFx(outputFx, visitedFxs); } } //------------------------------------------------------- bool FxSelection::areLinked(TFx *outFx, TFx *inFx) { int i; for (i = 0; i < outFx->getInputPortCount(); i++) { TFx *inputFx = outFx->getInputPort(i)->getFx(); if (inFx == inputFx) return true; } return false; }