| |
| |
|
|
| #include "tconst.h" |
| #include "tundo.h" |
| |
| |
| #include "tfxattributes.h" |
| |
| |
| #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" |
| |
| |
| #include "toonzqt/schematicnode.h" |
| #include "toonzqt/fxschematicnode.h" |
| #include "toonzqt/selectioncommandids.h" |
| #include "fxdata.h" |
| |
| |
| #include <QApplication> |
| #include <QClipboard> |
| |
| #include "toonzqt/fxselection.h" |
| |
| namespace |
| { |
| bool canGroup(TFx *fx) |
| { |
| TXsheetFx *xfx = dynamic_cast<TXsheetFx *>(fx); |
| TOutputFx *ofx = dynamic_cast<TOutputFx *>(fx); |
| return (!xfx && !ofx); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| 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); |
| qSort(m_selectedColIndexes); |
| } |
| |
| |
| |
| 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<TZeraryColumnFx *>(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() |
| { |
| std::list<TFxP, std::allocator<TFxP>> 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() |
| { |
| |
| if (m_selectedFxs.size() >= 1 && m_selectedLinks.size() == 0 && m_selectedColIndexes.isEmpty()) |
| replacePasteSelection(); |
| |
| else if (m_selectedFxs.size() == 0 && m_selectedLinks.size() >= 1 && m_selectedColIndexes.isEmpty()) |
| insertPasteSelection(); |
| else { |
| QClipboard *clipboard = QApplication::clipboard(); |
| const FxsData *fxsData = dynamic_cast<const FxsData *>(clipboard->mimeData()); |
| if (!fxsData) |
| return; |
| QList<TFxP> fxs; |
| QMap<TFx *, int> zeraryFxColumnSize; |
| QList<TXshColumnP> columns; |
| fxsData->getFxs(fxs, zeraryFxColumnSize, columns); |
| if (fxs.isEmpty() && columns.isEmpty()) |
| return; |
| |
| |
| if (m_pastePosition == TConst::nowhere && m_schematicScene) { |
| SchematicSceneViewer *ssv = dynamic_cast<SchematicSceneViewer *>(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<const FxsData *>(clipboard->mimeData()); |
| |
| m_pastePosition = TConst::nowhere; |
| |
| if (!fxsData || !fxsData->isConnected()) |
| return false; |
| |
| if (m_selectedLinks.isEmpty()) |
| return true; |
| |
| |
| struct Auto { |
| bool m_destruct; |
| ~Auto() |
| { |
| if (m_destruct) |
| TUndoManager::manager()->endBlock(); |
| } |
| } auto_ = {false}; |
| |
| |
| |
| QList<TFxCommand::Link> selectedLinks(m_selectedLinks); |
| |
| int i, size = selectedLinks.size(); |
| for (i = 0; i < size; ++i) { |
| |
| QList<TFxP> fxs; |
| QMap<TFx *, int> zeraryFxColumnSize; |
| QList<TXshColumnP> columns; |
| |
| fxsData->getFxs(fxs, zeraryFxColumnSize, columns); |
| if (fxs.empty() && columns.empty()) |
| return true; |
| |
| 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<const FxsData *>(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<TFxP> selectedFxs(m_selectedFxs); |
| |
| int i, size = selectedFxs.size(); |
| for (i = 0; i < size; ++i) { |
| |
| QList<TFxP> fxs; |
| QMap<TFx *, int> zeraryFxColumnSize; |
| QList<TXshColumnP> columns; |
| |
| fxsData->getFxs(fxs, zeraryFxColumnSize, columns); |
| if (fxs.empty() && columns.empty()) |
| return true; |
| |
| 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<const FxsData *>(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<TFxP> selectedFxs(m_selectedFxs); |
| |
| int i, size = selectedFxs.size(); |
| for (i = 0; i < size; ++i) { |
| |
| QList<TFxP> fxs; |
| QMap<TFx *, int> zeraryFxColumnSize; |
| QList<TXshColumnP> columns; |
| |
| fxsData->getFxs(fxs, zeraryFxColumnSize, columns); |
| if (fxs.empty() && columns.empty()) |
| return true; |
| |
| 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<int> 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<int>::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; |
| FxSchematicNode *inputNode = dynamic_cast<FxSchematicNode *>(outputPort->getNode()); |
| FxSchematicNode *outputNode = dynamic_cast<FxSchematicNode *>(inputPort->getNode()); |
| FxGroupNode *groupNode = dynamic_cast<FxGroupNode *>(inputNode); |
| |
| if (!inputNode || !outputNode || (groupNode && groupNode->getOutputConnectionsCount() != 1)) |
| return boundingFxs; |
| if (dynamic_cast<TXsheetFx *>(outputNode->getFx())) { |
| if (!groupNode) |
| boundingFxs.m_inputFx = inputNode->getFx(); |
| else { |
| TFxSet *terminals = m_xshHandle->getXsheet()->getFxDag()->getTerminalFxs(); |
| QList<TFxP> 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)) { |
| |
| FxGroupNode *groupNode = dynamic_cast<FxGroupNode *>(outputNode); |
| assert(groupNode); |
| QList<TFx *> 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) |
| return boundingFxs; |
| |
| TFx *outputFx = fxs[0]; |
| |
| 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<FxSchematicNode *>(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<TFx *> visitedFxs; |
| visitFx(m_selectedFxs.at(0).getPointer(), visitedFxs); |
| bool connected = true; |
| QList<TFxP>::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<TColumnFx *>(selectedFx); |
| if (!cfx && !internalFxs->containsFx(selectedFx)) |
| return false; |
| TZeraryColumnFx *zfx = dynamic_cast<TZeraryColumnFx *>(selectedFx); |
| if (zfx) |
| selectedFx = zfx->getZeraryFx(); |
| connected = connected && visitedFxs.contains(selectedFx); |
| } |
| return connected; |
| } |
| |
| |
| |
| void FxSelection::visitFx(TFx *fx, QList<TFx *> &visitedFxs) |
| { |
| if (visitedFxs.contains(fx)) |
| return; |
| TZeraryColumnFx *zfx = dynamic_cast<TZeraryColumnFx *>(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<TZeraryColumnFx *>(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<TZeraryColumnFx *>(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; |
| } |
| |