#include "tools/tool.h" // TnzTools includes #include "tools/toolcommandids.h" #include "tools/toolhandle.h" #include "tools/cursors.h" #include "tools/tooloptions.h" // TnzQt includes #include "toonzqt/icongenerator.h" // TnzLib includes #include "toonzqt/menubarcommand.h" #include "toonz/txshsimplelevel.h" #include "toonz/txshleveltypes.h" #include "toonz/levelproperties.h" #include "toonz/toonzscene.h" #include "toonz/sceneproperties.h" #include "toonz/preferences.h" #include "toonz/tscenehandle.h" #include "toonz/txsheethandle.h" #include "toonz/tframehandle.h" #include "toonz/tcolumnhandle.h" #include "toonz/tobjecthandle.h" #include "toonz/tpalettehandle.h" #include "toonz/txshlevelhandle.h" #include "toonz/txshcell.h" #include "toonz/tstageobject.h" #include "toonz/tstageobjectspline.h" #include "toonz/tstageobjecttree.h" #include "toonz/dpiscale.h" #include "toonz/palettecontroller.h" // TnzCore includes #include "tvectorimage.h" #include "timagecache.h" #include "tstroke.h" #include "tcolorstyles.h" #include "ttoonzimage.h" #include "trasterimage.h" //***************************************************************************************** // Local namespace //***************************************************************************************** namespace { // Global variables typedef std::pair ToolKey; typedef std::map ToolTable; ToolTable *toolTable = 0; std::set *toolNames = 0; //=================================================================== // Local classes struct DummyTool final : public TTool { ToolType getToolType() const override { return TTool::LevelReadTool; } // Test level type ToolTargetType getTargetType() const { return TTool::NoTarget; } // Works on nothing int getCursorId() const override { return ToolCursor::ForbiddenCursor; } // Forbids everything DummyTool() : TTool("T_Dummy") {} } theDummyTool; //------------------------------------------------------------------- class ToolSelector { std::string m_toolName; public: ToolSelector(std::string toolName) : m_toolName(toolName) {} void selectTool() { TTool::Application *app = TTool::getApplication(); if (app) app->getCurrentTool()->setTool(QString::fromStdString(m_toolName)); } }; //=================================================================== // Local functions TFrameId getNewFrameId(TXshSimpleLevel *sl, int row) { TFrameId fid(row + 1); if (sl->isFid(fid)) { fid = TFrameId(fid.getNumber(), 'a'); while (fid.getLetter() < 'z' && sl->isFid(fid)) fid = TFrameId(fid.getNumber(), fid.getLetter() + 1); } return fid; } } // namespace //***************************************************************************************** // TTool static members //***************************************************************************************** TTool::Application *TTool::m_application = 0; std::set TTool::m_selectedFrames = std::set(); bool TTool::m_isLevelCreated = false; bool TTool::m_isFrameCreated = false; // m_cellsData // brutto brutto. fix quick & dirty del baco #6213 (undo con animation sheet) // bisogna ripensare la logica degli undo e del touchImage // m_cellsData viene inizializzato nel touchImage() in modalita' animation sheet // contiene una o due terne che rappresentano range di celle (dell'xsheet) // modificate dall'inserimento // di un nuovo frame: [r0,r1,type]. // type = 0 : vecchio (cella[r0-1]) => nuovo // type = 1 : vuoto => vecchio (cella[r0-1]) // type = 2 : vuoto => nuovo // cfr. il codice di TTool::touchImage() // ToolUtils::TToolUndo::removeLevelAndFrameIfNeeded() std::vector TTool::m_cellsData; //***************************************************************************************** // TTool implementation //***************************************************************************************** TTool::TTool(std::string name) : m_name(name) , m_viewer(0) , m_targetType(NoTarget) , m_enabled(true) , m_active(false) , m_picking(false) {} //------------------------------------------------------------------- TTool *TTool::getTool(std::string toolName, ToolTargetType targetType) { if (!toolTable) return 0; ToolTable::iterator it = toolTable->find(std::make_pair(toolName, targetType)); if (it == toolTable->end()) return 0; return it->second; } //----------------------------------------------------------------------------- void TTool::bind(int targetType) { m_targetType = targetType; if (!toolTable) toolTable = new ToolTable(); if (!toolNames) toolNames = new std::set(); std::string name = getName(); if (toolNames->count(name) == 0) { toolNames->insert(name); // Initialize with the dummy tool toolTable->insert( std::make_pair(std::make_pair(name, ToonzImage), &theDummyTool)); toolTable->insert( std::make_pair(std::make_pair(name, VectorImage), &theDummyTool)); toolTable->insert( std::make_pair(std::make_pair(name, RasterImage), &theDummyTool)); toolTable->insert( std::make_pair(std::make_pair(name, MeshImage), &theDummyTool)); ToolSelector *toolSelector = new ToolSelector(name); CommandManager::instance()->setHandler( name.c_str(), new CommandHandlerHelper( toolSelector, &ToolSelector::selectTool)); } if (targetType & ToonzImage) (*toolTable)[std::make_pair(name, ToonzImage)] = this; if (targetType & VectorImage) (*toolTable)[std::make_pair(name, VectorImage)] = this; if (targetType & RasterImage) (*toolTable)[std::make_pair(name, RasterImage)] = this; if (targetType & MeshImage) (*toolTable)[std::make_pair(name, MeshImage)] = this; } //----------------------------------------------------------------------------- ToolOptionsBox *TTool::createOptionsBox() { TPaletteHandle *currPalette = m_application->getPaletteController()->getCurrentLevelPalette(); ToolHandle *currTool = m_application->getCurrentTool(); return new GenericToolOptionsBox(0, this, currPalette, 0, currTool); } //----------------------------------------------------------------------------- double TTool::getPixelSize() const { return m_viewer ? m_viewer->getPixelSize() : 1.0; } //----------------------------------------------------------------------------- TXshCell TTool::getImageCell() { assert(m_application); TXshCell result; TFrameHandle *currentFrame = m_application->getCurrentFrame(); TXshLevelHandle *currentLevel = m_application->getCurrentLevel(); if (currentFrame->isEditingLevel()) { if (TXshLevel *xl = currentLevel->getLevel()) { if (TXshSimpleLevel *sl = xl->getSimpleLevel()) { result.m_level = xl; result.m_frameId = currentFrame->getFid(); } } } else { if (TXsheet *xsh = m_application->getCurrentXsheet()->getXsheet()) { if (!m_application->getCurrentObject()->isSpline()) { int row = currentFrame->getFrame(); int col = m_application->getCurrentColumn()->getColumnIndex(); result = xsh->getCell(row, col); } } } return result; } //----------------------------------------------------------------------------- TImage *TTool::getImage(bool toBeModified, int subsampling) { assert(m_application); if (m_application->getCurrentFrame()->isPlaying()) toBeModified = false; // In playback mode, you are not going to modify images // Probably useless - tools are disabled when playing... const TXshCell &cell = getImageCell(); if (cell.isEmpty()) { TObjectHandle *currentObject = m_application->getCurrentObject(); return currentObject->isSpline() ? currentObject->getSplineImage() : (TImage *)0; } else return cell.getImage(toBeModified, subsampling).getPointer(); } //----------------------------------------------------------------------------- TImage *TTool::touchImage() { if (!m_application) return 0; m_cellsData.clear(); m_isLevelCreated = false; m_isFrameCreated = false; Preferences *pref = Preferences::instance(); bool isAutoCreateEnabled = pref->isAutoCreateEnabled(); bool animationSheetEnabled = pref->isAnimationSheetEnabled(); TFrameHandle *currentFrame = m_application->getCurrentFrame(); TXshLevelHandle *currentLevel = m_application->getCurrentLevel(); if (currentFrame->isEditingLevel()) { // Editing level // no level => return 0 TXshLevel *xl = currentLevel->getLevel(); if (!xl) return 0; TXshSimpleLevel *sl = xl->getSimpleLevel(); if (!sl || sl->isEmpty()) return 0; TFrameId fid = currentFrame->getFid(); TImageP img = sl->getFrame(fid, true); if (!img) { // no drawing found if (sl->isSubsequence() || sl->isReadOnly() || !isAutoCreateEnabled) return 0; // create a new drawing img = sl->createEmptyFrame(); sl->setFrame(fid, img); currentLevel->notifyLevelChange(); m_isFrameCreated = true; } return img.getPointer(); } else { // editing xsheet if (m_application->getCurrentObject()->isSpline()) return 0; TSceneHandle *currentScene = m_application->getCurrentScene(); ToonzScene *scene = currentScene->getScene(); int row = currentFrame->getFrame(); int col = m_application->getCurrentColumn()->getColumnIndex(); if (col < 0) return 0; TXsheetHandle *currentXsheet = m_application->getCurrentXsheet(); TXsheet *xsh = currentXsheet->getXsheet(); if (!xsh) return 0; TXshCell cell = xsh->getCell(row, col); TXshSimpleLevel *sl = cell.getSimpleLevel(); if (sl != 0) { // current cell is not empty if (isAutoCreateEnabled && animationSheetEnabled && row > 0 && xsh->getCell(row - 1, col) == xsh->getCell(row, col)) { // animationSheet is enabled and the current cell is a "hold". We must // create a new drawing. // measure the hold length (starting from the current row) : r0-r1 int r0 = row, r1 = row; while (xsh->getCell(r1 + 1, col) == cell) r1++; // find the proper frameid (possibly addisng suffix, in order to avoid a // fid already used) TFrameId fid = getNewFrameId(sl, row); // create the new drawing TImageP img = sl->createEmptyFrame(); m_isFrameCreated = true; // insert the drawing in the level sl->setFrame(fid, img); // update the cell cell = TXshCell(sl, fid); // update the xsheet (change the current cell and possibly all the // following "hold") for (int r = r0; r <= r1; r++) xsh->setCell(r, col, cell); // notify currentXsheet->notifyXsheetChanged(); currentScene->notifyCastChange(); currentLevel->notifyLevelChange(); m_cellsData.push_back(r0); m_cellsData.push_back(r1); m_cellsData.push_back(0); } // we've found the image. return it. return cell.getImage(true).getPointer(); } // current cell is empty. if (!isAutoCreateEnabled) return 0; // get the column range int r0, r1; xsh->getCellRange(col, r0, r1); if (animationSheetEnabled && r0 <= r1) { // animation sheet enabled and not empty column. We must create a new // drawing in the column level and possibly add "holds" // find the last not-empty cell before the current one (a) and the first // after (b) int a = row - 1, b = row + 1; while (a >= r0 && xsh->getCell(a, col).isEmpty()) a--; while (b <= r1 && xsh->getCell(b, col).isEmpty()) b++; // find the level we must attach to if (a >= r0) { // there is a not-emtpy cell before the current one sl = xsh->getCell(a, col).getSimpleLevel(); } else if (b <= r1) { sl = xsh->getCell(b, col).getSimpleLevel(); } if (sl) { // note: sl should be always !=0 (the column is not empty) // if - for some reason - it is ==0 then we skip to the standard (i.e. // !animationSheetEnabled) beahviour // create the drawing // find the proper frameid (possibly addisng suffix, in order to avoid a // fid already used) TFrameId fid = getNewFrameId(sl, row); // create the new drawing TImageP img = sl->createEmptyFrame(); m_isFrameCreated = true; // insert the drawing in the level sl->setFrame(fid, img); // update the cell cell = TXshCell(sl, fid); xsh->setCell(row, col, cell); // create holds if (a >= r0) { // create a hold before : [a+1, row-1] TXshCell aCell = xsh->getCell(a, col); for (int i = a + 1; i < row; i++) xsh->setCell(i, col, aCell); m_cellsData.push_back(a + 1); m_cellsData.push_back(row - 1); m_cellsData.push_back(1); // vuoto => vecchio if (b <= r1 && xsh->getCell(b, col).getSimpleLevel() == sl) { // create also a hold after for (int i = row + 1; i < b; i++) xsh->setCell(i, col, cell); m_cellsData.push_back(row); m_cellsData.push_back(b - 1); m_cellsData.push_back(2); // vuoto => nuovo } else { m_cellsData.push_back(row); m_cellsData.push_back(row); m_cellsData.push_back(2); // vuoto => nuovo } } else if (b <= r1) { // create a hold after for (int i = row + 1; i < b; i++) xsh->setCell(i, col, cell); m_cellsData.push_back(row); m_cellsData.push_back(b - 1); m_cellsData.push_back(2); // vuoto => nuovo } } // notify & return currentXsheet->notifyXsheetChanged(); currentScene->notifyCastChange(); currentLevel->notifyLevelChange(); return cell.getImage(true).getPointer(); } if (row > 0 && xsh->getCell(row - 1, col).getSimpleLevel() != 0 && !animationSheetEnabled) { sl = xsh->getCell(row - 1, col).getSimpleLevel(); if (sl->getType() != OVL_XSHLEVEL || sl->getPath().getFrame() != TFrameId::NO_FRAME) { // la cella precedente contiene un drawing di un livello. animationSheet // e' disabilitato // creo un nuovo frame currentLevel->setLevel(sl); if (sl->isSubsequence() || sl->isReadOnly()) return 0; TFrameId fid = sl->index2fid(sl->getFrameCount()); TImageP img = sl->createEmptyFrame(); m_isFrameCreated = true; sl->setFrame(fid, img); cell = TXshCell(sl, fid); xsh->setCell(row, col, cell); currentXsheet->notifyXsheetChanged(); currentScene->notifyCastChange(); currentLevel->notifyLevelChange(); return img.getPointer(); } } // animation sheet disabled or empty column. autoCreate is enabled: we must // create a new level int levelType = pref->getDefLevelType(); TXshLevel *xl = scene->createNewLevel(levelType); sl = xl->getSimpleLevel(); m_isLevelCreated = true; // create the drawing TFrameId fid = animationSheetEnabled ? getNewFrameId(sl, row) : TFrameId(1); TImageP img = sl->createEmptyFrame(); m_isFrameCreated = true; sl->setFrame(fid, img); cell = TXshCell(sl, fid); xsh->setCell(row, col, cell); if (animationSheetEnabled) { m_cellsData.push_back(row); m_cellsData.push_back(row); m_cellsData.push_back(2); // vuoto => nuovo } currentXsheet->notifyXsheetChanged(); currentScene->notifyCastChange(); currentLevel->notifyLevelChange(); return img.getPointer(); } } //----------------------------------------------------------------------------- void TTool::updateToolsPropertiesTranslation() { ToolTable::iterator tt, tEnd(toolTable->end()); for (tt = toolTable->begin(); tt != tEnd; ++tt) tt->second->updateTranslation(); } //----------------------------------------------------------------------------- void TTool::invalidate(const TRectD &rect) { if (m_viewer) { if (rect.isEmpty()) m_viewer->GLInvalidateAll(); else { TPointD dpiScale(1, 1); TXshSimpleLevel *sl = getApplication()->getCurrentLevel()->getSimpleLevel(); if (sl) dpiScale = getCurrentDpiScale(sl, getCurrentFid()); m_viewer->GLInvalidateRect(getCurrentColumnMatrix() * TScale(dpiScale.x, dpiScale.y) * rect); } } } //----------------------------------------------------------------------------- int TTool::pick(const TPointD &p) { if (!m_viewer) return 0; m_picking = true; int ret = m_viewer->pick(p); m_picking = false; return ret; } //----------------------------------------------------------------------------- TXsheet *TTool::getXsheet() const { if (!m_application) return 0; return m_application->getCurrentXsheet()->getXsheet(); } //----------------------------------------------------------------------------- int TTool::getFrame() { if (!m_application) return 0; return m_application->getCurrentFrame()->getFrame(); } //----------------------------------------------------------------------------- int TTool::getColumnIndex() { if (!m_application) return 0; return m_application->getCurrentColumn()->getColumnIndex(); } //----------------------------------------------------------------------------- TStageObjectId TTool::getObjectId() { if (!m_application) return TStageObjectId(); return m_application->getCurrentObject()->getObjectId(); } //------------------------------------------------------------ TTool::Application *TTool::getApplication() { if (m_application == 0) assert(!"you MUST call the TTool::setApplication function in the main of the program!"); return m_application; } //----------------------------------------------------------------------------- /*! Notify change of current image: update icon and notify level change. If current object is a spline commit spline chenged. If current mode is EditingLevel touch current frame. */ void TTool::notifyImageChanged() { onImageChanged(); if (!m_application) return; m_application->getCurrentScene()->setDirtyFlag(true); if (m_application->getCurrentFrame()->isEditingLevel()) { TXshLevel *xl = m_application->getCurrentLevel()->getLevel(); if (!xl) return; TXshSimpleLevel *sl = xl->getSimpleLevel(); if (!sl) return; TFrameId fid = m_application->getCurrentFrame()->getFid(); sl->touchFrame(fid); // sl->setDirtyFlag(true); IconGenerator::instance()->invalidate(sl, fid); IconGenerator::instance()->invalidateSceneIcon(); } else { TXsheet *xsh = m_application->getCurrentXsheet()->getXsheet(); if (!xsh) return; TObjectHandle *currentObject = m_application->getCurrentObject(); if (currentObject->isSpline()) { m_application->getCurrentObject()->commitSplineChanges(); TStageObject *pegbar = xsh->getStageObject(currentObject->getObjectId()); IconGenerator::instance()->invalidate(pegbar->getSpline()); } else { int row = m_application->getCurrentFrame()->getFrame(); int col = m_application->getCurrentColumn()->getColumnIndex(); if (col < 0) return; TXshCell cell = xsh->getCell(row, col); TXshSimpleLevel *sl = cell.getSimpleLevel(); if (sl) { IconGenerator::instance()->invalidate(sl, cell.m_frameId); sl->touchFrame(cell.m_frameId); IconGenerator::instance()->invalidateSceneIcon(); } } } m_application->getCurrentLevel()->notifyLevelChange(); } //----------------------------------------------------------------------------- /*! Notify change of image in \b fid: update icon and notify level change. */ void TTool::notifyImageChanged(const TFrameId &fid) { onImageChanged(); if (!m_application) return; m_application->getCurrentScene()->setDirtyFlag(true); if (m_application->getCurrentFrame()->isEditingLevel()) { TXshLevel *xl = m_application->getCurrentLevel()->getLevel(); if (!xl) return; TXshSimpleLevel *sl = xl->getSimpleLevel(); if (!sl) return; sl->setDirtyFlag(true); IconGenerator::instance()->invalidate(sl, fid); IconGenerator::instance()->invalidateSceneIcon(); } else { int row = m_application->getCurrentFrame()->getFrame(); int col = m_application->getCurrentColumn()->getColumnIndex(); if (col < 0) return; TXsheet *xsh = m_application->getCurrentXsheet()->getXsheet(); if (!xsh) return; TXshCell cell = xsh->getCell(row, col); TXshSimpleLevel *sl = cell.getSimpleLevel(); if (sl) { IconGenerator::instance()->invalidate(sl, fid); IconGenerator::instance()->invalidateSceneIcon(); sl->setDirtyFlag(true); } } m_application->getCurrentLevel()->notifyLevelChange(); } //----------------------------------------------------------------------------- TFrameId TTool::getCurrentFid() const { if (!m_application) return TFrameId(); TFrameHandle *fh = m_application->getCurrentFrame(); if (fh->isEditingLevel()) return fh->getFid(); int row = m_application->getCurrentFrame()->getFrame(); int col = m_application->getCurrentColumn()->getColumnIndex(); TXshCell cell = m_application->getCurrentXsheet()->getXsheet()->getCell(row, col); if (cell.isEmpty()) return TFrameId::NO_FRAME; return cell.getFrameId(); } //----------------------------------------------------------------------------- TAffine TTool::getCurrentColumnMatrix() const { return getColumnMatrix(m_application->getCurrentColumn()->getColumnIndex()); } //----------------------------------------------------------------------------- TAffine TTool::getCurrentColumnParentMatrix() const { if (!m_application) return TAffine(); TFrameHandle *fh = m_application->getCurrentFrame(); if (fh->isEditingLevel()) return TAffine(); int frame = fh->getFrame(); int columnIndex = m_application->getCurrentColumn()->getColumnIndex(); TXsheet *xsh = m_application->getCurrentXsheet()->getXsheet(); TStageObjectId parentId = xsh->getStageObjectParent(TStageObjectId::ColumnId(columnIndex)); return xsh->getPlacement(parentId, frame); } //----------------------------------------------------------------------------- TAffine TTool::getCurrentObjectParentMatrix() const { if (!m_application) return TAffine(); TXsheet *xsh = m_application->getCurrentXsheet()->getXsheet(); int frame = m_application->getCurrentFrame()->getFrame(); TStageObjectId currentObjectId = m_application->getCurrentObject()->getObjectId(); if (currentObjectId == TStageObjectId::NoneId) return TAffine(); TStageObjectId parentId = xsh->getStageObjectParent(currentObjectId); if (parentId == TStageObjectId::NoneId) return TAffine(); else return xsh->getPlacement(parentId, frame); } //----------------------------------------------------------------------------- TAffine TTool::getColumnMatrix(int columnIndex) const { if (!m_application) return TAffine(); TFrameHandle *fh = m_application->getCurrentFrame(); if (fh->isEditingLevel()) return TAffine(); int frame = fh->getFrame(); TXsheet *xsh = m_application->getCurrentXsheet()->getXsheet(); TStageObjectId columnId = TStageObjectId::ColumnId(columnIndex); TAffine columnPlacement = xsh->getPlacement(columnId, frame); double columnZ = xsh->getZ(columnId, frame); TStageObjectId cameraId = xsh->getStageObjectTree()->getCurrentCameraId(); TStageObject *camera = xsh->getStageObject(cameraId); TAffine cameraPlacement = camera->getPlacement(frame); double cameraZ = camera->getZ(frame); TStageObject *object = xsh->getStageObject(columnId); TAffine placement; TStageObject::perspective(placement, cameraPlacement, cameraZ, columnPlacement, columnZ, object->getGlobalNoScaleZ()); return placement; } //----------------------------------------------------------------------------- TAffine TTool::getCurrentObjectParentMatrix2() const { TTool::Application *app = m_application; TFrameHandle *fh = app->getCurrentFrame(); if (fh->isEditingLevel()) return TAffine(); int frame = fh->getFrame(); TXsheet *xsh = app->getCurrentXsheet()->getXsheet(); TStageObjectId id = app->getCurrentObject()->getObjectId(); double objZ = xsh->getZ(id, frame); TStageObjectId parentId = xsh->getStageObjectParent(id); if (parentId == TStageObjectId::NoneId) return TAffine(); id = parentId; TAffine objPlacement = xsh->getPlacement(id, frame); TStageObjectId cameraId = xsh->getStageObjectTree()->getCurrentCameraId(); TStageObject *camera = xsh->getStageObject(cameraId); TAffine cameraPlacement = camera->getPlacement(frame); double cameraZ = camera->getZ(frame); TAffine placement; TStageObject::perspective(placement, cameraPlacement, cameraZ, objPlacement, objZ, 0); return placement; } //----------------------------------------------------------------------------- void TTool::updateMatrix() { assert(m_application); if (m_application->getCurrentObject()->isSpline()) setMatrix(getCurrentObjectParentMatrix2()); else setMatrix(getCurrentColumnMatrix()); } //----------------------------------------------------------------------------- void TTool::resetInputMethod() { if (m_viewer) m_viewer->resetInputMethod(); } //----------------------------------------------------------------------------- bool TTool::isColumnLocked(int columnIndex) const { if (columnIndex < 0) return false; TXsheet *xsh = getXsheet(); TXshColumn *column = xsh->getColumn(columnIndex); return column->isLocked(); } //----------------------------------------------------------------------------- QString TTool::updateEnabled() { // Disable every tool during playback if (m_application->getCurrentFrame()->isPlaying()) return (enable(false), QString()); // Release Generic tools at once int toolType = getToolType(); int targetType = getTargetType(); if (toolType == TTool::GenericTool) return (enable(true), QString()); // Retrieve vars and view modes TXsheet *xsh = m_application->getCurrentXsheet()->getXsheet(); int rowIndex = m_application->getCurrentFrame()->getFrame(); int columnIndex = m_application->getCurrentColumn()->getColumnIndex(); TXshColumn *column = (columnIndex >= 0) ? xsh->getColumn(columnIndex) : 0; TXshLevel *xl = m_application->getCurrentLevel()->getLevel(); TXshSimpleLevel *sl = xl ? xl->getSimpleLevel() : 0; int levelType = sl ? sl->getType() : NO_XSHLEVEL; TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(columnIndex)); bool spline = m_application->getCurrentObject()->isSpline(); bool filmstrip = m_application->getCurrentFrame()->isEditingLevel(); /*-- MultiLayerStylePickerONのときは、現状に関わらず使用可能 --*/ if (m_name == T_StylePicker && Preferences::instance()->isMultiLayerStylePickerEnabled()) return (enable(true), QString()); // Check against unplaced columns (not in filmstrip mode) if (column && !filmstrip) { if (column->isLocked()) return (enable(false), QObject::tr("The current column is locked.")); else if (column->getSoundColumn()) return (enable(false), QObject::tr("It is not possible to edit the audio column.")); else if (column->getSoundTextColumn()) return (enable(false), QObject::tr("It is not possible to edit the Magpie column.")); if (toolType == TTool::ColumnTool) { // Check column target if (column->getLevelColumn() && !(targetType & LevelColumns)) return ( enable(false), QObject::tr("The current tool cannot be used on a Level column.")); if (column->getMeshColumn() && !(targetType & MeshColumns)) return ( enable(false), QObject::tr("The current tool cannot be used on a Mesh column.")); } } // Check column tools if (toolType == TTool::ColumnTool) { if (filmstrip) return ( enable(false), QObject::tr("The current tool cannot be used in Level Strip mode.")); if ((!column || column->isEmpty()) && !(targetType & TTool::EmptyTarget)) return (enable(false), QString()); } // Check LevelRead & LevelWrite tools if (toolType & TTool::LevelTool) { // Check against splines if (spline) { return (targetType & Splines) ? (enable(true), QString()) : (enable(false), QObject::tr("The current tool cannot be " "used to edit a motion path.")); } // Check against empty levels if (!xl) return (targetType & EmptyTarget) ? (enable(true), QString()) : (enable(false), QString()); // Check against simple-level-edness if (!sl) return (enable(false), QObject::tr("The current level is not editable.")); // Does it // happen at // all btw? // Check against level types { if ((levelType == PLI_XSHLEVEL) && !(targetType & VectorImage)) return ( enable(false), QObject::tr("The current tool cannot be used on a Vector Level.")); if ((levelType == TZP_XSHLEVEL) && !(targetType & ToonzImage)) return ( enable(false), QObject::tr("The current tool cannot be used on a Toonz Level.")); if ((levelType == OVL_XSHLEVEL) && !(targetType & RasterImage)) return ( enable(false), QObject::tr("The current tool cannot be used on a Raster Level.")); if ((levelType == MESH_XSHLEVEL) && !(targetType & MeshImage)) return ( enable(false), QObject::tr("The current tool cannot be used on a Mesh Level.")); } // Check against impossibly traceable movements on the column if ((levelType & LEVELCOLUMN_XSHLEVEL) && !filmstrip) { // 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 ( enable(false), QObject::tr( "The current tool cannot be used on a mesh-deformed level")); } } // Check TTool::ImageType tools if (toolType == TTool::LevelWriteTool) { // Check level against read-only status if (sl->isReadOnly()) { const std::set &editableFrames = sl->getEditableRange(); TFrameId currentFid = getCurrentFid(); if (editableFrames.find(currentFid) == editableFrames.end()) return ( enable(false), QObject::tr( "The current frame is locked: any editing is forbidden.")); } // Check level type write support if (sl->getPath().getType() == "psd" || // We don't have the API to write psd files sl->is16BitChannelLevel() || // Inherited by previous implementation. // Could be fixed? sl->getProperties()->getBpp() == 1) // Black & White images. Again, could be fixed? return (enable(false), QObject::tr("The current level is not editable.")); } } return (enable(true), QString()); } //----------------------------------------------------------------------------- void TTool::setSelectedFrames(const std::set &selectedFrames) { m_selectedFrames = selectedFrames; onSelectedFramesChanged(); }