Blob Blame Raw


#include "toonzqt/studiopaletteviewer.h"

// TnzQt includes
#include "toonzqt/menubarcommand.h"
#include "toonzqt/paletteviewer.h"
#include "toonzqt/dvdialog.h"
#include "toonzqt/trepetitionguard.h"
#include "toonzqt/gutil.h"
#include "toonzqt/icongenerator.h"
#include "toonzqt/intfield.h"
#include "palettesscanpopup.h"
#include "palettedata.h"

// TnzLib includes
#include "toonz/studiopalettecmd.h"
#include "toonz/tpalettehandle.h"
#include "toonz/txshsimplelevel.h"
#include "toonz/toonzscene.h"
#include "toonz/sceneproperties.h"
#include "toonz/txsheethandle.h"
#include "toonz/txshlevelhandle.h"

// TnzCore includes
#include "tconvert.h"
#include "tundo.h"
#include "tsystem.h"

#include "../toonz/menubarcommandids.h"

// Qt includes
#include <QHeaderView>
#include <QContextMenuEvent>
#include <QMenu>
#include <QUrl>
#include <QPainter>
#include <QVBoxLayout>
#include <QToolBar>
#include <QInputDialog>
#include <QPushButton>
#include <QDrag>

#include <time.h>

using namespace std;
using namespace PaletteViewerGUI;
using namespace DVGui;

