// TnzCore includes #include "tsystem.h" #include "tstream.h" #include "tfilepath_io.h" #include "tfunctorinvoker.h" // TnzBase incudes #include "tunit.h" #include "tparamcontainer.h" #include "tparamset.h" #include "tmacrofx.h" #include "tparamchange.h" // TnzExt includes #include "ext/plasticskeleton.h" // TnzLib includes #include "toonz/tstageobjecttree.h" #include "toonz/txsheet.h" #include "toonz/txsheethandle.h" #include "toonz/fxdag.h" #include "toonz/txshzeraryfxcolumn.h" #include "toonz/tcolumnfx.h" #include "toonz/tfxhandle.h" #include "toonz/tobjecthandle.h" // TnzQt includes #include "toonzqt/functionviewer.h" #include "toonzqt/dvdialog.h" #include "toonzqt/gutil.h" #include "toonzqt/plasticvertexselection.h" #include "tw/stringtable.h" // Qt includes #include #include #include #include #include #include #include // for drag&drop #include #include #include #include "toonzqt/functiontreeviewer.h" //************************************************************************************* // ChannelGroup specialization definition //************************************************************************************* namespace { class ParamChannelGroup final : public FunctionTreeModel::ParamWrapper, public FunctionTreeModel::ChannelGroup { public: ParamChannelGroup(TParam *param, const std::wstring &fxId, std::string ¶mName); void refresh() override; void *getInternalPointer() const override; }; //============================================================================= class SkVDChannelGroup final : public FunctionTreeModel::ChannelGroup { public: StageObjectChannelGroup *m_stageObjectGroup; //!< Parent stage object group const QString *m_vxName; //!< The associated vertex name public: SkVDChannelGroup(const QString *vxName, StageObjectChannelGroup *stageGroup) : ChannelGroup(*vxName) , m_stageObjectGroup(stageGroup) , m_vxName(vxName) {} QString getShortName() const override { return m_stageObjectGroup->getShortName(); } QString getLongName() const override { return *m_vxName; } void *getInternalPointer() const override { return (void *)m_vxName; } static inline bool compareStr(const TreeModel::Item *item, const QString &str) { const QString &thisStr = static_cast(item)->getLongName(); return (QString::localeAwareCompare(thisStr, str) < 0); } QVariant data(int role) const override; }; } // namespace //============================================================================= // // ChannelGroup // //----------------------------------------------------------------------------- FunctionTreeModel::ChannelGroup::ChannelGroup(const QString &name) : m_name(name), m_showFilter(ShowAllChannels) {} //----------------------------------------------------------------------------- FunctionTreeModel::ChannelGroup::~ChannelGroup() {} //----------------------------------------------------------------------------- bool FunctionTreeModel::ChannelGroup::isActive() const { // Analyze children. If one is active, this is active too. int c, childCount = getChildCount(); for (c = 0; c != childCount; ++c) if (static_cast(getChild(c))->isActive()) return true; return false; } //----------------------------------------------------------------------------- bool FunctionTreeModel::ChannelGroup::isAnimated() const { // Same for the animated feature, this is animate if any of its children is. int c, childCount = getChildCount(); for (c = 0; c != childCount; ++c) if (static_cast(getChild(c))->isAnimated()) return true; return false; } //----------------------------------------------------------------------------- QVariant FunctionTreeModel::ChannelGroup::data(int role) const { if (role == Qt::DisplayRole) return getLongName(); else if (role == Qt::DecorationRole) { bool animated = isAnimated(); bool active = isActive(); if (active) { static QIcon folderAnimOpen(createQIcon("folder_anim_on", true)); static QIcon folderAnimClose(createQIcon("folder_anim", true)); static QIcon folderOpen(createQIcon("folder_on", true)); static QIcon folderClose(createQIcon("folder", true)); return animated ? isOpen() ? folderAnimOpen : folderAnimClose : isOpen() ? folderOpen : folderClose; } else { static QIcon folderAnimOpen(createQIcon("folder_anim_inactive_on", true)); static QIcon folderAnimClose(createQIcon("folder_anim_inactive", true)); static QIcon folderOpen(createQIcon("folder_inactive_on", true)); static QIcon folderClose(createQIcon("folder_inactive", true)); return animated ? isOpen() ? folderAnimOpen : folderAnimClose : isOpen() ? folderOpen : folderClose; } } else return Item::data(role); } //----------------------------------------------------------------------------- //! \todo This is \a not recursive - I guess it should be...? void FunctionTreeModel::ChannelGroup::applyShowFilter() { int i, itemCount = getChildCount(); for (i = 0; i < itemCount; i++) { FunctionTreeModel::Channel *channel = dynamic_cast(getChild(i)); /*--- ChannelGroupの内部も同じフィルタで更新する ---*/ if (!channel) { FunctionTreeModel::ChannelGroup *channelGroup = dynamic_cast(getChild(i)); if (!channelGroup) continue; channelGroup->setShowFilter(m_showFilter); continue; } bool showItem = (m_showFilter == ShowAllChannels) || channel->getParam()->hasKeyframes(); QModelIndex modelIndex = createIndex(); getModel()->setRowHidden(i, modelIndex, !showItem); if (!showItem) channel->setIsActive(false); } } //----------------------------------------------------------------------------- void FunctionTreeModel::ChannelGroup::setShowFilter(ShowFilter showFilter) { m_showFilter = showFilter; applyShowFilter(); } //----------------------------------------------------------------------------- QString FunctionTreeModel::ChannelGroup::getIdName() const { QString tmpName = QString(m_name); tmpName.remove(QChar(' '), Qt::CaseInsensitive); tmpName = tmpName.toLower(); FunctionTreeModel::ChannelGroup *parentGroup = dynamic_cast(getParent()); if (parentGroup) { return parentGroup->getIdName() + QString(".") + tmpName; } return tmpName; } //----------------------------------------------------------------------------- void FunctionTreeModel::ChannelGroup::setChildrenAllActive(bool active) { for (int i = 0; i < getChildCount(); i++) { // for Channel FunctionTreeModel::Channel *channel = dynamic_cast(getChild(i)); if (channel) { channel->setIsActive(active); continue; } // for ChannelGroup else { FunctionTreeModel::ChannelGroup *channelGroup = dynamic_cast(getChild(i)); if (channelGroup) { channelGroup->setChildrenAllActive(active); continue; } } } } //============================================================================= // // StageObjectChannelGroup // //----------------------------------------------------------------------------- StageObjectChannelGroup::StageObjectChannelGroup(TStageObject *stageObject) : m_stageObject(stageObject), m_plasticGroup() { m_stageObject->addRef(); } //----------------------------------------------------------------------------- StageObjectChannelGroup::~StageObjectChannelGroup() { m_stageObject->release(); } //----------------------------------------------------------------------------- QVariant StageObjectChannelGroup::data(int role) const { if (role == Qt::DisplayRole) { std::string name = (m_stageObject->getId().isTable()) ? FunctionTreeView::tr("Table").toStdString() : m_stageObject->getName(); std::string id = m_stageObject->getId().toString(); return (name == id) ? QString::fromStdString(name) : QString::fromStdString(id + " (" + name + ")"); } else if (role == Qt::ForegroundRole) { FunctionTreeModel *model = dynamic_cast(getModel()); if (!model) #if QT_VERSION >= 0x050000 return QColor(Qt::black); #else return Qt::black; #endif FunctionTreeView *view = dynamic_cast(model->getView()); if (!view || !model->getCurrentStageObject()) #if QT_VERSION >= 0x050000 return QColor(Qt::black); #else return Qt::black; #endif TStageObjectId currentId = model->getCurrentStageObject()->getId(); return m_stageObject->getId() == currentId ? view->getViewer()->getCurrentTextColor() : view->getTextColor(); } else return ChannelGroup::data(role); } //----------------------------------------------------------------------------- QString StageObjectChannelGroup::getShortName() const { return QString::fromStdString(m_stageObject->getName()); } //----------------------------------------------------------------------------- QString StageObjectChannelGroup::getLongName() const { return QString::fromStdString(m_stageObject->getFullName()); } //----------------------------------------------------------------------------- QString StageObjectChannelGroup::getIdName() const { return QString::fromStdString(m_stageObject->getId().toString()).toLower(); } //============================================================================= // // FxChannelGroup // //----------------------------------------------------------------------------- FxChannelGroup::FxChannelGroup(TFx *fx) : m_fx(fx) { if (m_fx) m_fx->addRef(); } //----------------------------------------------------------------------------- FxChannelGroup::~FxChannelGroup() { if (m_fx) m_fx->release(); m_fx = 0; } //----------------------------------------------------------------------------- QString FxChannelGroup::getShortName() const { return QString::fromStdWString(m_fx->getFxId()); } //----------------------------------------------------------------------------- QString FxChannelGroup::getLongName() const { return QString::fromStdWString(m_fx->getFxId()); } //----------------------------------------------------------------------------- QVariant FxChannelGroup::data(int role) const { if (role == Qt::DecorationRole) { bool isAnimated = false; TParamContainer *paramContainer = m_fx->getParams(); int i; for (i = 0; i < paramContainer->getParamCount(); i++) { if (!paramContainer->getParam(i)->hasKeyframes()) continue; isAnimated = true; break; } bool isOneChildActive = false; for (i = 0; i < getChildCount(); i++) { FunctionTreeModel::Channel *channel = dynamic_cast(getChild(i)); if (!channel || !channel->isActive()) continue; isOneChildActive = true; break; } if (isOneChildActive) { static QIcon folderAnimOpen(createQIcon("folder_anim_on", true)); static QIcon folderAnimClose(createQIcon("folder_anim", true)); static QIcon folderOpen(createQIcon("folder_on", true)); static QIcon folderClose(createQIcon("folder", true)); return isAnimated ? isOpen() ? folderAnimOpen : folderAnimClose : isOpen() ? folderOpen : folderClose; } else { static QIcon folderAnimOpen(createQIcon("folder_anim_inactive_on", true)); static QIcon folderAnimClose(createQIcon("folder_anim_inactive", true)); static QIcon folderOpen(createQIcon("folder_inactive_on", true)); static QIcon folderClose(createQIcon("folder_inactive", true)); return isAnimated ? isOpen() ? folderAnimOpen : folderAnimClose : isOpen() ? folderOpen : folderClose; } } else if (role == Qt::DisplayRole) { std::wstring name = m_fx->getName(); std::wstring id = m_fx->getFxId(); if (name == id) return QString::fromStdWString(name); else return QString::fromStdWString(id + L" (" + name + L")"); } else if (role == Qt::ForegroundRole) { FunctionTreeModel *model = dynamic_cast(getModel()); if (!model) #if QT_VERSION >= 0x050000 return QColor(Qt::black); #else return Qt::black; #endif FunctionTreeView *view = dynamic_cast(model->getView()); if (!view) #if QT_VERSION >= 0x050000 return QColor(Qt::black); #else return Qt::black; #endif TFx *currentFx = model->getCurrentFx(); return m_fx == currentFx ? view->getViewer()->getCurrentTextColor() : view->getTextColor(); } else return Item::data(role); } //----------------------------------------------------------------------------- QString FxChannelGroup::getIdName() const { return QString::fromStdWString(m_fx->getFxId()).toLower(); } //----------------------------------------------------------------------------- void FxChannelGroup::refresh() { TMacroFx *macroFx = dynamic_cast(m_fx); int i, childrenCount = getChildCount(); for (i = 0; i < childrenCount; ++i) { FunctionTreeModel::ParamWrapper *wrap = dynamic_cast(getChild(i)); assert(wrap); TParam *param = 0; { TParamContainer *paramContainer = 0; if (macroFx) { const std::wstring &fxId = wrap->getFxId(); TFx *subFx = macroFx->getFxById(fxId); if (!subFx) continue; paramContainer = subFx->getParams(); } else paramContainer = m_fx->getParams(); param = paramContainer->getParam(wrap->getParam()->getName()); } assert(param); wrap->setParam(param); ParamChannelGroup *paramGroup = dynamic_cast(wrap); if (paramGroup) paramGroup->refresh(); } } //============================================================================= // // ParamChannelGroup // //----------------------------------------------------------------------------- ParamChannelGroup::ParamChannelGroup(TParam *param, const std::wstring &fxId, std::string ¶mName) : ParamWrapper(param, fxId) , ChannelGroup( param->hasUILabel() ? QString::fromStdString(param->getUILabel()) : QString::fromStdWString(TStringTable::translate(paramName))) {} //----------------------------------------------------------------------------- void *ParamChannelGroup::getInternalPointer() const { return this->m_param.getPointer(); } //----------------------------------------------------------------------------- void ParamChannelGroup::refresh() { TParamSet *paramSet = dynamic_cast(m_param.getPointer()); if (!paramSet) return; int c, childrenCount = getChildCount(); for (c = 0; c < childrenCount; ++c) { FunctionTreeModel::ParamWrapper *wrap = dynamic_cast(getChild(c)); assert(wrap); TParamP currentParam = wrap->getParam(); assert(currentParam); int p = paramSet->getParamIdx(wrap->getParam()->getName()); assert(p < paramSet->getParamCount()); TParamP param = paramSet->getParam(p); wrap->setParam(param); } } //============================================================================= // // SkVDChannelGroup // //----------------------------------------------------------------------------- QVariant SkVDChannelGroup::data(int role) const { if (role == Qt::ForegroundRole) { // Check whether current selection is a PlasticVertex one - in case, paint // it selection color // if this group refers to current vertex FunctionTreeModel *model = dynamic_cast(getModel()); if (!model) #if QT_VERSION >= 0x050000 return QColor(Qt::black); #else return Qt::black; #endif FunctionTreeView *view = dynamic_cast(model->getView()); if (!view || !model->getCurrentStageObject()) #if QT_VERSION >= 0x050000 return QColor(Qt::black); #else return Qt::black; #endif if (PlasticVertexSelection *vxSel = dynamic_cast(TSelection::getCurrent())) if (TStageObject *obj = model->getCurrentStageObject()) if (obj == m_stageObjectGroup->m_stageObject) if (const SkDP &sd = obj->getPlasticSkeletonDeformation()) { int vIdx = *vxSel; if (vIdx >= 0 && sd->skeleton(vxSel->skeletonId())->vertex(vIdx).name() == getLongName()) return view->getViewer()->getCurrentTextColor(); } return view->getTextColor(); } else return ChannelGroup::data(role); } //============================================================================= // // Channel // //----------------------------------------------------------------------------- FunctionTreeModel::Channel::Channel(FunctionTreeModel *model, TDoubleParam *param, std::string paramNamePref, std::wstring fxId) : ParamWrapper(param, fxId) , m_model(model) , m_group(0) , m_isActive(false) , m_paramNamePref(paramNamePref) {} //----------------------------------------------------------------------------- FunctionTreeModel::Channel::~Channel() { m_model->onChannelDestroyed(this); if (m_isActive) getParam()->removeObserver(this); } //----------------------------------------------------------------------------- void *FunctionTreeModel::Channel::getInternalPointer() const { return this->m_param.getPointer(); } //----------------------------------------------------------------------------- bool FunctionTreeModel::Channel::isAnimated() const { return m_param->hasKeyframes(); } //----------------------------------------------------------------------------- QVariant FunctionTreeModel::Channel::data(int role) const { if (role == Qt::DecorationRole) { QPixmap pixmap(10, 10); QColor color; QString name = getShortName(); std::string strName = name.toStdString(); if (name == "X") color = QColor("firebrick"); else if (name == "Y") color = QColor("limegreen"); else if (name == "Z") color = QColor("deepskyblue"); else if (name == "SO") color = QColor("hotpink"); else if (name == "Rotation") color = QColor("darkorchid"); else if (name == "Scale") color = QColor("gold"); else if (name == "Scale H") color = QColor("gold"); else if (name == "Scale V") color = QColor("gold"); else if (name == "Shear H") color = QColor("darkorange"); else if (name == "Shear V") color = QColor("darkorange"); else if (name == "posPath") color = QColor("darksalmon"); else color = QColor("darkcyan"); if (!isActive()) color = QColor("dimgray"); else if (!m_param->hasKeyframes()) color.setAlpha(100); pixmap.fill(color); QIcon param(pixmap); return param; } else if (role == Qt::DisplayRole) { if (m_param->hasUILabel()) { return QString::fromStdString(m_param->getUILabel()); } std::string name = m_paramNamePref + m_param->getName(); std::wstring translatedName = TStringTable::translate(name); if (m_fxId.size() > 0) return QString::fromStdWString(translatedName + L" (" + m_fxId + L")"); return QString::fromStdWString(translatedName); } else if (role == Qt::ForegroundRole) { // 130221 iwasawa FunctionTreeView *view = dynamic_cast(m_model->m_view); if (!view) #if QT_VERSION >= 0x050000 return QColor(Qt::black); #else return Qt::black; #endif return (isCurrent()) ? view->getViewer()->getCurrentTextColor() : view->getTextColor(); } else return TreeModel::Item::data(role); } //----------------------------------------------------------------------------- QString FunctionTreeModel::Channel::getShortName() const { if (m_param->hasUILabel()) { return QString::fromStdString(m_param->getUILabel()); } std::string name = m_paramNamePref + m_param->getName(); std::wstring translatedName = TStringTable::translate(name); return QString::fromStdWString(translatedName); } //----------------------------------------------------------------------------- QString FunctionTreeModel::Channel::getLongName() const { QString name = getShortName(); if (getChannelGroup()) name = getChannelGroup()->getLongName() + " " + name; return name; } //----------------------------------------------------------------------------- void FunctionTreeModel::Channel::setParam(const TParamP ¶m) { if (param.getPointer() == m_param.getPointer()) return; TParamP oldParam = m_param; m_param = param; if (m_isActive) { oldParam->removeObserver(this); param->addObserver(this); } } //----------------------------------------------------------------------------- /*! in order to show the expression name in the tooltip */ QString FunctionTreeModel::Channel::getExprRefName() const { QString tmpName = QString(QString::fromStdWString( TStringTable::translate(m_paramNamePref + m_param->getName()))); /*--- stage * objectパラメータの場合、TableにあわせてtmpNameを代表的なExpression名にする---*/ StageObjectChannelGroup *stageGroup = dynamic_cast(m_group); if (stageGroup) { if (tmpName == "Y") tmpName = "y"; else if (tmpName == "X") tmpName = "x"; else if (tmpName == "Z") tmpName = "z"; else if (tmpName == "Rotation") tmpName = "rot"; else if (tmpName == "Scale H") tmpName = "sx"; else if (tmpName == "Scale V") tmpName = "sy"; else if (tmpName == "Shear H") tmpName = "shx"; else if (tmpName == "Shear V") tmpName = "shy"; else if (tmpName == "posPath") tmpName = "path"; else if (tmpName == "Scale") tmpName = "sc"; else tmpName = tmpName.toLower(); return stageGroup->getIdName() + QString(".") + tmpName; } // expression for fx parameters // see txsheetexpr.cpp for generation of actual tokens tmpName.remove(QChar(' '), Qt::CaseInsensitive); tmpName.remove(QChar('/')); tmpName.remove(QChar('-')); tmpName = tmpName.toLower(); FunctionTreeModel::ChannelGroup *parentGroup = dynamic_cast(getParent()); if (parentGroup) { return QString("fx.") + parentGroup->getIdName() + QString(".") + tmpName; } else return ""; } //----------------------------------------------------------------------------- void FunctionTreeModel::Channel::setIsActive(bool active) { if (active == m_isActive) return; m_isActive = active; m_model->refreshActiveChannels(); if (m_isActive) { getParam()->addObserver(this); /*--- これが最初にVisibleにしたChannelの場合 ---*/ if (!m_model->m_currentChannel) { setIsCurrent(true); m_model->emitCurveSelected(getParam()); } } else { getParam()->removeObserver(this); if (isCurrent()) { setIsCurrent(false); m_model->emitCurveSelected(0); } } m_model->emitDataChanged(this); } //----------------------------------------------------------------------------- bool FunctionTreeModel::Channel::isCurrent() const { return m_model->m_currentChannel == this; } //----------------------------------------------------------------------------- void FunctionTreeModel::Channel::setIsCurrent(bool current) { Channel *oldCurrent = m_model->m_currentChannel; if (current) { // this channel must become the current if (oldCurrent == this) return; // already it is: nothing to do m_model->m_currentChannel = this; // change the current fx if the FxChannelGroup is clicked FxChannelGroup *fxGroup = dynamic_cast(m_group); if (fxGroup && m_model->getFxHandle()) { m_model->getFxHandle()->setFx(fxGroup->getFx()); } // or, change the current object if the stageObjectChannelGroup is clicked else { StageObjectChannelGroup *stageObjectGroup = dynamic_cast(m_group); if (stageObjectGroup && m_model->getObjectHandle()) { m_model->getObjectHandle()->setObjectId( stageObjectGroup->getStageObject()->getId()); } } // the current channel must be active if (!m_isActive) { m_isActive = true; m_model->refreshActiveChannels(); getParam()->addObserver(this); } // refresh the old current (if !=0) and the new one if (oldCurrent) m_model->emitDataChanged(oldCurrent); m_model->emitDataChanged(this); m_model->emitCurveSelected(getParam()); // scroll the column to ensure visible the current channel m_model->emitCurrentChannelChanged(this); } else { // this channel is not the current anymore if (oldCurrent != this) return; // it was not: nothing to do m_model->m_currentChannel = 0; // refresh the channel m_model->emitDataChanged(this); } } //----------------------------------------------------------------------------- bool FunctionTreeModel::Channel::isHidden() const { return getChannelGroup()->getShowFilter() == ChannelGroup::ShowAnimatedChannels && !getParam()->hasKeyframes(); } //----------------------------------------------------------------------------- void FunctionTreeModel::Channel::onChange(const TParamChange &ch) { m_model->onChange(ch); } //============================================================================= // // FunctionTreeModel // //----------------------------------------------------------------------------- FunctionTreeModel::FunctionTreeModel(FunctionTreeView *parent) : TreeModel(parent) , m_currentChannel(0) , m_stageObjects(0) , m_fxs(0) , m_currentStageObject(0) , m_currentFx(0) , m_paramsChanged(false) {} //----------------------------------------------------------------------------- FunctionTreeModel::~FunctionTreeModel() { // I must delete items here (not in TreeModel::~TreeModel()). // Channel::~Channel() refers to the model, which should be alive. setRootItem(0); if (m_currentFx) m_currentFx->release(); } //----------------------------------------------------------------------------- void FunctionTreeModel::refreshData(TXsheet *xsh) { m_activeChannels.clear(); Channel *currentChannel = m_currentChannel; beginRefresh(); { if (!getRootItem()) { setRootItem(new ChannelGroup("Root")); if (xsh) { getRootItem()->appendChild(m_stageObjects = new ChannelGroup(tr("Stage"))); getRootItem()->appendChild(m_fxs = new ChannelGroup(tr("FX"))); assert(getRootItem()->getChildCount() == 2); assert(getRootItem()->getChild(0) == m_stageObjects); assert(getRootItem()->getChild(1) == m_fxs); } } if (xsh) { refreshStageObjects(xsh); refreshFxs(xsh); } refreshActiveChannels(); } endRefresh(); if (m_currentChannel != currentChannel) emit curveSelected(0); } //----------------------------------------------------------------------------- void FunctionTreeModel::refreshStageObjects(TXsheet *xsh) { static const int channelIds[TStageObject::T_ChannelCount] = { TStageObject::T_X, TStageObject::T_Y, TStageObject::T_Z, TStageObject::T_SO, TStageObject::T_Path, TStageObject::T_Angle, TStageObject::T_ScaleX, TStageObject::T_ScaleY, TStageObject::T_Scale, TStageObject::T_ShearX, TStageObject::T_ShearY}; // Explicitly ordered // channels // Retrieve all (not-empty) root stage objects, and add them in the tree model QList newItems; TStageObjectTree *ptree = xsh->getStageObjectTree(); int i, iCount = ptree->getStageObjectCount(); for (i = 0; i < iCount; ++i) { TStageObject *pegbar = ptree->getStageObject(i); TStageObjectId id = pegbar->getId(); if (id.isColumn() && xsh->isColumnEmpty(id.getIndex())) continue; newItems.push_back(new StageObjectChannelGroup(pegbar)); } /*--- newItemsの中で、これまでのChildrenに無いものだけ m_stageObjectsの子に追加。既に有るものはnewChildrenから除外---*/ m_stageObjects->setChildren(newItems); // Add channels to the NEW stage entries (see the above call to setChildren()) iCount = newItems.size(); for (i = 0; i < iCount; ++i) { StageObjectChannelGroup *pegbarItem = dynamic_cast(newItems[i]); TStageObject *stageObject = pegbarItem->getStageObject(); // Add the standard stage object channels int j, jCount = TStageObject::T_ChannelCount; // making each channel of pegbar for (j = 0; j < jCount; ++j) { TDoubleParam *param = stageObject->getParam((TStageObject::Channel)channelIds[j]); Channel *channel = new Channel(this, param); pegbarItem->appendChild(channel); channel->setChannelGroup(pegbarItem); } pegbarItem->applyShowFilter(); } // As plastic deformations are stored in stage objects, refresh them if // necessary refreshPlasticDeformations(); } //----------------------------------------------------------------------------- void FunctionTreeModel::refreshFxs(TXsheet *xsh) { std::vector fxs; xsh->getFxDag()->getFxs(fxs); for (int i = 0; i < xsh->getColumnCount(); i++) { TXshZeraryFxColumn *zc = dynamic_cast(xsh->getColumn(i)); if (!zc) continue; fxs.push_back(zc->getZeraryColumnFx()->getZeraryFx()); } // sort items by fxId for (int j = 1; j < (int)fxs.size(); j++) { int index = j; while (index > 0 && QString::localeAwareCompare( QString::fromStdWString(fxs[index - 1]->getFxId()), QString::fromStdWString(fxs[index]->getFxId())) > 0) { std::swap(fxs[index - 1], fxs[index]); index = index - 1; } } QList newItems; int i; for (i = 0; i < (int)fxs.size(); i++) { TFx *fx = fxs[i]; if (!fx) continue; TParamContainer *params = fx->getParams(); bool hasChannel = false; int j; for (j = 0; j < params->getParamCount(); j++) if (0 != dynamic_cast(params->getParam(j)) || 0 != dynamic_cast(params->getParam(j)) || 0 != dynamic_cast(params->getParam(j)) || 0 != dynamic_cast(params->getParam(j))) { hasChannel = true; break; } if (hasChannel) newItems.push_back(new FxChannelGroup(fxs[i])); } m_fxs->setChildren(newItems); // Add channels to new fxs (only for those actually added: see setChildren()) for (i = 0; i < (int)newItems.size(); i++) { TreeModel::Item *item = newItems[i]; FxChannelGroup *fxItem = dynamic_cast(item); assert(fxItem); if (!fxItem) continue; TFx *fx = fxItem->getFx(); assert(fx); if (!fx) continue; TMacroFx *macroFx = dynamic_cast(fx); if (macroFx) { const std::vector ¯oFxs = macroFx->getFxs(); int j; for (j = 0; j < (int)macroFxs.size(); j++) { TParamContainer *params = macroFxs[j]->getParams(); addChannels(macroFxs[j].getPointer(), fxItem, params); } } else { TParamContainer *params = fx->getParams(); addChannels(fx, fxItem, params); } fxItem->applyShowFilter(); } } //----------------------------------------------------------------------------- void FunctionTreeModel::refreshPlasticDeformations() { // Refresh ALL stage object items (including OLD ones) int i, iCount = m_stageObjects->getChildCount(); for (i = 0; i < iCount; ++i) { // Add the eventual Plastic channels group StageObjectChannelGroup *stageItem = static_cast(m_stageObjects->getChild(i)); TStageObject *stageObject = stageItem->getStageObject(); const PlasticSkeletonDeformationP &sd = stageObject->getPlasticSkeletonDeformation(); FunctionTreeModel::ChannelGroup *&plasticGroup = stageItem->m_plasticGroup; if (sd || plasticGroup) { if (!plasticGroup) { // Add a group plasticGroup = new ChannelGroup(tr("Plastic Skeleton")); stageItem->appendChild(plasticGroup); } // Prepare each vertex deformation QList plasticItems; if (sd) { SkD::vd_iterator vdt, vdEnd; sd->vertexDeformations(vdt, vdEnd); for (; vdt != vdEnd; ++vdt) { const QString *str = (*vdt).first; QList::iterator it = std::lower_bound(plasticItems.begin(), plasticItems.end(), *str, SkVDChannelGroup::compareStr); plasticItems.insert(it, new SkVDChannelGroup(str, stageItem)); } // Add the channel corresponding to the skeleton id { Channel *skelIdsChannel = new Channel(this, sd->skeletonIdsParam().getPointer()); plasticItems.insert(plasticItems.begin(), skelIdsChannel); skelIdsChannel->setChannelGroup(plasticGroup); } } if (plasticItems.empty()) plasticGroup->setIsOpen(false); // Add the vertex deformations group (this needs to be done BEFORE adding // the actual // channels, seemingly due to a limitation in the TreeModel // implementation) plasticGroup->setChildren(plasticItems); int pi, piCount = plasticItems.size(); // NOTE: plasticItems now stores only PART for (pi = 0; pi != piCount; ++pi) // of the specified items - those considered { // 'new' by internal pointer comparison... SkVDChannelGroup *vxGroup = dynamic_cast(plasticItems[pi]); if (!vxGroup) continue; SkVD *skvd = sd->vertexDeformation(vxGroup->ChannelGroup::getShortName()); for (int k = 0; k < SkVD::PARAMS_COUNT; ++k) { // Add channel in the vertex deformation Channel *channel = new Channel(this, skvd->m_params[k].getPointer()); channel->setChannelGroup(vxGroup); vxGroup->appendChild(channel); } vxGroup->applyShowFilter(); } plasticGroup->applyShowFilter(); } } } //----------------------------------------------------------------------------- void FunctionTreeModel::addParameter(ChannelGroup *group, const std::string &prefixString, const std::wstring &fxId, TParam *param) { if (TDoubleParam *dp = dynamic_cast(param)) { Channel *channel = new Channel(this, dp, prefixString, fxId); group->appendChild(channel); channel->setChannelGroup(group); } else if (dynamic_cast(param) || dynamic_cast(param) || dynamic_cast(param)) { TParamSet *paramSet = dynamic_cast(param); assert(paramSet); std::string paramName = prefixString + param->getName(); ChannelGroup *paramChannel = new ParamChannelGroup(param, fxId, paramName); group->appendChild(paramChannel); TPixelParam *pixParam = dynamic_cast(param); int p, paramCount = paramSet->getParamCount(); for (p = 0; p != paramCount; ++p) { TDoubleParam *dp = dynamic_cast(paramSet->getParam(p).getPointer()); if (!dp || (pixParam && !pixParam->isMatteEnabled() && p == paramCount - 1)) continue; Channel *channel = new Channel(this, dp, "", fxId); paramChannel->appendChild(channel); channel->setChannelGroup(group); } } } //----------------------------------------------------------------------------- void FunctionTreeModel::addChannels(TFx *fx, ChannelGroup *groupItem, TParamContainer *params) { FxChannelGroup *fxItem = static_cast(groupItem); std::wstring fxId = L""; TMacroFx *macro = dynamic_cast(fxItem->getFx()); if (macro) fxId = fx->getFxId(); const std::string ¶mNamePref = fx->getFxType() + "."; int p, pCount = params->getParamCount(); for (p = 0; p != pCount; ++p) addParameter(fxItem, paramNamePref, fxId, params->getParam(p)); } //----------------------------------------------------------------------------- void FunctionTreeModel::addActiveChannels(TreeModel::Item *item) { assert(item); if (Channel *channel = dynamic_cast(item)) { if (channel->isActive()) m_activeChannels.push_back(channel); } else for (int i = 0; i < item->getChildCount(); i++) addActiveChannels(item->getChild(i)); } //----------------------------------------------------------------------------- void FunctionTreeModel::refreshActiveChannels() { m_activeChannels.clear(); if (m_stageObjects) addActiveChannels(m_stageObjects); if (m_fxs) addActiveChannels(m_fxs); } //----------------------------------------------------------------------------- void FunctionTreeModel::onChannelDestroyed(Channel *channel) { if (channel == m_currentChannel) m_currentChannel = 0; } //----------------------------------------------------------------------------- FunctionTreeModel::Channel *FunctionTreeModel::getActiveChannel( int index) const { if (index < 0 || index >= (int)m_activeChannels.size()) return 0; else return m_activeChannels[index]; } //----------------------------------------------------------------------------- int FunctionTreeModel::getColumnIndexByCurve(TDoubleParam *param) const { for (int i = 0; i < (int)m_activeChannels.size(); i++) { if (m_activeChannels[i]->getParam() == param) return i; } return -1; } //----------------------------------------------------------------------------- FunctionTreeModel::ChannelGroup *FunctionTreeModel::getStageObjectChannel( int index) const { if (index < 0 || index >= (int)m_stageObjects->getChildCount()) return 0; else return dynamic_cast( m_stageObjects->getChild(index)); } //----------------------------------------------------------------------------- FunctionTreeModel::ChannelGroup *FunctionTreeModel::getFxChannel( int index) const { if (index < 0 || index >= (int)m_fxs->getChildCount()) return 0; else return dynamic_cast( m_fxs->getChild(index)); } //----------------------------------------------------------------------------- void FunctionTreeModel::onChange(const TParamChange &tpc) { if (!m_paramsChanged) { m_paramsChanged = true; struct Func final : public TFunctorInvoker::BaseFunctor { FunctionTreeModel *m_obj; // Use a copy of 'TParamChange' since callers declare // and free this value on the stack, // so we can't ensure its valid later on when the notifier executes. const TParamChange m_tpc; Func(FunctionTreeModel *obj, const TParamChange *tpc) : m_obj(obj), m_tpc(*tpc) {} void operator()() override { m_obj->onParamChange(m_tpc.m_dragging); } }; QMetaObject::invokeMethod(TFunctorInvoker::instance(), "invoke", Qt::QueuedConnection, Q_ARG(void *, new Func(this, &tpc))); } } //----------------------------------------------------------------------------- void FunctionTreeModel::onParamChange(bool isDragging) { m_paramsChanged = false; emit curveChanged(isDragging); } //----------------------------------------------------------------------------- void FunctionTreeModel::resetAll() { #if QT_VERSION >= 0x050000 beginResetModel(); #endif m_activeChannels.clear(); TreeModel::Item *root_item = getRootItem(); setRootItem_NoFree(NULL); m_stageObjects = 0; m_fxs = 0; #if QT_VERSION < 0x050000 reset(); #endif beginRefresh(); refreshActiveChannels(); endRefresh(); // postpone until after refresh, // since its members are used for reference. delete root_item; m_currentChannel = 0; #if QT_VERSION >= 0x050000 endResetModel(); #endif } //----------------------------------------------------------------------------- void FunctionTreeModel::setCurrentFx(TFx *fx) { TZeraryColumnFx *zcfx = dynamic_cast(fx); if (zcfx) fx = zcfx->getZeraryFx(); if (fx != m_currentFx) { if (fx) fx->addRef(); if (m_currentFx) m_currentFx->release(); m_currentFx = fx; } } //----------------------------------------------------------------------------- void FunctionTreeModel::applyShowFilters() { // WARNING: This is implemented BAD - notice that the get*() functions below // DO NOT ACTUALLY RETURN CHANNELS, but rather the child // ChannelGROUPS! // // This means that these show filters are presumably applied only to // the FIRST LEVEL OF PARAMETERS...! if (m_stageObjects) { int so, soCount = m_stageObjects->getChildCount(); for (so = 0; so != soCount; ++so) getStageObjectChannel(so)->applyShowFilter(); } if (m_fxs) { int fx, fxCount = m_fxs->getChildCount(); for (fx = 0; fx != fxCount; ++fx) getFxChannel(fx)->applyShowFilter(); } } //----------------------------------------------------------------------------- void FunctionTreeModel::addParameter(TParam *parameter, const TFilePath &folder) { struct locals { static void locateExistingRoot(ChannelGroup *&root, TFilePath &fp) { std::wstring firstName; TFilePath tempFp; while (!fp.isEmpty()) { // Get the path's first name fp.split(firstName, tempFp); // Search a matching channel group in root's children int c, cCount = root->getChildCount(); for (c = 0; c != cCount; ++c) { if (ChannelGroup *group = dynamic_cast(root->getChild(c))) { if (group->getShortName().toStdWString() == firstName) { root = group, fp = tempFp; break; } } } if (c == cCount) break; } } static void addFolders(ChannelGroup *&group, TFilePath &fp) { std::wstring firstName; TFilePath tempFp; while (!fp.isEmpty()) { fp.split(firstName, tempFp); ChannelGroup *newGroup = new ChannelGroup(QString::fromStdWString(firstName)); group->appendChild(newGroup); group = newGroup, fp = tempFp; } } }; // locals // Search for the furthest existing channel group chain leading to our folder ChannelGroup *group = static_cast(getRootItem()); assert(group); TFilePath path = folder; locals::locateExistingRoot(group, path); // If the chain interrupts prematurely, create new groups up to the required // folder if (!path.isEmpty()) locals::addFolders(group, path); assert(path.isEmpty()); // Add the parameter to the last group addParameter(group, "", L"", parameter); } //============================================================================= // // FunctionTreeView // //----------------------------------------------------------------------------- FunctionTreeView::FunctionTreeView(FunctionViewer *parent) : TreeView(parent) , m_scenePath() , m_clickedItem(0) , m_draggingChannel(0) , m_viewer(parent) { assert(parent); setModel(new FunctionTreeModel(this)); setObjectName("FunctionEditorTree"); setSelectionMode(QAbstractItemView::NoSelection); connect(this, SIGNAL(pressed(const QModelIndex &)), this, SLOT(onActivated(const QModelIndex &))); setFocusPolicy(Qt::NoFocus); setIndentation(14); setAlternatingRowColors(true); } //----------------------------------------------------------------------------- void FunctionTreeView::onActivated(const QModelIndex &index) { enum { NO_CHANNELS = 0x0, ACTIVE_CHANNELS = 0x1, INACTIVE_CHANNELS = 0x2, HAS_CHANNELS = ACTIVE_CHANNELS | INACTIVE_CHANNELS }; if (!index.isValid()) return; FunctionTreeModel *ftModel = (FunctionTreeModel *)model(); if (!ftModel) return; std::vector childChannels; std::vector channelGroups; // Scan for already active children - to decide whether to activate or // deactivate them int activeFlag = NO_CHANNELS; TreeModel::Item *item = static_cast(index.internalPointer()); if (item) { int c, cCount = item->getChildCount(); for (c = 0; c != cCount; ++c) { FunctionTreeModel::Channel *channel = dynamic_cast(item->getChild(c)); if (!channel) { FunctionTreeModel::ChannelGroup *channelGroup = dynamic_cast(item->getChild(c)); if (!channelGroup) continue; channelGroups.push_back(channelGroup); continue; } if (channel->isHidden()) continue; childChannels.push_back(channel); activeFlag |= (channel->isActive() ? ACTIVE_CHANNELS : INACTIVE_CHANNELS); } } // Open the item (ie show children) if it was closed AND not all its children // were active bool someInactiveChannels = (activeFlag != ACTIVE_CHANNELS); if (someInactiveChannels && !isExpanded(index)) { setExpanded(index, true); ftModel->onExpanded(index); } if (item) { if (!childChannels.empty()) { // Activate child channels if there is some inactive channel - deactivate // otherwise int c, cCount = childChannels.size(); for (c = 0; c != cCount; ++c) childChannels[c]->setIsActive(someInactiveChannels); for (int i = 0; i < (int)channelGroups.size(); i++) channelGroups[i]->setChildrenAllActive(someInactiveChannels); update(); } else { // There was no child channel. Try to activate children groups. int c, cCount = item->getChildCount(); for (c = 0; c != cCount; ++c) onActivated(item->getChild(c)->createIndex()); } } } //----------------------------------------------------------------------------- void FunctionTreeView::mouseDoubleClickEvent(QMouseEvent *event) { emit(fit()); } //----------------------------------------------------------------------------- void FunctionTreeView::onClick(TreeModel::Item *item, const QPoint &itemPos, QMouseEvent *e) { m_draggingChannel = 0; FunctionTreeModel::Channel *channel = dynamic_cast(item); FxChannelGroup *fxChannelGroup = dynamic_cast(item); StageObjectChannelGroup *stageObjectChannelGroup = dynamic_cast(item); m_clickedItem = channel; if (channel) { fxChannelGroup = dynamic_cast(channel->getParent()); stageObjectChannelGroup = dynamic_cast(channel->getParent()); int x = itemPos.x(); if (x >= 20) channel->setIsCurrent(true); else if (0 <= x && x < 20) { channel->setIsActive( (e->button() == Qt::RightButton) ? true : !channel->isActive()); update(); } } if (fxChannelGroup) { TFx *fx = fxChannelGroup->getFx(); emit switchCurrentFx(fx); } if (stageObjectChannelGroup) { TStageObject *obj = stageObjectChannelGroup->getStageObject(); emit switchCurrentObject(obj); } } //----------------------------------------------------------------------------- void FunctionTreeView::onMidClick(TreeModel::Item *item, const QPoint &itemPos, QMouseEvent *e) { FunctionTreeModel::Channel *channel = dynamic_cast(item); if (channel && e->button() == Qt::MidButton) { m_draggingChannel = channel; m_dragStartPosition = e->pos(); } else m_draggingChannel = 0; } //----------------------------------------------------------------------------- void FunctionTreeView::onDrag(TreeModel::Item *item, const QPoint &itemPos, QMouseEvent *e) { // middle drag of the channel item can retrieve expression name if ((e->buttons() & Qt::MidButton) && m_draggingChannel && (e->pos() - m_dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) { QDrag *drag = new QDrag(this); QMimeData *mimeData = new QMimeData; mimeData->setText(m_draggingChannel->getExprRefName()); drag->setMimeData(mimeData); static const QPixmap cursorPixmap(":Resources/dragcursor_exp_text.png"); drag->setDragCursor(cursorPixmap, Qt::MoveAction); Qt::DropAction dropAction = drag->exec(); return; } FunctionTreeModel::Channel *channel = dynamic_cast(item); if (!channel || !m_clickedItem) return; // i0: item under the current cursor position // i1: clicked item QModelIndex i0 = channel->createIndex(), i1 = m_clickedItem->createIndex(); if (!i0.isValid() || !i1.isValid() || i0.parent() != i1.parent()) return; if (i0.row() > i1.row()) std::swap(i0, i1); FunctionTreeModel *md = static_cast(model()); bool active = m_clickedItem->isActive(); for (int row = i0.row(); row <= i1.row(); ++row) { if (isRowHidden(row, i0.parent())) continue; QModelIndex index = md->index(row, 0, i0.parent()); TreeModel::Item *chItem = static_cast(index.internalPointer()); FunctionTreeModel::Channel *ch = dynamic_cast(chItem); if (ch && ch->isActive() != active) { ch->setIsActive(active); update(); } } } //----------------------------------------------------------------------------- void FunctionTreeView::onRelease() { m_clickedItem = 0; } //----------------------------------------------------------------------------- void FunctionTreeView::openContextMenu(TreeModel::Item *item, const QPoint &globalPos) { if (FunctionTreeModel::Channel *channel = dynamic_cast(item)) openContextMenu(channel, globalPos); else if (FunctionTreeModel::ChannelGroup *group = dynamic_cast(item)) openContextMenu(group, globalPos); } //----------------------------------------------------------------------------- void FunctionTreeView::openContextMenu(FunctionTreeModel::Channel *channel, const QPoint &globalPos) { assert(channel); if (!m_viewer) return; QMenu menu; QAction saveCurveAction(tr("Save Curve"), 0); QAction loadCurveAction(tr("Load Curve"), 0); QAction exportDataAction(tr("Export Data"), 0); menu.addAction(&saveCurveAction); menu.addAction(&loadCurveAction); menu.addAction(&exportDataAction); QAction *action = menu.exec(globalPos); TDoubleParam *curve = channel->getParam(); if (action == &saveCurveAction) m_viewer->emitIoCurve((int)FunctionViewer::eSaveCurve, curve, ""); else if (action == &loadCurveAction) m_viewer->emitIoCurve((int)FunctionViewer::eLoadCurve, curve, ""); else if (action == &exportDataAction) m_viewer->emitIoCurve((int)FunctionViewer::eExportCurve, curve, channel->getLongName().toStdString()); } //----------------------------------------------------------------------------- void FunctionTreeView::openContextMenu(FunctionTreeModel::ChannelGroup *group, const QPoint &globalPos) { assert(group); QMenu menu; QAction showAnimateOnly(tr("Show Animated Only"), 0); QAction showAll(tr("Show All"), 0); menu.addAction(&showAnimateOnly); menu.addAction(&showAll); // execute menu QAction *action = menu.exec(globalPos); if (action != &showAll && action != &showAnimateOnly) return; FunctionTreeModel::ChannelGroup::ShowFilter showFilter = (action == &showAll) ? FunctionTreeModel::ChannelGroup::ShowAllChannels : FunctionTreeModel::ChannelGroup::ShowAnimatedChannels; expand(group->createIndex()); group->setShowFilter(showFilter); } //----------------------------------------------------------------------------- void FunctionTreeView::updateAll() { FunctionTreeModel *functionTreeModel = dynamic_cast(model()); assert(functionTreeModel); functionTreeModel->applyShowFilters(); update(); } //----------------------------------------------------------------------------- /*! show all the animated channels when the scene switched */ void FunctionTreeView::displayAnimatedChannels() { FunctionTreeModel *functionTreeModel = dynamic_cast(model()); assert(functionTreeModel); int i; for (i = 0; i < functionTreeModel->getStageObjectsChannelCount(); i++) functionTreeModel->getStageObjectChannel(i)->displayAnimatedChannels(); for (i = 0; i < functionTreeModel->getFxsChannelCount(); i++) functionTreeModel->getFxChannel(i)->displayAnimatedChannels(); update(); } //----------------------------------------------------------------------------- /*! show all the animated channels when the scene switched */ void FunctionTreeModel::ChannelGroup::displayAnimatedChannels() { int itemCount = getChildCount(); int i; for (i = 0; i < itemCount; i++) { FunctionTreeModel::Channel *channel = dynamic_cast(getChild(i)); if (!channel) { FunctionTreeModel::ChannelGroup *channelGroup = dynamic_cast(getChild(i)); if (!channelGroup) continue; channelGroup->displayAnimatedChannels(); continue; } channel->setIsActive(channel->getParam()->hasKeyframes()); } }