diff --git a/toonz/sources/include/tools/rasterselection.h b/toonz/sources/include/tools/rasterselection.h index 40a1513..113b7e8 100644 --- a/toonz/sources/include/tools/rasterselection.h +++ b/toonz/sources/include/tools/rasterselection.h @@ -143,6 +143,8 @@ Can be different from getSelectionBound() after a free deform transformation. */ void pasteSelection(); bool isTransformed(); + + bool isEditable(); }; #endif // RASTER_SELECTION_H diff --git a/toonz/sources/include/tools/strokeselection.h b/toonz/sources/include/tools/strokeselection.h index 0757b3a..bf50c68 100644 --- a/toonz/sources/include/tools/strokeselection.h +++ b/toonz/sources/include/tools/strokeselection.h @@ -66,6 +66,8 @@ public: void setSceneHandle(TSceneHandle *tsh) { m_sceneHandle = tsh; } + bool isEditable(); + public: // Commands diff --git a/toonz/sources/tnztools/imagegrouping.cpp b/toonz/sources/tnztools/imagegrouping.cpp index e6cd04c..43fc537 100644 --- a/toonz/sources/tnztools/imagegrouping.cpp +++ b/toonz/sources/tnztools/imagegrouping.cpp @@ -12,6 +12,7 @@ // TnzQt includes #include "toonzqt/tselectionhandle.h" #include "toonzqt/selectioncommandids.h" +#include "toonzqt/dvdialog.h" // TnzLib includes #include "toonz/tscenehandle.h" @@ -478,6 +479,13 @@ void TGroupCommand::group() { TVectorImage *vimg = (TVectorImage *)tool->getImage(true); if (!vimg) return; + + if (!m_sel->isEditable()) { + DVGui::error( + QObject::tr("The selection cannot be grouped. It is not editable.")); + return; + } + QMutexLocker lock(vimg->getMutex()); groupWithoutUndo(vimg, m_sel); TXshSimpleLevel *level = @@ -494,6 +502,13 @@ void TGroupCommand::enterGroup() { TVectorImage *vimg = (TVectorImage *)tool->getImage(true); if (!vimg) return; + + if (!m_sel->isEditable()) { + DVGui::error( + QObject::tr("The selection cannot be entered. It is not editable.")); + return; + } + int index = -1; for (int i = 0; i < (int)vimg->getStrokeCount(); i++) @@ -532,6 +547,13 @@ void TGroupCommand::ungroup() { if (!tool) return; TVectorImage *vimg = (TVectorImage *)tool->getImage(true); if (!vimg) return; + + if (!m_sel->isEditable()) { + DVGui::error( + QObject::tr("The selection cannot be ungrouped. It is not editable.")); + return; + } + QMutexLocker lock(vimg->getMutex()); ungroupWithoutUndo(vimg, m_sel); TXshSimpleLevel *level = @@ -687,6 +709,12 @@ void TGroupCommand::moveGroup(UCHAR moveType) { TVectorImage *vimg = (TVectorImage *)tool->getImage(true); if (!vimg) return; + if (!m_sel->isEditable()) { + DVGui::error( + QObject::tr("The selection cannot be moved. It is not editable.")); + return; + } + std::vector> selectedGroups = getSelectedGroups(vimg, m_sel); if (selectedGroups.empty()) return; diff --git a/toonz/sources/tnztools/rasterselection.cpp b/toonz/sources/tnztools/rasterselection.cpp index 886aee4..3b6b413 100644 --- a/toonz/sources/tnztools/rasterselection.cpp +++ b/toonz/sources/tnztools/rasterselection.cpp @@ -24,6 +24,10 @@ #include "toonz/toonzscene.h" #include "toonz/tcamera.h" #include "toonz/trasterimageutils.h" +#include "toonz/tcolumnhandle.h" +#include "toonz/tframehandle.h" +#include "toonz/txsheethandle.h" +#include "toonz/tstageobject.h" #include #include @@ -1003,6 +1007,9 @@ void RasterSelection::transform(const TAffine &affine) { void RasterSelection::makeFloating() { if (isEmpty()) return; if (!m_currentImage) return; + + if (!isEditable()) return; + m_floatingSelection = getImageFromSelection(m_currentImage, *this); m_originalfloatingSelection = m_floatingSelection->clone(); deleteSelectionWithoutUndo(m_currentImage, m_strokes); @@ -1050,6 +1057,12 @@ void RasterSelection::deleteSelection() { if (!m_currentImage) return; TTool::Application *app = TTool::getApplication(); TXshSimpleLevel *level = app->getCurrentLevel()->getSimpleLevel(); + if (!isEditable()) { + DVGui::error( + QObject::tr("The selection cannot be deleted. It is not editable.")); + return; + } + // we have to remove all undo transformation and the undo for the makeFloating // operation! if (isFloating()) { @@ -1163,6 +1176,12 @@ void RasterSelection::pasteSelection() { if (!image) return; + if (!isEditable()) { + DVGui::error( + QObject::tr("The selection cannot be pasted. It is not editable.")); + return; + } + m_currentImage = image; m_fid = tool->getCurrentFid(); @@ -1209,6 +1228,42 @@ bool RasterSelection::isTransformed() { return !m_affine.isIdentity(); } //----------------------------------------------------------------------------- +bool RasterSelection::isEditable() { + TTool::Application *app = TTool::getApplication(); + TXshSimpleLevel *level = app->getCurrentLevel()->getSimpleLevel(); + + TFrameHandle *frame = app->getCurrentFrame(); + bool filmstrip = frame->isEditingLevel(); + + if (level) { + if (level->isReadOnly()) return false; + + TFrameId frameId = app->getCurrentTool()->getTool()->getCurrentFid(); + if (level->isFrameReadOnly(frameId)) return false; + } + + if (!filmstrip) { + int colIndex = app->getCurrentTool()->getTool()->getColumnIndex(); + int rowIndex = frame->getFrame(); + if (app->getCurrentTool()->getTool()->isColumnLocked(colIndex)) + return false; + + TXsheet *xsh = app->getCurrentXsheet()->getXsheet(); + TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(colIndex)); + // Test for Mesh-deformed levels + const TStageObjectId &parentId = obj->getParent(); + if (parentId.isColumn() && obj->getParentHandle()[0] != 'H') { + TXshSimpleLevel *parentSl = + xsh->getCell(rowIndex, parentId.getIndex()).getSimpleLevel(); + if (parentSl && parentSl->getType() == MESH_XSHLEVEL) return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- + TRectD RasterSelection::getStrokesBound(std::vector strokes) const { int i; TRectD box = TRectD(); diff --git a/toonz/sources/tnztools/rasterselectiontool.cpp b/toonz/sources/tnztools/rasterselectiontool.cpp index cbc8113..8d22d41 100644 --- a/toonz/sources/tnztools/rasterselectiontool.cpp +++ b/toonz/sources/tnztools/rasterselectiontool.cpp @@ -10,6 +10,7 @@ #include "toonz/toonzimageutils.h" #include "toonzqt/tselectionhandle.h" #include "toonzqt/imageutils.h" +#include "toonz/txshlevelhandle.h" using namespace ToolUtils; using namespace DragSelectionTool; @@ -485,6 +486,8 @@ void RasterSelectionTool::setNewFreeDeformer() { TRasterImageP ri = (TRasterImageP)image; if (!ti && !ri) return; + if (!m_rasterSelection.isEditable()) return; + if (!isFloating()) m_rasterSelection.makeFloating(); m_freeDeformers.push_back( new RasterFreeDeformer(m_rasterSelection.getFloatingSelection())); @@ -563,6 +566,11 @@ void RasterSelectionTool::modifySelectionOnClick(TImageP image, m_selecting = true; } } + + TTool::getApplication() + ->getCurrentTool() + ->notifyToolChanged(); // Refreshes toolbar values + invalidate(); } @@ -598,6 +606,8 @@ void RasterSelectionTool::leftButtonDrag(const TPointD &pos, return; } if (m_dragTool) { + if (!m_rasterSelection.isEditable()) return; + m_dragTool->leftButtonDrag(pos, e); invalidate(); return; @@ -623,6 +633,11 @@ void RasterSelectionTool::leftButtonDrag(const TPointD &pos, m_selectingRect = rectD; m_bboxs.clear(); + + TTool::getApplication() + ->getCurrentTool() + ->notifyToolChanged(); // Refreshes toolbar values + invalidate(); } else if (m_strokeSelectionType.getValue() == FREEHAND_SELECTION) { freehandDrag(pos); @@ -646,6 +661,11 @@ void RasterSelectionTool::leftButtonDrag(const TPointD &pos, bool selectOverlappingStroke = (m_firstPos.x > pos.x); TRectD rect(m_firstPos, pos); m_selectingRect = rect; + + TTool::getApplication() + ->getCurrentTool() + ->notifyToolChanged(); // Refreshes toolbar values + invalidate(); } } @@ -663,6 +683,12 @@ void RasterSelectionTool::leftButtonUp(const TPointD &pos, m_shiftPressed = false; if (m_dragTool) { + if (!m_rasterSelection.isEditable()) { + delete m_dragTool; + m_dragTool = 0; + return; + } + m_dragTool->leftButtonUp(pos, e); delete m_dragTool; m_dragTool = 0; @@ -719,6 +745,10 @@ void RasterSelectionTool::leftButtonDoubleClick(const TPointD &pos, m_selecting = false; return; } + + TTool::getApplication() + ->getCurrentTool() + ->notifyToolChanged(); // Refreshes toolbar values } //----------------------------------------------------------------------------- @@ -878,6 +908,10 @@ void RasterSelectionTool::doOnActivate() { m_rasterSelection.selectNone(); m_noAntialiasing.setValue(NoAntialiasing); m_rasterSelection.setNoAntialiasing(m_noAntialiasing.getValue()); + + TTool::getApplication() + ->getCurrentTool() + ->notifyToolChanged(); // Refreshes toolbar values } //----------------------------------------------------------------------------- @@ -910,6 +944,10 @@ void RasterSelectionTool::onImageChanged() { if ((!ti && !ri) || image != m_rasterSelection.getCurrentImage()) m_rasterSelection.selectNone(); + + TTool::getApplication() + ->getCurrentTool() + ->notifyToolChanged(); // Refreshes toolbar values } //----------------------------------------------------------------------------- @@ -957,6 +995,8 @@ void RasterSelectionTool::onActivate() { //----------------------------------------------------------------------------- bool RasterSelectionTool::onPropertyChanged(std::string propertyName) { + if (!m_rasterSelection.isEditable()) return false; + if (SelectionTool::onPropertyChanged(propertyName)) return true; if (m_targetType & ToonzImage) { ModifySavebox = (int)(m_modifySavebox.getValue()); diff --git a/toonz/sources/tnztools/rasterselectiontool.h b/toonz/sources/tnztools/rasterselectiontool.h index bc02c7f..b29c29c 100644 --- a/toonz/sources/tnztools/rasterselectiontool.h +++ b/toonz/sources/tnztools/rasterselectiontool.h @@ -242,6 +242,8 @@ public: bool onPropertyChanged(std::string propertyName) override; bool getNoAntialiasingValue() { return m_noAntialiasing.getValue(); } + bool isSelectionEditable() { return m_rasterSelection.isEditable(); } + protected: void updateTranslation() override; }; diff --git a/toonz/sources/tnztools/selectiontool.cpp b/toonz/sources/tnztools/selectiontool.cpp index f2a8caa..b6f04fc 100644 --- a/toonz/sources/tnztools/selectiontool.cpp +++ b/toonz/sources/tnztools/selectiontool.cpp @@ -935,6 +935,8 @@ void SelectionTool::updateAction(TPointD pos, const TMouseEvent &e) { } else if (m_leftButtonMousePressed) return; + if (!isSelectionEditable()) return; + FourPoints bbox = getBBox(); double pixelSize = getPixelSize(); @@ -1225,6 +1227,8 @@ void SelectionTool::drawCommandHandle(const TImage *image) { if (m_dragTool) m_dragTool->draw(); + if (!isSelectionEditable()) return; + double pixelSize = getPixelSize(); if (!isLevelType() && !isSelectedFramesType()) { TPointD c = getCenter() + TPointD(-pixelSize, +pixelSize); diff --git a/toonz/sources/tnztools/selectiontool.h b/toonz/sources/tnztools/selectiontool.h index f1f3c8a..ac0d5e3 100644 --- a/toonz/sources/tnztools/selectiontool.h +++ b/toonz/sources/tnztools/selectiontool.h @@ -457,6 +457,8 @@ public: // returns true if the pressed key is recognized and processed. bool isEventAcceptable(QEvent *e) override; + + virtual bool isSelectionEditable() { return true; } }; #endif // SELECTIONTOOL_INCLUDED diff --git a/toonz/sources/tnztools/strokeselection.cpp b/toonz/sources/tnztools/strokeselection.cpp index 6a80c28..dabd937 100644 --- a/toonz/sources/tnztools/strokeselection.cpp +++ b/toonz/sources/tnztools/strokeselection.cpp @@ -29,6 +29,9 @@ #include "toonz/tstageobject.h" #include "toonz/toonzscene.h" #include "toonz/sceneproperties.h" +#include "toonz/tframehandle.h" +#include "toonz/txsheethandle.h" +#include "toonz/tstageobject.h" // TnzCore includes #include "tthreadmessage.h" @@ -442,6 +445,12 @@ void StrokeSelection::removeEndpoints() { if (!m_vi) return; if (m_indexes.empty()) return; + if (!isEditable()) { + DVGui::error( + QObject::tr("The selection cannot be updated. It is not editable.")); + return; + } + std::vector> undoData; m_vi->findRegions(); @@ -462,7 +471,6 @@ void StrokeSelection::removeEndpoints() { m_updateSelectionBBox = false; } - //============================================================================= // // selectAll @@ -475,7 +483,7 @@ void StrokeSelection::selectAll() { int sCount = int(m_vi->getStrokeCount()); for (int s = 0; s < sCount; ++s) { - m_indexes.insert(s); + m_indexes.insert(s); } StrokeSelection *selection = dynamic_cast( @@ -495,6 +503,12 @@ void StrokeSelection::deleteStrokes() { TTool *tool = TTool::getApplication()->getCurrentTool()->getTool(); if (!tool) return; + if (!isEditable()) { + DVGui::error( + QObject::tr("The selection cannot be deleted. It is not editable.")); + return; + } + bool isSpline = tool->getApplication()->getCurrentObject()->isSpline(); TUndo *undo; if (isSpline) @@ -542,6 +556,12 @@ void StrokeSelection::copy() { void StrokeSelection::paste() { TTool *tool = TTool::getApplication()->getCurrentTool()->getTool(); if (!tool) return; + if (!isEditable()) { + DVGui::error( + QObject::tr("The selection cannot be pasted. It is not editable.")); + return; + } + if (TTool::getApplication()->getCurrentObject()->isSpline()) { const StrokesData *stData = dynamic_cast( QApplication::clipboard()->mimeData()); @@ -597,6 +617,12 @@ void StrokeSelection::cut() { TTool *tool = TTool::getApplication()->getCurrentTool()->getTool(); if (!tool) return; + if (!isEditable()) { + DVGui::error( + QObject::tr("The selection cannot be deleted. It is not editable.")); + return; + } + bool isSpline = tool->getApplication()->getCurrentObject()->isSpline(); TUndo *undo; if (isSpline) @@ -728,3 +754,39 @@ void StrokeSelection::changeColorStyle(int styleIndex) { tool->notifyImageChanged(); TUndoManager::manager()->add(undo); } + +//----------------------------------------------------------------------------- + +bool StrokeSelection::isEditable() { + TTool::Application *app = TTool::getApplication(); + TXshSimpleLevel *level = app->getCurrentLevel()->getSimpleLevel(); + + TFrameHandle *frame = app->getCurrentFrame(); + bool filmstrip = frame->isEditingLevel(); + + if (level) { + if (level->isReadOnly()) return false; + + TFrameId frameId = app->getCurrentTool()->getTool()->getCurrentFid(); + if (level->isFrameReadOnly(frameId)) return false; + } + + if (!filmstrip) { + int colIndex = app->getCurrentTool()->getTool()->getColumnIndex(); + int rowIndex = frame->getFrame(); + if (app->getCurrentTool()->getTool()->isColumnLocked(colIndex)) + return false; + + TXsheet *xsh = app->getCurrentXsheet()->getXsheet(); + TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(colIndex)); + // Test for Mesh-deformed levels + const TStageObjectId &parentId = obj->getParent(); + if (parentId.isColumn() && obj->getParentHandle()[0] != 'H') { + TXshSimpleLevel *parentSl = + xsh->getCell(rowIndex, parentId.getIndex()).getSimpleLevel(); + if (parentSl && parentSl->getType() == MESH_XSHLEVEL) return false; + } + } + + return true; +} diff --git a/toonz/sources/tnztools/tool.cpp b/toonz/sources/tnztools/tool.cpp index 3f19d27..a1eafe9 100644 --- a/toonz/sources/tnztools/tool.cpp +++ b/toonz/sources/tnztools/tool.cpp @@ -770,6 +770,7 @@ bool TTool::isColumnLocked(int columnIndex) const { if (columnIndex < 0) return false; TXsheet *xsh = getXsheet(); TXshColumn *column = xsh->getColumn(columnIndex); + if (!column) return false; return column->isLocked(); } @@ -884,7 +885,7 @@ QString TTool::updateEnabled(int rowIndex, int columnIndex) { // Check against unplaced columns (not in filmstrip mode) if (column && !filmstrip) { - if (column->isLocked()) + if (column->isLocked() && m_name != T_Selection) return (enable(false), QObject::tr("The current column is locked.")); else if (!column->isCamstandVisible()) @@ -971,7 +972,8 @@ QString TTool::updateEnabled(int rowIndex, int columnIndex) { if (parentId.isColumn() && obj->getParentHandle()[0] != 'H') { TXshSimpleLevel *parentSl = xsh->getCell(rowIndex, parentId.getIndex()).getSimpleLevel(); - if (parentSl && parentSl->getType() == MESH_XSHLEVEL) + if (parentSl && parentSl->getType() == MESH_XSHLEVEL && + m_name != T_Selection) return ( enable(false), QObject::tr( @@ -980,7 +982,7 @@ QString TTool::updateEnabled(int rowIndex, int columnIndex) { } // Check TTool::ImageType tools - if (toolType == TTool::LevelWriteTool) { + if (toolType == TTool::LevelWriteTool && m_name != T_Selection) { // Check level against read-only status if (sl->isFrameReadOnly(getCurrentFid())) return (enable(false), @@ -993,7 +995,8 @@ QString TTool::updateEnabled(int rowIndex, int columnIndex) { sl->getPath().getType() == "gif" || sl->getPath().getType() == "mp4" || sl->getPath().getType() == "webm" || - sl->is16BitChannelLevel() || // Inherited by previous implementation. + sl->is16BitChannelLevel() || // Inherited by previous + // implementation. // Could be fixed? sl->getProperties()->getBpp() == 1) // Black & White images. Again, could be fixed? diff --git a/toonz/sources/tnztools/tooloptionscontrols.cpp b/toonz/sources/tnztools/tooloptionscontrols.cpp index fbda3a5..06335a3 100644 --- a/toonz/sources/tnztools/tooloptionscontrols.cpp +++ b/toonz/sources/tnztools/tooloptionscontrols.cpp @@ -681,7 +681,7 @@ void ToolOptionCombo::doOnActivated(int index) { onActivated(index); setCurrentIndex(index); // for updating the cursor - if (m_toolHandle) m_toolHandle->notifyToolChanged(); + if (m_toolHandle) m_toolHandle->notifyToolChanged(); return; } @@ -1649,7 +1649,8 @@ void SelectionScaleField::onChange(TMeasuredValue *fld, bool addToUndo) { //----------------------------------------------------------------------------- void SelectionScaleField::updateStatus() { - if (!m_tool || (m_tool->isSelectionEmpty() && !m_tool->isLevelType())) { + if (!m_tool || !m_tool->isSelectionEditable() || + (m_tool->isSelectionEmpty() && !m_tool->isLevelType())) { setValue(0); setDisabled(true); return; @@ -1702,7 +1703,8 @@ void SelectionRotationField::onChange(TMeasuredValue *fld, bool addToUndo) { //----------------------------------------------------------------------------- void SelectionRotationField::updateStatus() { - if (!m_tool || (m_tool->isSelectionEmpty() && !m_tool->isLevelType())) { + if (!m_tool || !m_tool->isSelectionEditable() || + (m_tool->isSelectionEmpty() && !m_tool->isLevelType())) { setValue(0); setDisabled(true); return; @@ -1763,7 +1765,8 @@ void SelectionMoveField::onChange(TMeasuredValue *fld, bool addToUndo) { //----------------------------------------------------------------------------- void SelectionMoveField::updateStatus() { - if (!m_tool || (m_tool->isSelectionEmpty() && !m_tool->isLevelType())) { + if (!m_tool || !m_tool->isSelectionEditable() || + (m_tool->isSelectionEmpty() && !m_tool->isLevelType())) { setValue(0); setDisabled(true); return; @@ -1824,7 +1827,8 @@ void ThickChangeField::onChange(TMeasuredValue *fld, bool addToUndo) { //----------------------------------------------------------------------------- void ThickChangeField::updateStatus() { - if (!m_tool || m_tool->m_deformValues.m_isSelectionModified || + if (!m_tool || !m_tool->isSelectionEditable() || + m_tool->m_deformValues.m_isSelectionModified || (m_tool->isSelectionEmpty() && !m_tool->isLevelType())) { setValue(0); setDisabled(true); diff --git a/toonz/sources/tnztools/vectorselectiontool.cpp b/toonz/sources/tnztools/vectorselectiontool.cpp index afe62c3..b77b843 100644 --- a/toonz/sources/tnztools/vectorselectiontool.cpp +++ b/toonz/sources/tnztools/vectorselectiontool.cpp @@ -1556,6 +1556,8 @@ void VectorSelectionTool::leftButtonDoubleClick(const TPointD &pos, void VectorSelectionTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) { if (m_dragTool) { + if (!m_strokeSelection.isEditable()) return; + m_dragTool->leftButtonDrag(pos, e); return; } @@ -1621,6 +1623,12 @@ void VectorSelectionTool::leftButtonUp(const TPointD &pos, m_shiftPressed = false; if (m_dragTool) { + if (!m_strokeSelection.isEditable()) { + delete m_dragTool; + m_dragTool = 0; + return; + } + m_dragTool->leftButtonUp(pos, e); delete m_dragTool; m_dragTool = 0; @@ -1827,7 +1835,7 @@ void VectorSelectionTool::computeBBox() { getFourPointsFromVectorImage(vi, selectedStyles(), maxThickness); m_bboxs.push_back(p); - m_centers.push_back(0.5 * (p.getP00() + p.getP11())); + m_centers.push_back(0.5 * (p.getP00() + p.getP11())); m_deformValues.m_maxSelectionThickness = maxThickness; } } @@ -1998,6 +2006,8 @@ TPropertyGroup *VectorSelectionTool::getProperties(int idx) { //----------------------------------------------------------------------------- bool VectorSelectionTool::onPropertyChanged(std::string propertyName) { + if (!m_strokeSelection.isEditable()) return false; + if (SelectionTool::onPropertyChanged(propertyName)) return true; if (propertyName == m_constantThickness.getName()) @@ -2180,6 +2190,8 @@ void VectorSelectionTool::updateAction(TPointD pos, const TMouseEvent &e) { SelectionTool::updateAction(pos, e); if (m_what != Outside || m_cursorId != ToolCursor::StrokeSelectCursor) return; + if (!m_strokeSelection.isEditable()) return; + FourPoints bbox = getBBox(); UINT index = 0; diff --git a/toonz/sources/tnztools/vectorselectiontool.h b/toonz/sources/tnztools/vectorselectiontool.h index 570a873..7e10b60 100644 --- a/toonz/sources/tnztools/vectorselectiontool.h +++ b/toonz/sources/tnztools/vectorselectiontool.h @@ -311,6 +311,8 @@ public: void setResetCenter(bool update) { m_resetCenter = update; } bool canResetCenter() { return m_resetCenter; } + bool isSelectionEditable() { return m_strokeSelection.isEditable(); } + protected: void onActivate() override; void onDeactivate() override;