//=============================================================================
namespace
{
//-----------------------------------------------------------------------------
/*! Return true if path is in folder \b rootPath of \b StudioPalette.
*/
bool isInStudioPaletteFolder(TFilePath path, TFilePath rootPath)
{
	if (path.getType() != "tpl")
		return false;
	StudioPalette *studioPalette = StudioPalette::instance();
	std::vector<TFilePath> childrenPath;
	studioPalette->getChildren(childrenPath, rootPath);
	int i;
	for (i = 0; i < (int)childrenPath.size(); i++) {
		if (path == childrenPath[i])
			return true;
		else if (isInStudioPaletteFolder(path, childrenPath[i]))
			return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
/*! Return true if path is in a \b StudioPalette folder.
*/
bool isInStudioPalette(TFilePath path)
{
	if (path.getType() != "tpl")
		return false;
	StudioPalette *studioPalette = StudioPalette::instance();
	if (isInStudioPaletteFolder(path, studioPalette->getLevelPalettesRoot()))
		return true;
	if (isInStudioPaletteFolder(path, studioPalette->getProjectPalettesRoot()))
		return true;
	return false;
}

//-----------------------------------------------------------------------------
} //namespace
//-----------------------------------------------------------------------------

//=============================================================================
// StudioPaletteTreeViewer
//-----------------------------------------------------------------------------

StudioPaletteTreeViewer::StudioPaletteTreeViewer(QWidget *parent,
												 TPaletteHandle *studioPaletteHandle,
												 TPaletteHandle *levelPaletteHandle,
												 TXsheetHandle *xsheetHandle,
												 TXshLevelHandle *currentLevelHandle)
	: QTreeWidget(parent), m_dropItem(0), m_studioPaletteHandle(studioPaletteHandle), m_levelPaletteHandle(levelPaletteHandle), m_currentLevelHandle(currentLevelHandle), m_xsheetHandle(xsheetHandle), m_folderIcon(QIcon()), m_levelPaletteIcon(QIcon()), m_studioPaletteIcon(QIcon())
{
	setIndentation(14);
	setAlternatingRowColors(true);

	header()->close();
	setUniformRowHeights(true);
	setIconSize(QSize(21, 17));

	QList<QTreeWidgetItem *> paletteItems;

	QString open = QString(":Resources/folder_close.png");
	QString close = QString(":Resources/folder_open.png");
	m_folderIcon.addFile(close, QSize(21, 17), QIcon::Normal, QIcon::On);
	m_folderIcon.addFile(open, QSize(21, 17), QIcon::Normal, QIcon::Off);

	QString levelPaletteIcon = QString(":Resources/palette.png");
	m_levelPaletteIcon.addPixmap(levelPaletteIcon, QIcon::Normal, QIcon::On);
	QString studioPaletteIcon = QString(":Resources/studiopalette.png");
	m_studioPaletteIcon.addPixmap(studioPaletteIcon, QIcon::Normal, QIcon::On);

	StudioPalette *studioPalette = StudioPalette::instance();

	TFilePath levelPalettePath = studioPalette->getLevelPalettesRoot();
	paletteItems.append(createRootItem(levelPalettePath));

	TFilePath projectPalettePath = studioPalette->getProjectPalettesRoot();
	if (TSystem::doesExistFileOrLevel(projectPalettePath))
		paletteItems.append(createRootItem(projectPalettePath));

	insertTopLevelItems(0, paletteItems);

	bool ret = connect(this, SIGNAL(itemChanged(QTreeWidgetItem *, int)), SLOT(onItemChanged(QTreeWidgetItem *, int)));
	ret = ret && connect(this, SIGNAL(itemClicked(QTreeWidgetItem *, int)), SLOT(onItemClicked(QTreeWidgetItem *, int)));
	ret = ret && connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
						 SLOT(onCurrentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
	ret = ret && connect(this, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(onTreeItemExpanded(QTreeWidgetItem *)));

	//refresh tree with shortcut key
	QAction *refreshAct = CommandManager::instance()->getAction(MI_RefreshTree);
	ret = ret && connect(refreshAct, SIGNAL(triggered()), this, SLOT(onRefreshTreeShortcutTriggered()));
	addAction(refreshAct);

	m_palettesScanPopup = new PalettesScanPopup();

	setAcceptDrops(true);

	//Per la selezione multipla
	setSelectionMode(QAbstractItemView::ExtendedSelection);

	StudioPalette::instance()->addListener(this);
	TProjectManager::instance()->addListener(this);

	refresh();

	assert(ret);
}

//-----------------------------------------------------------------------------

StudioPaletteTreeViewer::~StudioPaletteTreeViewer()
{
	StudioPalette::instance()->removeListener(this);
	TProjectManager::instance()->removeListener(this);
}

//-----------------------------------------------------------------------------
void StudioPaletteTreeViewer::setCurrentLevelHandle(TXshLevelHandle *currentLevelHandle)
{
	m_currentLevelHandle = currentLevelHandle;
}

//---------------------------------------------------------------------------

void StudioPaletteTreeViewer::setLevelPaletteHandle(TPaletteHandle *paletteHandle)
{
	m_levelPaletteHandle = paletteHandle;
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::setStdPaletteHandle(TPaletteHandle *studioPaletteHandle)
{
	m_studioPaletteHandle = studioPaletteHandle;
}

//-----------------------------------------------------------------------------

QTreeWidgetItem *StudioPaletteTreeViewer::createRootItem(TFilePath path)
{
	QString rootName = QString::fromStdWString(path.getWideName());
	if (rootName != "Toonz Palettes")
		rootName = "Project Palettes";
	QTreeWidgetItem *rootItem = new QTreeWidgetItem((QTreeWidget *)0, QStringList(rootName));
	rootItem->setIcon(0, m_folderIcon);
	rootItem->setData(1, Qt::UserRole, toQString(path));

	refreshItem(rootItem);

	return rootItem;
}

//-----------------------------------------------------------------------------

bool StudioPaletteTreeViewer::isRootItem(QTreeWidgetItem *item)
{
	assert(item);
	TFilePath path = getItemPath(item);

	StudioPalette *studioPalette = StudioPalette::instance();
	if (path == studioPalette->getLevelPalettesRoot() ||
		path == studioPalette->getProjectPalettesRoot())
		return true;

	return false;
}

//-----------------------------------------------------------------------------

QTreeWidgetItem *StudioPaletteTreeViewer::createItem(const TFilePath path)
{
	StudioPalette *studioPalette = StudioPalette::instance();
	QString itemName = toQString(TFilePath(path.getWideName()));
	QTreeWidgetItem *item = new QTreeWidgetItem((QTreeWidget *)0, QStringList(itemName));
	if (studioPalette->isPalette(path)) {
		if (studioPalette->hasGlobalName(path))
			item->setIcon(0, m_studioPaletteIcon);
		else
			item->setIcon(0, m_levelPaletteIcon);
	} else if (studioPalette->isFolder(path))
		item->setIcon(0, m_folderIcon);
	item->setData(1, Qt::UserRole, toQString(path));
	item->setFlags(item->flags() | Qt::ItemIsEditable);

	return item;
}

//-----------------------------------------------------------------------------

TFilePath StudioPaletteTreeViewer::getItemPath(QTreeWidgetItem *item)
{
	TFilePath path = (item) ? TFilePath(item->data(1, Qt::UserRole).toString().toStdWString())
							: TFilePath();
	return path;
}

//-----------------------------------------------------------------------------

TFilePath StudioPaletteTreeViewer::getCurrentFolderPath()
{
	return getItemPath(currentItem());
}

//-----------------------------------------------------------------------------

QTreeWidgetItem *StudioPaletteTreeViewer::getItem(const TFilePath path)
{
	QList<QTreeWidgetItem *> oldItems = findItems(QString(""), Qt::MatchContains, 0);
	if (oldItems.isEmpty())
		return 0;
	int i;
	for (i = 0; i < (int)oldItems.size(); i++) {
		TFilePath oldItemPath(oldItems[i]->data(1, Qt::UserRole).toString().toStdWString());
		if (oldItemPath == path)
			return oldItems[i];
		else {
			QTreeWidgetItem *item = getFolderItem(oldItems[i], path);
			if (item)
				return item;
		}
	}
	return 0;
}

//-----------------------------------------------------------------------------

QTreeWidgetItem *StudioPaletteTreeViewer::getFolderItem(QTreeWidgetItem *parent, const TFilePath path)
{
	int childrenCount = parent->childCount();
	int i;
	for (i = 0; i < childrenCount; i++) {
		QTreeWidgetItem *item = parent->child(i);
		if (getItemPath(item) == path)
			return item;
		else {
			item = getFolderItem(item, path);
			if (item)
				return item;
		}
	}
	return 0;
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::resetDropItem()
{
	if (!m_dropItem)
		return;
	m_dropItem->setTextColor(0, Qt::black);
	m_dropItem = 0;
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::refresh()
{
	m_openedItems.clear();

	StudioPalette *studioPalette = StudioPalette::instance();

	TFilePath levelPalettePath = studioPalette->getLevelPalettesRoot();
	refreshItem(getItem(levelPalettePath));

	TFilePath projectPalettePath = studioPalette->getProjectPalettesRoot();
	if (!TSystem::doesExistFileOrLevel(projectPalettePath))
		return;
	refreshItem(getItem(projectPalettePath));

	//refresh all expanded items
	QList<QTreeWidgetItem *> items = findItems(QString(""), Qt::MatchContains | Qt::MatchRecursive, 0);
	if (items.isEmpty())
		return;

	for (int i = 0; i < (int)items.size(); i++)
		if (items[i]->isExpanded())
			refreshItem(items[i]);
}

//-----------------------------------------------------------------------------
/*!When expand a tree, prepare the child items of it 
*/
void StudioPaletteTreeViewer::onTreeItemExpanded(QTreeWidgetItem *item)
{
	if (!item)
		return;

	//if this item was not yet opened, then get the children of this item
	if (!m_openedItems.contains(item))
		refreshItem(item);

	//expand the item
	item->setExpanded(!item->isExpanded());
}

//-----------------------------------------------------------------------------
/*! Refresh tree only when this widget has focus
*/
void StudioPaletteTreeViewer::onRefreshTreeShortcutTriggered()
{
	if (hasFocus())
		refresh();
}

//-----------------------------------------------------------------------------
/*! Update the content of item
*/

void StudioPaletteTreeViewer::refreshItem(QTreeWidgetItem *item)
{
	TFilePath folderPath = getItemPath(item);
	assert(folderPath != TFilePath());
	//correct only tpl files and folders
	std::vector<TFilePath> childrenPath;
	StudioPalette::instance()->getChildren(childrenPath, folderPath);
	int currentChildCount = item->childCount();
	std::vector<QTreeWidgetItem *> currentChildren;
	int i;
	for (i = 0; i < currentChildCount; i++)
		currentChildren.push_back(item->child(i));

	int childrenPathCount = childrenPath.size();
	int itemIndex = 0;
	int pathIndex = 0;
	while (itemIndex < currentChildCount || pathIndex < childrenPathCount) {
		TFilePath path = (pathIndex < childrenPathCount) ? childrenPath[pathIndex] : TFilePath();

		QTreeWidgetItem *currentItem = (itemIndex < currentChildCount) ? currentChildren[itemIndex] : 0;
		TFilePath currentItemPath = getItemPath(currentItem);

		if (path == currentItemPath) {
			itemIndex++;
			pathIndex++;
		} else if ((!path.isEmpty() && path < currentItemPath) ||
				   currentItemPath.isEmpty()) {
			currentItem = createItem(path);
			item->insertChild(itemIndex, currentItem);
			itemIndex++;
			pathIndex++;
		} else {
			assert(currentItemPath < path || path.isEmpty());
			assert(currentItem);
			item->removeChild(currentItem);
			itemIndex++;
		}
	}
	m_openedItems.insert(item);
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::resetProjectPaletteFolder()
{
	int projectPaletteIndex = 1;
	TFilePath projectPalettePath = StudioPalette::instance()->getProjectPalettesRoot();
	// Prendo l'item del project palette.
	QTreeWidgetItem *projectPaletteItem = topLevelItem(projectPaletteIndex);
	if (projectPaletteItem) {
		// Se il path dell'item e' uguale a quello del project palette corrente ritorno.
		if (getItemPath(projectPaletteItem) == projectPalettePath)
			return;
		// Altrimenti lo rimuovo.
		removeItemWidget(projectPaletteItem, 0);
		delete projectPaletteItem;
		//clear the item list in order to search the folder from scratch
		m_openedItems.clear();
		//Toonz Palette is not changed, so resurrect the ToonzPaletteRoot
		m_openedItems.insert(topLevelItem(0));
	}
	if (!TSystem::doesExistFileOrLevel(projectPalettePath))
		return;
	// Creo il nuovo item con il nuovo project folder e lo inserisco nell'albero.
	// Items in the ProjectPaletteRoot are refreshed here. Stored in openedItems as well.
	QTreeWidgetItem *newProjectPaletteItem = createRootItem(projectPalettePath);
	insertTopLevelItem(projectPaletteIndex, newProjectPaletteItem);

	setCurrentItem(0);
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::onItemClicked(QTreeWidgetItem *item, int column)
{
	if (currentItem() && m_studioPaletteHandle && m_currentPalette.getPointer()) {
		// if(m_studioPaletteHandle->getPalette() != m_currentPalette.getPointer())
		{
			m_studioPaletteHandle->setPalette(m_currentPalette.getPointer());
			m_studioPaletteHandle->notifyPaletteSwitched();
			StudioPaletteCmd::updateAllLinkedStyles(m_levelPaletteHandle, m_xsheetHandle);
		}
	}
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::onItemChanged(QTreeWidgetItem *item, int column)
{
	if (item != currentItem() || isRootItem(item))
		return;
	wstring name = item->text(column).toStdWString();
	TFilePath oldPath = getCurrentFolderPath();
	if (oldPath.isEmpty() || name.empty() || oldPath.getWideName() == name)
		return;
	TFilePath newPath(oldPath.getParentDir() + TFilePath(name + toWideString(oldPath.getDottedType())));
	try {
		StudioPaletteCmd::movePalette(newPath, oldPath);
	} catch (TException &e) {
		error(QString(toString(e.getMessage()).c_str()));
		item->setText(column, QString::fromStdWString(oldPath.getWideName()));
	} catch (...) {
		error("Can't rename file");
		item->setText(column, QString::fromStdWString(oldPath.getWideName()));
	}
	refreshItem(getItem(oldPath.getParentDir()));
	setCurrentItem(getItem(newPath));
}

//-----------------------------------------------------------------------------
/*! Called when the current palette is switched
*/
void StudioPaletteTreeViewer::onCurrentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{
	TFilePath oldPath = getItemPath(previous);
	TFilePath newPath = getCurrentFolderPath();
	if (!m_studioPaletteHandle)
		return;

	if (m_currentPalette.getPointer() && m_currentPalette->getDirtyFlag()) {
		TFilePath oldPath = StudioPalette::instance()->getPalettePath(m_currentPalette->getGlobalName());
		if (oldPath == newPath)
			return;
		wstring gname = m_currentPalette->getGlobalName();
		QString question;
		question = "The current palette " + QString::fromStdWString(oldPath.getWideString()) + " \nin the studio palette has been modified. Do you want to save your changes?";
		int ret = DVGui::MsgBox(question, QObject::tr("Save"), QObject::tr("Discard"), QObject::tr("Cancel"), 0);
		if (ret == 3) {
			setCurrentItem(getItem(oldPath));
			return;
		}
		if (ret == 1) {
			//If the palette is level palette (i.e. NOT stdio palette), just overwrite it
			if (gname.empty())
				StudioPalette::instance()->save(oldPath, m_currentPalette.getPointer());
			else
				StudioPalette::instance()->setPalette(oldPath, m_currentPalette.getPointer(), false);
		}
		m_currentPalette->setDirtyFlag(false);
	}
	//load palette here
	m_currentPalette = StudioPalette::instance()->getPalette(newPath, false);
	m_studioPaletteHandle->setPalette(m_currentPalette.getPointer());
	m_studioPaletteHandle->notifyPaletteSwitched();
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::addNewPalette()
{
	if (!currentItem()) {
		error("Error: No folder selected.");
		return;
	}
	TFilePath newPath;
	try {
		newPath = StudioPaletteCmd::createPalette(getCurrentFolderPath(), "", 0);
	} catch (TException &e) {
		error("Can't create palette: " + QString(toString(e.getMessage()).c_str()));
	} catch (...) {
		error("Can't create palette");
	}
	refreshItem(currentItem());
	setCurrentItem(getItem(newPath));
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::addNewFolder()
{
	if (!currentItem()) {
		error("Error: No folder selected.");
		return;
	}
	TFilePath newPath;
	try {
		newPath = StudioPaletteCmd::addFolder(getCurrentFolderPath());
	} catch (TException &e) {
		error("Can't create palette folder: " + QString(toString(e.getMessage()).c_str()));
	} catch (...) {
		error("Can't create palette folder");
	}
	refreshItem(currentItem());
	setCurrentItem(getItem(newPath));
}

//-----------------------------------------------------------------------------
/*! Convert level palette to studio palette. 
*/
void StudioPaletteTreeViewer::convertToStudioPalette()
{
	TFilePath path = getItemPath(currentItem());
	StudioPalette *studioPalette = StudioPalette::instance();
	if (studioPalette->isPalette(path)) {
		TPalette *palette = studioPalette->getPalette(path);

		if (!palette) {
			error("Can't touch palette");
			return;
		}

		if (m_currentPalette->getPaletteName() != palette->getPaletteName()) {
			error("Can't touch palette");
			return;
		}

		QString question;
		question = QString::fromStdWString(L"Convert " + path.getWideString() + L" to Studio Palette and Overwrite. \nAre you sure ?");
		int ret = DVGui::MsgBox(question, QObject::tr("Yes"), QObject::tr("No"));
		if (ret == 0 || ret == 2)
			return;

		// apply global name
		time_t ltime;
		time(&ltime);
		wstring gname = toWideString((int)ltime) + L"_" + toWideString(rand());
		m_currentPalette->setGlobalName(gname);
		studioPalette->setStylesGlobalNames(m_currentPalette.getPointer());
		studioPalette->save(path, m_currentPalette.getPointer());

		m_currentPalette->setDirtyFlag(false);
		currentItem()->setIcon(0, m_studioPaletteIcon);

	} else
		error("Can't find palette");
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::deleteItem(QTreeWidgetItem *item)
{
	QTreeWidgetItem *parent = item->parent();
	if (!parent)
		return;

	if (item->childCount() > 0) {
		QString question;
		question = tr("This folder is not empty. Delete anyway?");
		int ret = DVGui::MsgBox(question, QObject::tr("Yes"), QObject::tr("No"));
		if (ret == 0 || ret == 2)
			return;
	}

	TFilePath path = getItemPath(item);
	StudioPalette *studioPalette = StudioPalette::instance();
	if (studioPalette->isFolder(path)) {
		try {
			StudioPaletteCmd::deleteFolder(path);
		} catch (TException &e) {
			error("Can't delete folder: " + QString(toString(e.getMessage()).c_str()));
		} catch (...) {
			error("Can't delete folder");
		}
	} else {
		assert(studioPalette->isPalette(path));
		try {
			StudioPaletteCmd::deletePalette(path);
		} catch (TException &e) {
			error("Can't delete palette: " + QString(toString(e.getMessage()).c_str()));
		} catch (...) {
			error("Can't delete palette");
		}
	}

	refreshItem(parent);
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::deleteItems()
{
	QList<QTreeWidgetItem *> items = selectedItems();
	int count = items.size();

	if (count == 0) {
		error("Nothing to delete");
		return;
	}
	int i;
	TUndoManager::manager()->beginBlock();
	for (i = 0; i < count; i++)
		deleteItem(items[i]);
	TUndoManager::manager()->endBlock();
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::searchForPalette()
{
	m_palettesScanPopup->setCurrentFolder(getCurrentFolderPath());
	int ret = m_palettesScanPopup->exec();
	if (ret == QDialog::Accepted)
		refresh();
}

//-----------------------------------------------------------------------------

class InvalidateIconsUndo : public TUndo
{
	TPaletteP m_targetPalette, m_oldPalette, m_newPalette;
	TXshLevelHandle *m_levelHandle;

public:
	InvalidateIconsUndo(TXshLevelHandle *levelHandle)
		: m_levelHandle(levelHandle)
	{
	}

	void undo() const
	{
		TXshLevel *level = m_levelHandle->getLevel();
		if (level) {
			std::vector<TFrameId> fids;
			level->getFids(fids);
			invalidateIcons(level, fids);
		}
	}
	void redo() const
	{
		undo();
	}

	int getSize() const
	{
		return sizeof(*this);
	}
};

//----------------------------------------------------------------------

//-----------------------------------------------------------------------------

class AdjustPaletteDialog : public DVGui::Dialog
{
private:
	IntField *m_tolerance;

public:
	int getTolerance() { return m_tolerance->getValue(); }

	AdjustPaletteDialog()
		: Dialog(0, true, true, "Adjust Current Level to This Palette")
	{
		setWindowTitle(tr("Adjust Current Level to This Palette"));

		beginVLayout();
		m_tolerance = new IntField(this);
		m_tolerance->setRange(0, 255);
		m_tolerance->setValue(0);
		addWidget(tr("Tolerance"), m_tolerance);
		endVLayout();

		QPushButton *okBtn = new QPushButton(tr("Apply"), this);
		okBtn->setDefault(true);
		QPushButton *cancelBtn = new QPushButton(tr("Cancel"), this);
		bool ret = connect(okBtn, SIGNAL(clicked()), this, SLOT(accept()));
		ret = ret && connect(cancelBtn, SIGNAL(clicked()), this, SLOT(reject()));
		assert(ret);

		addButtonBarWidget(okBtn, cancelBtn);
	}
};

//-------------------------------------------------------------------------------------------------

void StudioPaletteTreeViewer::loadInCurrentPaletteAndAdaptLevel()
{
	QList<QTreeWidgetItem *> items = selectedItems();
	assert(items.size() == 1);

	TPalette *palette = m_levelPaletteHandle->getPalette();
	if (!palette)
		return;

	TPalette *newPalette = StudioPalette::instance()->getPalette(getItemPath(items[0]), true);
	if (!newPalette)
		return;

	AdjustPaletteDialog apd;

	if (apd.exec() != QDialog::Accepted)
		return;

	/* awful patch: since in StudioPaletteCmd(defined in toonzlib) I cannot use the invalidateIcons(defined in toonzqt) 
    i do invalidate icons from here using a "fake TUndo", named InvalidateIconsUndo. 
    And, since I need to refresh icons at the end of the processing, i have to put that fake undo twice, one before and one after.
    this way , when the user do either an undo or a redo operation, I am ensured that the last operation is the icon refresh...
  */
	TUndoManager::manager()->beginBlock();

	TUndoManager::manager()->add(new InvalidateIconsUndo(m_currentLevelHandle));

	StudioPaletteCmd::loadIntoCurrentPalette(m_levelPaletteHandle, newPalette, m_currentLevelHandle, apd.getTolerance());

	TUndoManager::manager()->add(new InvalidateIconsUndo(m_currentLevelHandle));

	TUndoManager::manager()->endBlock();

	InvalidateIconsUndo(m_currentLevelHandle).undo();
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::loadInCurrentPalette()
{
	QList<QTreeWidgetItem *> items = selectedItems();
	int count = items.size();
	if (count == 0)
		return;

	TPalette *palette = m_levelPaletteHandle->getPalette();
	if (!palette)
		return;

	if (palette->isLocked()) {
		DVGui::warning("Palette is Locked!");
		return;
	}

	TPalette *newPalette = StudioPalette::instance()->getPalette(getItemPath(items[0]), false);
	if (!newPalette)
		return;
	if (m_xsheetHandle) {
		int ret = DVGui::eraseStylesInDemand(palette, m_xsheetHandle, newPalette);
		if (ret == 0)
			return;
	}

	StudioPaletteCmd::loadIntoCurrentPalette(m_levelPaletteHandle, newPalette);
	m_currentLevelHandle->notifyLevelChange();

	TXshLevel *level = m_currentLevelHandle->getLevel();
	if (level) {
		std::vector<TFrameId> fids;
		level->getFids(fids);
		invalidateIcons(level, fids);
	}

	int i;
	for (i = 1; i < count; i++) {
		TFilePath path = getItemPath(items[i]);
		StudioPaletteCmd::mergeIntoCurrentPalette(m_levelPaletteHandle, path);
	}
	// in order to update the title bar of palette viewer
	m_levelPaletteHandle->getPalette()->setDirtyFlag(true);
	m_levelPaletteHandle->notifyPaletteChanged();
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::replaceCurrentPalette()
{
	QList<QTreeWidgetItem *> items = selectedItems();
	int count = items.size();
	if (count == 0)
		return;

	//exec confirmation dialog
	TPalette *current = m_levelPaletteHandle->getPalette();
	if (!current)
		return;

	QString label;
	if (count != 1) //replacing to multiple palettes
		label = QString::fromStdWString(L"Replacing all selected palettes with the palette \"" + current->getPaletteName() + L"\". \nAre you sure ?");
	else {
		TPalette *dstPalette = StudioPalette::instance()->getPalette(getItemPath(items[0]));
		if (!dstPalette)
			return;
		label = QString::fromStdWString(L"Replacing the palette \"" + dstPalette->getPaletteName() + L"\" with the palette \"" + current->getPaletteName() + L"\". \nAre you sure ?");
	}

	int ret = DVGui::MsgBox(label, QObject::tr("Replace"), QObject::tr("Cancel"), 1);
	if (ret == 0 || ret == 2)
		return;

	TUndoManager::manager()->beginBlock();
	int i;
	for (i = 0; i < count; i++)
		StudioPaletteCmd::replaceWithCurrentPalette(m_levelPaletteHandle, m_studioPaletteHandle, getItemPath(items[i]));
	TUndoManager::manager()->endBlock();

	if (m_currentPalette)
		m_currentPalette->setDirtyFlag(false);
	//in order to update display
	onCurrentItemChanged(currentItem(), currentItem());
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::mergeToCurrentPalette()
{
	QList<QTreeWidgetItem *> items = selectedItems();
	int count = items.size();
	if (count == 0)
		return;

	TUndoManager::manager()->beginBlock();
	int i;
	for (i = 0; i < count; i++)
		StudioPaletteCmd::mergeIntoCurrentPalette(m_levelPaletteHandle, getItemPath(items[i]));
	TUndoManager::manager()->endBlock();
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::paintEvent(QPaintEvent *event)
{
	QTreeWidget::paintEvent(event);
	QPainter p(viewport());
	if (m_dropItem) {
		p.setPen(QColor(50, 105, 200));
		p.drawRect(visualItemRect(m_dropItem));
	}
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::contextMenuEvent(QContextMenuEvent *event)
{
	TFilePath path = getCurrentFolderPath();

	StudioPalette *studioPalette = StudioPalette::instance();

	//Menu' per la selezione singola
	QList<QTreeWidgetItem *> items = selectedItems();
	int count = items.size();
	if (count == 1) {
		// Verify if click position is in a row containing an item.
		QRect rect = visualItemRect(currentItem());
		if (!QRect(0, rect.y(), width(), rect.height()).contains(event->pos()))
			return;

		bool isFolder = (studioPalette->isFolder(path));

		QMenu menu(this);
		if (isFolder) {
			createMenuAction(menu, "", tr("New Palette"), "addNewPalette()");
			createMenuAction(menu, "", tr("New Folder"), "addNewFolder()");
		}

		if (studioPalette->isFolder(path) &&
			studioPalette->getLevelPalettesRoot() != path &&
			studioPalette->getProjectPalettesRoot() != path) {
			menu.addSeparator();
			createMenuAction(menu, "", tr("Delete Folder"), "deleteItems()");
		} else if (studioPalette->isPalette(path)) {
			if (m_studioPaletteHandle->getPalette()) {
				createMenuAction(menu, "MI_LoadIntoCurrentPalette", tr("Load into Current Palette"), "loadInCurrentPalette()");
				createMenuAction(menu, "MI_AdjustCurrentLevelToPalette", tr("Adjust Current Level to This Palette"), "loadInCurrentPaletteAndAdaptLevel()");
				createMenuAction(menu, "MI_MergeToCurrentPalette", tr("Merge to Current Palette"), "mergeToCurrentPalette()");
				if (!m_studioPaletteHandle->getPalette()->isLocked()) {
					createMenuAction(menu, "MI_ReplaceWithCurrentPalette", tr("Replace with Current Palette"), "replaceCurrentPalette()");
					menu.addSeparator();
					createMenuAction(menu, "MI_DeletePalette", tr("Delete Palette"), "deleteItems()");
				}
			}
			if (!studioPalette->hasGlobalName(path)) {
				menu.addSeparator();
				createMenuAction(menu, "", tr("Convert to Studio Palette and Overwrite"), "convertToStudioPalette()");
			}
		}

		if (isFolder) {
			menu.addSeparator();
			createMenuAction(menu, "", tr("Search for Palettes"), "searchForPalette()");
		}
		menu.exec(event->globalPos());
		return;
	}

	//Menu' per la selezione multipla
	// Verify if click position is in a row containing an item.
	bool areAllPalette = true;
	bool isClickInSelection = false;
	int i;
	for (i = 0; i < count; i++) {
		QTreeWidgetItem *item = items[i];
		QRect rect = visualItemRect(item);
		if (QRect(0, rect.y(), width(), rect.height()).contains(event->pos()))
			isClickInSelection = true;
		TFilePath path = getItemPath(item);
		if (studioPalette->isFolder(path))
			areAllPalette = false;
	}
	if (!isClickInSelection)
		return;

	QMenu menu(this);
	if (areAllPalette) {
		createMenuAction(menu, "", tr("Load into Current Palette"), "loadInCurrentPalette()");
		createMenuAction(menu, "", tr("Merge to Current Palette"), "mergeToCurrentPalette()");
		createMenuAction(menu, "", tr("Replace with Current Palette"), "replaceCurrentPalette()");
		menu.addSeparator();
	}
	createMenuAction(menu, "", tr("Delete"), "deleteItems()");

	menu.exec(event->globalPos());
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::createMenuAction(QMenu &menu, const char *id, QString name, const char *slot)
{
	QAction *act = menu.addAction(name);
	string slotName(slot);
	slotName = string("1") + slotName;
	connect(act, SIGNAL(triggered()), slotName.c_str());
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::mouseMoveEvent(QMouseEvent *event)
{
	// If left button is not pressed return; is not drag event.
	if (!(event->buttons() & Qt::LeftButton))
		return;
	startDragDrop();
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::startDragDrop()
{
	TRepetitionGuard guard;
	if (!guard.hasLock())
		return;

	QDrag *drag = new QDrag(this);
	QMimeData *mimeData = new QMimeData;
	QList<QUrl> urls;

	QList<QTreeWidgetItem *> items = selectedItems();
	int i;

	for (i = 0; i < items.size(); i++) {
		//Sposto solo le palette.
		TFilePath path = getItemPath(items[i]);
		if (!path.isEmpty() &&
			(path.getType() == "tpl" || path.getType() == "pli" ||
			 path.getType() == "tlv" || path.getType() == "tnz"))
			urls.append(pathToUrl(path));
	}
	if (urls.isEmpty())
		return;
	mimeData->setUrls(urls);
	drag->setMimeData(mimeData);
	Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);
	viewport()->update();
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::dragEnterEvent(QDragEnterEvent *event)
{
	const QMimeData *mimeData = event->mimeData();
	const PaletteData *paletteData = dynamic_cast<const PaletteData *>(mimeData);

	if (acceptResourceDrop(mimeData->urls())) {
		QList<QUrl> urls = mimeData->urls();
		int count = urls.size();
		if (count == 0)
			return;

		//Controllo che almeno un url del drag sia una palette da spostare.
		bool isPalette = false;
		int i;
		for (i = 0; i < count; i++) {
			QUrl url = urls[i];
			TFilePath path(url.toLocalFile().toStdWString());
			if (!path.isEmpty() &&
				(path.getType() == "tpl" || path.getType() == "pli" ||
				 path.getType() == "tlv" || path.getType() == "tnz")) {
				isPalette = true;
				break;
			}
		}
		if (!isPalette)
			return;

		event->acceptProposedAction();
	} else if (paletteData && paletteData->hasOnlyPalette())
		event->acceptProposedAction();
	viewport()->update();
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::dragMoveEvent(QDragMoveEvent *event)
{
	QTreeWidgetItem *item = itemAt(event->pos());
	TFilePath newPath = getItemPath(item);

	if (m_dropItem)
		m_dropItem->setTextColor(0, Qt::black);

	if (item) {
		m_dropItem = item;
		event->acceptProposedAction();
	} else {
		m_dropItem = 0;
		event->ignore();
	}
	viewport()->update();
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::dropEvent(QDropEvent *event)
{
	TFilePath newPath = getItemPath(m_dropItem);

	resetDropItem();

	const QMimeData *mimeData = event->mimeData();
	const PaletteData *paletteData = dynamic_cast<const PaletteData *>(mimeData);
	if (paletteData) {
		if (paletteData->hasOnlyPalette()) {
			TPalette *palette = paletteData->getPalette();
			if (!palette)
				return;

			try {
				StudioPaletteCmd::createPalette(newPath, toString(palette->getPaletteName()), palette);
			} catch (TException &e) {
				error("Can't create palette: " + QString(toString(e.getMessage()).c_str()));
			} catch (...) {
				error("Can't create palette");
			}
		}
		return;
	}

	if (!mimeData->hasUrls() || mimeData->urls().size() == 0)
		return;

	QList<QUrl> urls = mimeData->urls();
	TUndoManager::manager()->beginBlock();
	int i;
	for (i = 0; i < urls.size(); i++) {
		QUrl url = urls[i];
		TFilePath path = TFilePath(url.toLocalFile().toStdWString());

		StudioPalette *studioPalette = StudioPalette::instance();
		if (path == newPath || path.getParentDir() == newPath)
			continue;

		if (isInStudioPalette(path)) {
			TFilePath newPalettePath = newPath + TFilePath(path.getWideName() + toWideString(path.getDottedType()));
			try {
				StudioPaletteCmd::movePalette(newPalettePath, path);
			} catch (TException &e) {
				error("Can't rename palette: " + QString(toString(e.getMessage()).c_str()));
			} catch (...) {
				error("Can't rename palette");
			}
		}
	}
	TUndoManager::manager()->endBlock();
	event->setDropAction(Qt::CopyAction);
	event->accept();
}

//-----------------------------------------------------------------------------

void StudioPaletteTreeViewer::dragLeaveEvent(QDragLeaveEvent *event)
{
	resetDropItem();
	update();
}

//=============================================================================
// StudioPaletteViewer
//-----------------------------------------------------------------------------

StudioPaletteViewer::StudioPaletteViewer(QWidget *parent,
										 TPaletteHandle *studioPaletteHandle,
										 TPaletteHandle *levelPaletteHandle,
										 TFrameHandle *frameHandle,
										 TXsheetHandle *xsheetHandle,
										 TXshLevelHandle *currentLevelHandle)
	: QSplitter(parent)
{
	// style sheet
	setObjectName("StudioPaletteViewer");
	setFrameStyle(QFrame::StyledPanel);

	setAcceptDrops(true);
	setOrientation(Qt::Vertical);

	//First Splitter Widget
	QWidget *treeWidget = new QWidget(this);
	QVBoxLayout *treeVLayout = new QVBoxLayout(treeWidget);
	treeVLayout->setMargin(0);
	treeVLayout->setSpacing(0);

	m_studioPaletteTreeViewer =
		new StudioPaletteTreeViewer(treeWidget, studioPaletteHandle, levelPaletteHandle, xsheetHandle, currentLevelHandle);

	treeVLayout->addWidget(m_studioPaletteTreeViewer);
	treeWidget->setLayout(treeVLayout);

	//Second Splitter Widget
	PaletteViewer *studioPaletteViewer = new PaletteViewer(this, PaletteViewerGUI::STUDIO_PALETTE);
	studioPaletteViewer->setObjectName("PaletteViewerInStudioPalette");
	studioPaletteViewer->setXsheetHandle(xsheetHandle);
	studioPaletteViewer->setPaletteHandle(studioPaletteHandle);
	studioPaletteViewer->setFrameHandle(frameHandle);

	addWidget(treeWidget);
	addWidget(studioPaletteViewer);

	setFocusProxy(studioPaletteViewer);
}

//-----------------------------------------------------------------------------

StudioPaletteViewer::~StudioPaletteViewer()
{
}

//-----------------------------------------------------------------------------
/*! In order to save current palette from the tool button in the PageViewer.
*/
TFilePath StudioPaletteViewer::getCurrentItemPath()
{
	return m_studioPaletteTreeViewer->getCurrentItemPath();
}