#include "insertfxpopup.h"
// Tnz6 includes
#include "menubarcommandids.h"
#include "tapp.h"
// TnzQt includes
#include "toonzqt/menubarcommand.h"
#include "toonzqt/gutil.h"
#include "toonzqt/fxselection.h"
#include "toonzqt/tselectionhandle.h"
#include "toonzqt/pluginloader.h" // inter-module plugin loader accessor
// TnzLib includes
#include "toonz/tscenehandle.h"
#include "toonz/txsheethandle.h"
#include "toonz/tframehandle.h"
#include "toonz/tcolumnhandle.h"
#include "toonz/tfxhandle.h"
#include "toonz/toonzscene.h"
#include "toonz/txsheet.h"
#include "toonz/fxdag.h"
#include "toonz/tcolumnfx.h"
#include "toonz/txshlevelcolumn.h"
#include "toonz/tcolumnfxset.h"
#include "toonz/tstageobjecttree.h"
#include "toonz/txshzeraryfxcolumn.h"
#include "toonz/toonzfolders.h"
#include "toonz/scenefx.h"
#include "toonz/fxcommand.h"
#include "tw/stringtable.h"
// TnzBase includes
#include "tdoubleparam.h"
#include "tparamcontainer.h"
#include "tmacrofx.h"
#include "tfx.h"
#include "texternfx.h"
// TnzCore includes
#include "tsystem.h"
// Qt includes
#include <QPushButton>
#include <QTreeWidget>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QMenu>
#include <QContextMenuEvent>
#include <QMainWindow>
#include <QLineEdit>
#include <QLabel>
#include <memory>
using namespace DVGui;
//=============================================================================
namespace {
//-----------------------------------------------------------------------------
TFx *createFxByName(const std::string &fxId) {
if (fxId.find("_ext_") == 0) {
return TExternFx::create(fxId.substr(5));
}
if (fxId.find("_plg_") == 0) {
return PluginLoader::create_host(fxId);
}
return TFx::create(fxId);
}
//-----------------------------------------------------------------------------
TFx *createPresetFxByName(TFilePath path) {
const std::string &id = path.getParentDir().getName();
TFx *fx = createFxByName(id);
if (fx) {
TIStream is(path);
fx->loadPreset(is);
fx->setName(path.getWideName());
}
return fx;
}
//-----------------------------------------------------------------------------
// same as createMacroFxByPath() in addfxcontextmenu.cpp
TFx *createMacroFxByPath(TFilePath path) {
try {
TIStream is(path);
TPersist *p = 0;
is >> p;
TMacroFx *fx = dynamic_cast<TMacroFx *>(p);
if (!fx) return 0;
fx->setName(path.getWideName());
// Assign a unic ID to each fx in the macro!
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
if (!xsh) return fx;
FxDag *fxDag = xsh->getFxDag();
if (!fxDag) return fx;
std::vector<TFxP> fxs;
fxs = fx->getFxs();
QMap<std::wstring, std::wstring> oldNewId;
int i;
for (i = 0; i < fxs.size(); i++) {
std::wstring oldId = fxs[i]->getFxId();
fxDag->assignUniqueId(fxs[i].getPointer());
std::wstring newId = fxs[i]->getFxId();
oldNewId[oldId] = newId;
// changing the id of the internal effects of a macro breaks the links
// between the name of the port and the port to which it is linked :
// I have to change the names of the ports and remap them within the macro
int j;
for (j = 0; j < fx->getInputPortCount(); j++) {
QString inputName = QString::fromStdString(fx->getInputPortName(j));
if (inputName.endsWith(QString::fromStdWString(oldId))) {
QString newInputName = inputName;
newInputName.replace(QString::fromStdWString(oldId),
QString::fromStdWString(newId));
fx->renamePort(inputName.toStdString(), newInputName.toStdString());
}
}
}
return fx;
} catch (...) {
return 0;
}
}
} // anonymous namespace
//-----------------------------------------------------------------------------
//=============================================================================
// FxTree
//=============================================================================
void FxTree::displayAll(QTreeWidgetItem *item) {
int childCount = item->childCount();
for (int i = 0; i < childCount; ++i) {
displayAll(item->child(i));
}
item->setHidden(false);
item->setExpanded(false);
}
//-------------------------------------------------------------------
void FxTree::hideAll(QTreeWidgetItem *item) {
int childCount = item->childCount();
for (int i = 0; i < childCount; ++i) {
hideAll(item->child(i));
}
item->setHidden(true);
item->setExpanded(false);
}
//-------------------------------------------------------------------
void FxTree::searchItems(const QString &searchWord) {
// if search word is empty, show all items
if (searchWord.isEmpty()) {
int itemCount = topLevelItemCount();
for (int i = 0; i < itemCount; ++i) {
displayAll(topLevelItem(i));
}
update();
return;
}
// hide all items first
int itemCount = topLevelItemCount();
for (int i = 0; i < itemCount; ++i) {
hideAll(topLevelItem(i));
}
QList<QTreeWidgetItem *> foundItems =
findItems(searchWord, Qt::MatchContains | Qt::MatchRecursive, 0);
if (foundItems.isEmpty()) { // if nothing is found, do nothing but update
update();
return;
}
// for each item found, show it and show its parent
for (auto item : foundItems) {
while (item) {
item->setHidden(false);
item->setExpanded(true);
item = item->parent();
}
}
update();
}
//=============================================================================
/*! \class InsertFxPopup
\brief The InsertFxPopup class provides a dialog to browse fx
and add it to
current scene.
Inherits \b Dialog.
*/
InsertFxPopup::InsertFxPopup()
: Dialog(TApp::instance()->getMainWindow(), true, false, "InsertFx")
, m_folderIcon(QIcon())
, m_presetIcon(QIcon())
, m_fxIcon(QIcon()) {
setWindowTitle(tr("FX Browser"));
setModal(false);
setTopMargin(0);
setTopSpacing(0);
QHBoxLayout *searchLay = new QHBoxLayout();
QLineEdit *searchEdit = new QLineEdit(this);
searchLay->setMargin(0);
searchLay->setSpacing(5);
searchLay->addWidget(new QLabel(tr("Search:"), this), 0);
searchLay->addWidget(searchEdit);
addLayout(searchLay);
connect(searchEdit, SIGNAL(textChanged(const QString &)), this,
SLOT(onSearchTextChanged(const QString &)));
m_fxTree = new FxTree();
m_fxTree->setIconSize(QSize(21, 18));
m_fxTree->setColumnCount(1);
m_fxTree->header()->close();
m_fxTree->setObjectName("FxTreeView");
m_fxTree->setAlternatingRowColors(true);
m_presetIcon = createQIcon("folder_preset", true);
m_fxIcon = createQIcon("fx");
QList<QTreeWidgetItem *> fxItems;
TFilePath path = TFilePath(ToonzFolder::getProfileFolder() + "layouts" +
"fxs" + "fxs.lst");
m_presetFolder = TFilePath(ToonzFolder::getFxPresetFolder() + "presets");
loadFx(path);
loadMacro();
// add 'Plugins' directory
auto plugins =
new QTreeWidgetItem((QTreeWidget *)NULL, QStringList("Plugins"));
plugins->setIcon(0, createQIcon("folder", true));
m_fxTree->addTopLevelItem(plugins);
// create vendor / Fx
// send a special setup for the menu item
std::map<std::string, QTreeWidgetItem *> vendors =
PluginLoader::create_menu_items(
[&](QTreeWidgetItem *firstlevel_item) {
plugins->addChild(firstlevel_item);
firstlevel_item->setIcon(0, createQIcon("folder"));
},
[&](QTreeWidgetItem *secondlevel_item) {
secondlevel_item->setIcon(0, m_fxIcon);
});
m_fxTree->insertTopLevelItems(0, fxItems);
connect(m_fxTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)),
SLOT(onItemDoubleClicked(QTreeWidgetItem *, int)));
addWidget(m_fxTree);
QPushButton *insertBtn = new QPushButton(tr("Insert"), this);
insertBtn->setMinimumSize(65, 25);
insertBtn->setObjectName("PushButton_NoPadding");
connect(insertBtn, SIGNAL(clicked()), this, SLOT(onInsert()));
insertBtn->setDefault(true);
m_buttonLayout->addWidget(insertBtn);
QPushButton *addBtn = new QPushButton(tr("Add"), this);
addBtn->setMinimumSize(65, 25);
addBtn->setObjectName("PushButton_NoPadding");
connect(addBtn, SIGNAL(clicked()), this, SLOT(onAdd()));
m_buttonLayout->addWidget(addBtn);
QPushButton *replaceBtn = new QPushButton(tr("Replace"), this);
replaceBtn->setMinimumSize(65, 25);
replaceBtn->setObjectName("PushButton_NoPadding");
connect(replaceBtn, SIGNAL(clicked()), this, SLOT(onReplace()));
m_buttonLayout->addWidget(replaceBtn);
}
//-------------------------------------------------------------------
void InsertFxPopup::onSearchTextChanged(const QString &text) {
static bool busy = false;
if (busy) return;
busy = true;
m_fxTree->searchItems(text);
busy = false;
}
//-------------------------------------------------------------------
void InsertFxPopup::makeItem(QTreeWidgetItem *parent, std::string fxId) {
QTreeWidgetItem *fxItem = new QTreeWidgetItem(
(QTreeWidget *)0,
QStringList(QString::fromStdWString(TStringTable::translate(fxId))));
fxItem->setData(0, Qt::UserRole, QVariant(QString::fromStdString(fxId)));
parent->addChild(fxItem);
fxItem->setIcon(0, loadPreset(fxItem) ? m_presetIcon : m_fxIcon);
}
//-------------------------------------------------------------------
void InsertFxPopup::loadFolder(QTreeWidgetItem *parent) {
while (!m_is->eos()) {
std::string tagName;
if (m_is->matchTag(tagName)) {
// Found a sub-folder
QString folderName = QString::fromStdString(tagName);
std::unique_ptr<QTreeWidgetItem> folder(
new QTreeWidgetItem((QTreeWidget *)0, QStringList(folderName)));
folder->setIcon(0, createQIcon("folder", true));
loadFolder(folder.get());
m_is->closeChild();
if (folder->childCount()) {
if (parent)
parent->addChild(folder.release());
else
m_fxTree->addTopLevelItem(folder.release());
}
} else {
// Found an fx
std::string fxName;
*m_is >> fxName;
makeItem(parent, fxName);
}
}
}
//-------------------------------------------------------------------
bool InsertFxPopup::loadFx(TFilePath fp) {
TIStream is(fp);
if (!is) return false;
m_is = &is;
try {
std::string tagName;
if (m_is->matchTag(tagName) && tagName == "fxs") {
loadFolder(0);
m_is->closeChild();
}
} catch (...) {
m_is = 0;
return false;
}
m_is = 0;
return true;
}
//-------------------------------------------------------------------
bool InsertFxPopup::loadPreset(QTreeWidgetItem *item) {
QString str = item->data(0, Qt::UserRole).toString();
TFilePath presetsFilepath(m_presetFolder + str.toStdWString());
int i;
for (i = item->childCount() - 1; i >= 0; i--)
item->removeChild(item->child(i));
if (TFileStatus(presetsFilepath).isDirectory()) {
TFilePathSet presets = TSystem::readDirectory(presetsFilepath);
if (!presets.empty()) {
for (TFilePathSet::iterator it2 = presets.begin(); it2 != presets.end();
++it2) {
TFilePath presetPath = *it2;
QString name(presetPath.getName().c_str());
QTreeWidgetItem *presetItem =
new QTreeWidgetItem((QTreeWidget *)0, QStringList(name));
presetItem->setData(0, Qt::UserRole, QVariant(toQString(presetPath)));
item->addChild(presetItem);
presetItem->setIcon(0, m_fxIcon);
}
} else
return false;
} else
return false;
return true;
}
//-------------------------------------------------------------------
void InsertFxPopup::loadMacro() {
TFilePath fp = m_presetFolder + TFilePath("macroFx");
try {
if (TFileStatus(fp).isDirectory()) {
TFilePathSet macros = TSystem::readDirectory(fp);
if (macros.empty()) return;
QTreeWidgetItem *macroFolder =
new QTreeWidgetItem((QTreeWidget *)0, QStringList(tr("Macro")));
macroFolder->setData(0, Qt::UserRole, QVariant(toQString(fp)));
macroFolder->setIcon(0, createQIcon("folder", true));
m_fxTree->addTopLevelItem(macroFolder);
for (TFilePathSet::iterator it = macros.begin(); it != macros.end();
++it) {
TFilePath macroPath = *it;
QString name(macroPath.getName().c_str());
QTreeWidgetItem *macroItem =
new QTreeWidgetItem((QTreeWidget *)0, QStringList(name));
macroItem->setData(0, Qt::UserRole, QVariant(toQString(macroPath)));
macroItem->setIcon(0, m_fxIcon);
macroFolder->addChild(macroItem);
}
}
} catch (...) {
}
}
//-----------------------------------------------------------------------------
void InsertFxPopup::onItemDoubleClicked(QTreeWidgetItem *w, int c) {
if (w->childCount() == 0) // E' una foglia
onInsert();
}
//-----------------------------------------------------------------------------
void InsertFxPopup::onInsert() {
TFx *fx = createFx();
if (fx) {
TApp *app = TApp::instance();
TXsheetHandle *xshHandle = app->getCurrentXsheet();
QList<TFxP> fxs;
QList<TFxCommand::Link> links;
FxSelection *selection =
dynamic_cast<FxSelection *>(app->getCurrentSelection()->getSelection());
if (selection) {
fxs = selection->getFxs();
links = selection->getLinks();
}
TFxCommand::insertFx(fx, fxs, links, app,
app->getCurrentColumn()->getColumnIndex(),
app->getCurrentFrame()->getFrameIndex());
xshHandle->notifyXsheetChanged();
}
}
//-----------------------------------------------------------------------------
void InsertFxPopup::onAdd() {
TFx *fx = createFx();
if (fx) {
TApp *app = TApp::instance();
TXsheetHandle *xshHandle = app->getCurrentXsheet();
QList<TFxP> fxs;
FxSelection *selection =
dynamic_cast<FxSelection *>(app->getCurrentSelection()->getSelection());
if (selection) fxs = selection->getFxs();
TFxCommand::addFx(fx, fxs, app, app->getCurrentColumn()->getColumnIndex(),
app->getCurrentFrame()->getFrameIndex());
xshHandle->notifyXsheetChanged();
}
}
//-----------------------------------------------------------------------------
void InsertFxPopup::onReplace() {
TFx *fx = createFx();
if (fx) {
TApp *app = TApp::instance();
TXsheetHandle *xshHandle = app->getCurrentXsheet();
QList<TFxP> fxs;
FxSelection *selection =
dynamic_cast<FxSelection *>(app->getCurrentSelection()->getSelection());
if (selection) fxs = selection->getFxs();
TFxCommand::replaceFx(fx, fxs, app->getCurrentXsheet(),
app->getCurrentFx());
xshHandle->notifyXsheetChanged();
}
}
//-----------------------------------------------------------------------------
TFx *InsertFxPopup::createFx() {
TApp *app = TApp::instance();
ToonzScene *scene = app->getCurrentScene()->getScene();
TXsheet *xsh = scene->getXsheet();
QTreeWidgetItem *item = m_fxTree->currentItem();
if (item == NULL) return 0;
QString text = item->data(0, Qt::UserRole).toString();
if (text.isEmpty()) return 0;
TFx *fx;
TFilePath path = TFilePath(text.toStdWString());
if (TFileStatus(path).doesExist() &&
TFileStatus(path.getParentDir()).isDirectory()) {
std::string folder = path.getParentDir().getName();
if (folder == "macroFx") // Devo caricare una macro
fx = createMacroFxByPath(path);
else // Verifico se devo caricare un preset
{
folder = path.getParentDir().getParentDir().getName();
if (folder == "presets") // Devo caricare un preset
fx = createPresetFxByName(path);
}
} else
fx = createFxByName(text.toStdString());
if (fx)
return fx;
else
return 0;
}
//-----------------------------------------------------------------------------
void InsertFxPopup::showEvent(QShowEvent *) {
updatePresets();
connect(TApp::instance()->getCurrentFx(), SIGNAL(fxPresetSaved()),
SLOT(updatePresets()));
}
//-----------------------------------------------------------------------------
void InsertFxPopup::hideEvent(QHideEvent *e) {
disconnect(TApp::instance()->getCurrentFx(), SIGNAL(fxPresetSaved()), this,
SLOT(updatePresets()));
Dialog::hideEvent(e);
}
//-----------------------------------------------------------------------------
void InsertFxPopup::contextMenuEvent(QContextMenuEvent *event) {
QTreeWidgetItem *item = m_fxTree->currentItem();
QString itemRole = item->data(0, Qt::UserRole).toString();
TFilePath path = TFilePath(itemRole.toStdWString());
if (TFileStatus(path).doesExist() &&
TFileStatus(path.getParentDir()).isDirectory()) {
QMenu *menu = new QMenu(this);
std::string folder = path.getParentDir().getName();
if (folder == "macroFx") // Menu' macro
{
QAction *remove = new QAction(tr("Remove Macro FX"), menu);
connect(remove, SIGNAL(triggered()), this, SLOT(removePreset()));
menu->addAction(remove);
} else // Verifico se devo caricare un preset
{
folder = path.getParentDir().getParentDir().getName();
if (folder == "presets") // Menu' preset
{
QAction *remove = new QAction(tr("Remove Preset"), menu);
connect(remove, SIGNAL(triggered()), this, SLOT(removePreset()));
menu->addAction(remove);
}
}
menu->exec(event->globalPos());
}
}
//-------------------------------------------------------------------
void InsertFxPopup::updatePresets() {
int i;
for (i = 0; i < m_fxTree->topLevelItemCount(); i++) {
QTreeWidgetItem *folder = m_fxTree->topLevelItem(i);
TFilePath path =
TFilePath(folder->data(0, Qt::UserRole).toString().toStdWString());
if (folder->text(0).toStdString() == "Plugins") {
continue;
}
if (path.getName() == "macroFx") {
int j;
for (j = folder->childCount() - 1; j >= 0; j--)
folder->removeChild(folder->child(j));
m_fxTree->removeItemWidget(folder, 0);
delete folder;
} else if (path.getParentDir().getName() == "macroFx")
continue;
else
for (int i = 0; i < folder->childCount(); i++) {
bool isPresetLoaded = loadPreset(folder->child(i));
if (isPresetLoaded)
folder->child(i)->setIcon(0, m_presetIcon);
else
folder->child(i)->setIcon(0, m_fxIcon);
}
}
loadMacro();
update();
}
//-----------------------------------------------------------------------------
void InsertFxPopup::removePreset() {
QTreeWidgetItem *item = m_fxTree->currentItem();
QString itemRole = item->data(0, Qt::UserRole).toString();
TFilePath path = TFilePath(itemRole.toStdWString());
QString question = QString(
tr("Are you sure you want to delete %1?").arg(path.getName().c_str()));
int ret = DVGui::MsgBox(question, tr("Yes"), tr("No"), 1);
if (ret == 2 || ret == 0) return;
try {
TSystem::deleteFile(path);
} catch (...) {
error(QString(tr("It is not possible to delete %1.").arg(toQString(path))));
return;
}
m_fxTree->removeItemWidget(item, 0);
delete item;
TApp::instance()->getCurrentFx()->notifyFxPresetRemoved();
}
//=============================================================================
OpenPopupCommandHandler<InsertFxPopup> openInsertFxPopup(MI_InsertFx);