#include "styleselection.h"
#include "menubarcommandids.h"
#include "toonz/tpalettehandle.h"
#include "toonz/tscenehandle.h"
#include "tapp.h"
#include "toonzutil.h"
#include "tcolorstyles.h"
#include "tpixelutils.h"
#include "styledata.h"
#include "tundo.h"
#include "toonzqt/gutil.h"
#include "tconvert.h"
#include "toonzqt/dvdialog.h"
#include "toonz/studiopalette.h"
#include <QApplication>
#include <QClipboard>
#include <QByteArray>
#include <QBuffer>
using namespace DVGui;
//-----------------------------------------------------------------------------
namespace
{
//-----------------------------------------------------------------------------
void copyStylesWithoutUndo(TPaletteP palette, int pageIndex,
std::set<int> *styleIndicesInPage)
{
if (!palette || pageIndex < 0)
return;
int n = styleIndicesInPage->size();
if (n == 0)
return;
TPalette::Page *page = palette->getPage(pageIndex);
assert(page);
TPaletteHandle *cph = TApp::instance()->getCurrentPalette();
StyleData *data = new StyleData();
std::set<int>::iterator it;
for (it = styleIndicesInPage->begin(); it != styleIndicesInPage->end(); ++it) {
int j = *it;
int styleId = page->getStyleId(j);
TColorStyle *style = page->getStyle(j)->clone();
data->addStyle(styleId, style);
}
QApplication::clipboard()->setMimeData(data);
}
//-----------------------------------------------------------------------------
bool pasteStylesWithoutUndo(TPaletteP palette, int pageIndex,
std::set<int> *styleIndicesInPage)
{
// page = pagina corrente
TPalette::Page *page = palette->getPage(pageIndex);
assert(page);
// cerco il punto di inserimento (primo stile selezionato oppure dopo l'ultimo stile della pagina
// se nulla e' selezionato)
int indexInPage = page->getStyleCount();
if (!styleIndicesInPage->empty())
indexInPage = *styleIndicesInPage->begin();
// prendo i dati dalla clipboard
const StyleData *data = dynamic_cast<const StyleData *>(QApplication::clipboard()->mimeData());
if (!data)
return false;
// cancello la selezione
styleIndicesInPage->clear();
// comincio a fare paste
int i;
for (i = 0; i < data->getStyleCount(); i++) {
int styleId = data->getStyleIndex(i);
TColorStyle *style = data->getStyle(i)->clone();
if (palette->getStylePage(styleId) < 0) {
// styleId non e' utilizzato: uso quello
// (cut/paste utilizzato per spostare stili)
palette->setStyle(styleId, style);
} else {
// styleId e' gia' utilizzato. ne devo prendere un altro
styleId = palette->getFirstUnpagedStyle();
if (styleId >= 0)
palette->setStyle(styleId, style);
else
styleId = palette->addStyle(style);
}
// inserisco lo stile nella pagina
page->insertStyle(indexInPage + i, styleId);
// e lo seleziono
styleIndicesInPage->insert(indexInPage + i);
}
TApp::instance()->getCurrentPalette()->notifyColorStyleChanged();
return true;
}
//-----------------------------------------------------------------------------
void deleteStylesWithoutUndo(TPaletteP palette, int pageIndex,
std::set<int> *styleIndicesInPage)
{
int n = styleIndicesInPage->size();
if (n == 0)
return;
TPalette::Page *page = palette->getPage(pageIndex);
assert(page);
TPaletteHandle *cph = TApp::instance()->getCurrentPalette();
int currentStyleIndexInPage = page->search(cph->getStyleIndex());
bool mustChangeCurrentStyle =
currentStyleIndexInPage >= 0 && styleIndicesInPage->count(currentStyleIndexInPage) > 0;
std::set<int>::reverse_iterator it;
for (it = styleIndicesInPage->rbegin(); it != styleIndicesInPage->rend(); ++it) {
int j = *it;
int styleId = page->getStyleId(j);
if (styleId < 2) {
error("Can't delete color #" + QString::number(styleId));
} else {
if (styleId == cph->getStyleIndex())
cph->setStyleIndex(1);
palette->setStyle(styleId, TPixel32::Red);
page->removeStyle(j);
}
}
styleIndicesInPage->clear();
if (mustChangeCurrentStyle) {
// ho cancellato lo stile corrente
if (currentStyleIndexInPage < page->getStyleCount()) {
// posso fare in modo che lo stile selezionato sia nella stessa posizione
cph->setStyleIndex(page->getStyleId(currentStyleIndexInPage));
} else if (page->getStyleCount() > 0) {
// almeno faccio in modo che sia nella stessa pagina
int styleId = page->getStyleId(page->getStyleCount() - 1);
cph->setStyleIndex(styleId);
} else {
// seleziono lo stile #1 (che c'e' sempre). n.b. questo puo' comportare un cambio pagina
cph->setStyleIndex(1);
}
}
cph->notifyColorStyleChanged();
}
//-----------------------------------------------------------------------------
void cutStylesWithoutUndo(TPaletteP palette, int pageIndex,
std::set<int> *styleIndicesInPage)
{
copyStylesWithoutUndo(palette, pageIndex, styleIndicesInPage);
deleteStylesWithoutUndo(palette, pageIndex, styleIndicesInPage);
}
//=============================================================================
// CopyStylesUndo
//-----------------------------------------------------------------------------
class CopyStylesUndo : public TUndo
{
QMimeData *m_oldData;
QMimeData *m_newData;
public:
CopyStylesUndo(QMimeData *oldData,
QMimeData *newData)
: m_oldData(oldData), m_newData(newData)
{
}
void undo() const
{
QClipboard *clipboard = QApplication::clipboard();
clipboard->setMimeData(cloneData(m_oldData), QClipboard::Clipboard);
}
void redo() const
{
QClipboard *clipboard = QApplication::clipboard();
clipboard->setMimeData(cloneData(m_newData), QClipboard::Clipboard);
}
int getSize() const
{
return sizeof(*this);
}
};
//=============================================================================
// PasteStylesUndo
//-----------------------------------------------------------------------------
class PasteStylesUndo : public TUndo
{
TStyleSelection *m_selection;
public:
PasteStylesUndo(TStyleSelection *selection)
: m_selection(selection)
{
}
~PasteStylesUndo()
{
delete m_selection;
}
void undo() const
{
std::set<int> styleIndicesInPage = m_selection->getIndicesInPage();
cutStylesWithoutUndo(m_selection->getPalette(),
m_selection->getPageIndex(),
&styleIndicesInPage);
}
void redo() const
{
std::set<int> styleIndicesInPage = m_selection->getIndicesInPage();
pasteStylesWithoutUndo(m_selection->getPalette(),
m_selection->getPageIndex(),
&styleIndicesInPage);
}
int getSize() const
{
return sizeof(*this);
}
};
//=============================================================================
// DeleteStylesUndo
//-----------------------------------------------------------------------------
class DeleteStylesUndo : public TUndo
{
TStyleSelection *m_selection;
QMimeData *m_data;
public:
DeleteStylesUndo(TStyleSelection *selection, QMimeData *data)
: m_selection(selection), m_data(data)
{
}
~DeleteStylesUndo()
{
delete m_selection;
}
void undo() const
{
QClipboard *clipboard = QApplication::clipboard();
QMimeData *oldData = cloneData(clipboard->mimeData());
clipboard->setMimeData(cloneData(m_data), QClipboard::Clipboard);
std::set<int> styleIndicesInPage = m_selection->getIndicesInPage();
pasteStylesWithoutUndo(m_selection->getPalette(),
m_selection->getPageIndex(),
&styleIndicesInPage);
clipboard->setMimeData(oldData, QClipboard::Clipboard);
}
void redo() const
{
std::set<int> styleIndicesInPage = m_selection->getIndicesInPage();
deleteStylesWithoutUndo(m_selection->getPalette(),
m_selection->getPageIndex(),
&styleIndicesInPage);
}
int getSize() const
{
return sizeof(*this);
}
};
//=============================================================================
// CutStylesUndo
//-----------------------------------------------------------------------------
class CutStylesUndo : public TUndo
{
TStyleSelection *m_selection;
QMimeData *m_oldData;
public:
CutStylesUndo(TStyleSelection *selection, QMimeData *data)
: m_selection(selection), m_oldData(data)
{
}
~CutStylesUndo()
{
delete m_selection;
}
void undo() const
{
std::set<int> styleIndicesInPage = m_selection->getIndicesInPage();
pasteStylesWithoutUndo(m_selection->getPalette(),
m_selection->getPageIndex(),
&styleIndicesInPage);
QClipboard *clipboard = QApplication::clipboard();
clipboard->setMimeData(cloneData(m_oldData), QClipboard::Clipboard);
}
void redo() const
{
std::set<int> styleIndicesInPage = m_selection->getIndicesInPage();
cutStylesWithoutUndo(m_selection->getPalette(),
m_selection->getPageIndex(),
&styleIndicesInPage);
}
int getSize() const
{
return sizeof(*this);
}
};
//-----------------------------------------------------------------------------
} // namespace
//-----------------------------------------------------------------------------
//=============================================================================
// TStyleSelection
//-----------------------------------------------------------------------------
TStyleSelection::TStyleSelection()
: m_palette(0), m_pageIndex(-1)
{
}
//-------------------------------------------------------------------
TStyleSelection::~TStyleSelection()
{
}
//-----------------------------------------------------------------------------
void TStyleSelection::enableCommands()
{
enableCommand(this, MI_Cut, &TStyleSelection::cutStyles);
enableCommand(this, MI_Copy, &TStyleSelection::copyStyles);
enableCommand(this, MI_Paste, &TStyleSelection::pasteStyles);
enableCommand(this, MI_PasteValues, &TStyleSelection::pasteStylesValue);
enableCommand(this, MI_Clear, &TStyleSelection::deleteStyles);
enableCommand(this, MI_BlendColors, &TStyleSelection::blendStyles);
}
//-------------------------------------------------------------------
void TStyleSelection::select(const TPaletteP &palette, int pageIndex)
{
m_palette = palette;
m_pageIndex = pageIndex;
m_styleIndicesInPage.clear();
}
//-------------------------------------------------------------------
void TStyleSelection::select(const TPaletteP &palette,
int pageIndex,
int styleIndexInPage,
bool on)
{
if (on) {
if (m_palette.getPointer() != palette.getPointer() || pageIndex != m_pageIndex)
m_styleIndicesInPage.clear();
m_palette = palette;
m_pageIndex = pageIndex;
m_styleIndicesInPage.insert(styleIndexInPage);
} else if (m_palette.getPointer() == palette.getPointer() && pageIndex == m_pageIndex)
m_styleIndicesInPage.erase(styleIndexInPage);
}
//-------------------------------------------------------------------
bool TStyleSelection::isSelected(const TPaletteP &palette,
int pageIndex,
int id) const
{
return m_palette.getPointer() == palette.getPointer() &&
m_pageIndex == pageIndex &&
m_styleIndicesInPage.find(id) != m_styleIndicesInPage.end();
}
//------------------------------------------------------------------
bool TStyleSelection::isPageSelected(const TPaletteP &palette, int pageIndex) const
{
return m_palette.getPointer() == palette.getPointer() &&
m_pageIndex == pageIndex;
}
//-------------------------------------------------------------------
void TStyleSelection::selectNone()
{
m_palette = 0;
m_pageIndex = -1;
m_styleIndicesInPage.clear();
notifyView();
}
//-------------------------------------------------------------------
bool TStyleSelection::isEmpty() const
{
return m_pageIndex < 0 && m_styleIndicesInPage.empty();
}
//-------------------------------------------------------------------
int TStyleSelection::getStyleCount() const
{
return m_styleIndicesInPage.size();
}
//-------------------------------------------------------------------
void TStyleSelection::cutStyles()
{
if (isEmpty())
return;
QClipboard *clipboard = QApplication::clipboard();
QMimeData *oldData = cloneData(clipboard->mimeData());
TStyleSelection *selection = new TStyleSelection(this);
if (m_pageIndex == 0 && (isSelected(m_palette, 0, 0) || isSelected(m_palette, 0, 1))) {
MsgBox(CRITICAL, "Can't delete colors #0 and #1");
return;
}
cutStylesWithoutUndo(m_palette, m_pageIndex, &m_styleIndicesInPage);
TUndoManager::manager()->add(new CutStylesUndo(selection, oldData));
}
//-------------------------------------------------------------------
void TStyleSelection::copyStyles()
{
if (isEmpty())
return;
QClipboard *clipboard = QApplication::clipboard();
QMimeData *oldData = cloneData(clipboard->mimeData());
copyStylesWithoutUndo(m_palette, m_pageIndex, &m_styleIndicesInPage);
QMimeData *newData = cloneData(clipboard->mimeData());
TUndoManager::manager()->add(new CopyStylesUndo(oldData, newData));
}
//-------------------------------------------------------------------
void TStyleSelection::pasteStyles()
{
// se non c'e' palette o pagina corrente esco
if (!m_palette || m_pageIndex < 0)
return;
if (m_pageIndex == 0 && !m_styleIndicesInPage.empty() && *m_styleIndicesInPage.begin() < 2) {
MsgBox(CRITICAL, "Can't paste styles there");
return;
}
pasteStylesWithoutUndo(m_palette, m_pageIndex, &m_styleIndicesInPage);
TUndoManager::manager()->add(new PasteStylesUndo(new TStyleSelection(this)));
}
//-------------------------------------------------------------------
void TStyleSelection::deleteStyles()
{
if (!m_palette || m_pageIndex < 0)
return;
if (m_pageIndex == 0 && (isSelected(m_palette, 0, 0) || isSelected(m_palette, 0, 1))) {
MsgBox(CRITICAL, "Can't delete colors #0 and #1");
return;
}
StyleData *data = new StyleData();
std::set<int>::iterator it;
TPalette::Page *page = m_palette->getPage(m_pageIndex);
for (it = m_styleIndicesInPage.begin(); it != m_styleIndicesInPage.end(); ++it) {
int j = *it;
int styleId = page->getStyleId(j);
if (styleId < 0)
continue;
TColorStyle *style = page->getStyle(j)->clone();
data->addStyle(styleId, style);
}
TStyleSelection *selection = new TStyleSelection(this);
deleteStylesWithoutUndo(m_palette, m_pageIndex, &m_styleIndicesInPage);
TUndoManager::manager()->add(new DeleteStylesUndo(selection, data));
}
//-----------------------------------------------------------------------------
namespace
{
//-----------------------------------------------------------------------------
//=============================================================================
// UndoPasteValues
//-----------------------------------------------------------------------------
class UndoPasteValues : public TUndo
{
TPaletteP m_palette;
class Item
{
public:
int m_index;
TColorStyle *m_oldStyle;
TColorStyle *m_newStyle;
Item(int index, const TColorStyle *oldStyle, const TColorStyle *newStyle)
: m_index(index), m_oldStyle(oldStyle->clone()), m_newStyle(newStyle->clone())
{
}
~Item()
{
delete m_oldStyle;
delete m_newStyle;
}
};
std::vector<Item *> m_items;
public:
UndoPasteValues(const TPaletteP &palette)
: m_palette(palette)
{
}
~UndoPasteValues()
{
clearPointerContainer(m_items);
}
void addItem(int index, const TColorStyle *oldStyle, const TColorStyle *newStyle)
{
m_items.push_back(new Item(index, oldStyle, newStyle));
}
void pasteValue(int styleId, const TColorStyle *newStyle) const
{
// preserva il vecchio nome
wstring str = m_palette->getStyle(styleId)->getName();
m_palette->setStyle(styleId, newStyle->clone());
m_palette->getStyle(styleId)->setName(str);
}
void undo() const
{
int i;
for (i = 0; i < (int)m_items.size(); i++)
pasteValue(m_items[i]->m_index, m_items[i]->m_oldStyle);
TApp::instance()->getCurrentPalette()->notifyColorStyleChanged();
}
void redo() const
{
int i;
for (i = 0; i < (int)m_items.size(); i++)
pasteValue(m_items[i]->m_index, m_items[i]->m_newStyle);
TApp::instance()->getCurrentPalette()->notifyColorStyleChanged();
}
int getSize() const
{
return sizeof(*this) + (int)m_items.size() * 100; // forfait
}
};
//-----------------------------------------------------------------------------
} // namespace
//-----------------------------------------------------------------------------
void TStyleSelection::pasteStylesValue()
{
if (!m_palette || m_pageIndex < 0)
return;
if (m_pageIndex == 0 && isSelected(m_palette, 0, 0)) {
MsgBox(CRITICAL, "Can't modify color #0");
return;
}
TPalette::Page *page = m_palette->getPage(m_pageIndex);
assert(page);
const StyleData *data = dynamic_cast<const StyleData *>(QApplication::clipboard()->mimeData());
if (!data)
return;
int i = 0;
UndoPasteValues *undo = new UndoPasteValues(m_palette);
std::set<int>::iterator it;
for (it = m_styleIndicesInPage.begin(); it != m_styleIndicesInPage.end() && i < data->getStyleCount(); ++it, i++) {
int styleId = page->getStyleId(*it);
undo->addItem(styleId, m_palette->getStyle(styleId), data->getStyle(i));
m_palette->setStyle(styleId, data->getStyle(i)->clone());
}
TUndoManager::manager()->add(undo);
TPaletteHandle *ph = TApp::instance()->getCurrentPalette();
ph->notifyColorStyleChanged();
ph->updateColor();
}
//-----------------------------------------------------------------------------
namespace
{
//-----------------------------------------------------------------------------
//=============================================================================
// UndoBlendColor
//-----------------------------------------------------------------------------
class UndoBlendColor : public TUndo
{
TPaletteP m_palette;
int m_pageIndex;
std::vector<std::pair<int, TColorStyle *>> m_colorStyles;
TPixel32 m_c0, m_c1;
public:
UndoBlendColor(TPaletteP palette, int pageIndex, std::vector<std::pair<int, TColorStyle *>> colorStyles,
const TPixel32 &c0, const TPixel32 &c1)
: m_palette(palette), m_pageIndex(pageIndex), m_colorStyles(colorStyles), m_c0(c0), m_c1(c1)
{
}
~UndoBlendColor()
{
}
void undo() const
{
if (!m_palette)
return;
TPalette::Page *page = m_palette->getPage(m_pageIndex);
if (!page)
return;
int count = 0;
for (UINT i = 0; i < m_colorStyles.size(); i++) {
TColorStyle *st = page->getStyle(m_colorStyles[i].first);
QString gname = QString::fromStdWString(st->getGlobalName());
if (!gname.isEmpty() && gname[0] != L'-')
continue;
m_palette->setStyle(page->getStyleId(m_colorStyles[i].first), m_colorStyles[i].second->clone());
m_colorStyles[i].second->invalidateIcon();
}
TApp::instance()->getCurrentPalette()->notifyColorStyleChanged();
}
void redo() const
{
if (!m_palette)
return;
TPalette::Page *page = m_palette->getPage(m_pageIndex);
if (!page)
return;
for (UINT i = 0; i < m_colorStyles.size(); i++) {
TColorStyle *cs = page->getStyle(m_colorStyles[i].first);
QString gname = QString::fromStdWString(cs->getGlobalName());
if (!gname.isEmpty() && gname[0] != L'-')
continue;
assert(cs);
if (!cs)
continue;
cs->setMainColor(blend(m_c0, m_c1, (double)i / (double)(m_colorStyles.size() - 1)));
cs->invalidateIcon();
}
TApp::instance()->getCurrentPalette()->notifyColorStyleChanged();
}
int getSize() const
{
return sizeof(*this); //+500*m_oldStyles.size(); //dipende da che stile ha salvato
}
};
//-----------------------------------------------------------------------------
} //namespace
//-----------------------------------------------------------------------------
void TStyleSelection::blendStyles()
{
if (!m_palette || m_pageIndex < 0)
return;
int n = m_styleIndicesInPage.size();
if (n < 2)
return;
TPalette::Page *page = m_palette->getPage(m_pageIndex);
assert(page);
std::vector<TColorStyle *> styles;
std::vector<std::pair<int, TColorStyle *>> oldStyles;
for (std::set<int>::iterator it = m_styleIndicesInPage.begin();
it != m_styleIndicesInPage.end(); ++it) {
TColorStyle *cs = page->getStyle(*it);
assert(cs);
styles.push_back(cs);
oldStyles.push_back(std::pair<int, TColorStyle *>(*it, cs->clone()));
}
assert(styles.size() >= 2);
TPixel32 c0 = styles.front()->getMainColor();
TPixel32 c1 = styles.back()->getMainColor();
bool areAllStyleLincked = true;
int i;
for (i = 1; i < n - 1; i++) {
QString gname = QString::fromStdWString(styles[i]->getGlobalName());
if (!gname.isEmpty() && gname[0] != L'-')
continue;
areAllStyleLincked = false;
styles[i]->setMainColor(blend(c0, c1, (double)i / (double)(n - 1)));
styles[i]->invalidateIcon();
}
if (areAllStyleLincked)
return;
TApp::instance()->getCurrentPalette()->notifyColorStyleChanged();
UndoBlendColor *undo = new UndoBlendColor(m_palette, m_pageIndex, oldStyles, c0, c1);
TUndoManager::manager()->add(undo);
}
//-----------------------------------------------------------------------------
void TStyleSelection::toggleLink()
{
if (!m_palette || m_pageIndex < 0)
return;
int n = m_styleIndicesInPage.size();
if (n <= 0)
return;
bool somethingHasBeenLinked = false;
bool currentStyleIsInSelection = false;
TApp *app = TApp::instance();
TPaletteHandle *ph = app->getCurrentPalette();
TPalette::Page *page = m_palette->getPage(m_pageIndex);
assert(page);
for (std::set<int>::iterator it = m_styleIndicesInPage.begin();
it != m_styleIndicesInPage.end(); ++it) {
TColorStyle *cs = page->getStyle(*it);
assert(cs);
wstring name = cs->getGlobalName();
if (name != L"" && (name[0] == L'-' || name[0] == L'+')) {
name[0] = name[0] == L'-' ? L'+' : L'-';
cs->setGlobalName(name);
if (name[0] == L'+')
somethingHasBeenLinked = true;
}
if (*it == m_palette->getPage(m_pageIndex)->search(ph->getStyleIndex()))
currentStyleIsInSelection = true;
}
if (somethingHasBeenLinked)
StudioPalette::instance()->updateLinkedColors(m_palette.getPointer());
ph->notifyPaletteChanged();
if (currentStyleIsInSelection)
ph->notifyColorStyleSwitched();
// DA FARE: e' giusto mettere la nofica del dirty flag a current scene
// o e' meglio farlo al livello corrente!?
app->getCurrentScene()->setDirtyFlag(true);
// extern void setPaletteDirtyFlag();
// setPaletteDirtyFlag();
}
//-----------------------------------------------------------------------------
QByteArray TStyleSelection::toByteArray(int pageIndex,
const std::set<int> &indicesInPage,
const QString paletteGlobalName)
{
QByteArray data;
QBuffer dataBuffer(&data);
dataBuffer.open(QIODevice::WriteOnly);
QDataStream out(&dataBuffer);
out << (int)pageIndex << (int)indicesInPage.size() << paletteGlobalName;
std::set<int>::const_iterator it;
for (it = indicesInPage.begin(); it != indicesInPage.end(); ++it)
out << (int)*it;
return data;
}
//-----------------------------------------------------------------------------
void TStyleSelection::fromByteArray(QByteArray &byteArray,
int &pageIndex,
std::set<int> &indicesInPage,
QString &paletteGlobalName)
{
QBuffer dataBuffer(&byteArray);
dataBuffer.open(QIODevice::ReadOnly);
QDataStream in(&dataBuffer);
int i, count = 0;
in >> pageIndex >> count >> paletteGlobalName;
for (i = 0; i < count; i++) {
int indexInPage;
in >> indexInPage;
indicesInPage.insert(indexInPage);
}
}
//-----------------------------------------------------------------------------
QByteArray TStyleSelection::toByteArray() const
{
QString globalName = QString::fromStdWString(m_palette->getGlobalName().c_str());
return toByteArray(m_pageIndex, m_styleIndicesInPage, globalName);
}
//-----------------------------------------------------------------------------
const char *TStyleSelection::getMimeType()
{
return "application/vnd.toonz.style";
}