955 lines
30 KiB
C++
955 lines
30 KiB
C++
#include "toonzqt/hexcolornames.h"
|
|
|
|
// TnzLib includes
|
|
#include "toonz/toonzfolders.h"
|
|
|
|
// TnzCore includes
|
|
#include "tconvert.h"
|
|
#include "tfiletype.h"
|
|
#include "tsystem.h"
|
|
#include "tcolorstyles.h"
|
|
#include "tpalette.h"
|
|
#include "tpixel.h"
|
|
#include "tvectorimage.h"
|
|
#include "trasterimage.h"
|
|
#include "tlevel_io.h"
|
|
|
|
// Qt includes
|
|
#include <QCoreApplication>
|
|
#include <QPainter>
|
|
#include <QMouseEvent>
|
|
#include <QLabel>
|
|
#include <QToolTip>
|
|
#include <QTabWidget>
|
|
#include <QCheckBox>
|
|
#include <QFileDialog>
|
|
#include <QMessageBox>
|
|
|
|
using namespace DVGui;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#define COLORNAMES_FILE "colornames.txt"
|
|
|
|
QMap<QString, QString> HexColorNames::s_maincolornames;
|
|
QMap<QString, QString> HexColorNames::s_usercolornames;
|
|
QMap<QString, QString> HexColorNames::s_tempcolornames;
|
|
|
|
HexColorNames *HexColorNames::instance() {
|
|
static HexColorNames _instance;
|
|
return &_instance;
|
|
}
|
|
|
|
HexColorNames::HexColorNames() {}
|
|
|
|
void HexColorNames::loadColorTableXML(QMap<QString, QString> &table,
|
|
const TFilePath &fp) {
|
|
if (!TFileStatus(fp).doesExist()) throw TException("File not found");
|
|
|
|
TIStream is(fp);
|
|
if (!is) throw TException("Can't read color names");
|
|
|
|
std::string tagName;
|
|
if (!is.matchTag(tagName) || tagName != "colors")
|
|
throw TException("Not a color names file");
|
|
|
|
while (!is.matchEndTag()) {
|
|
if (!is.matchTag(tagName)) throw TException("Expected tag");
|
|
if (tagName == "color") {
|
|
QString name, hex;
|
|
name = QString::fromStdString(is.getTagAttribute("name"));
|
|
std::string hexs;
|
|
is >> hexs;
|
|
hex = QString::fromStdString(hexs);
|
|
if (name.size() != 0 && hex.size() != 0)
|
|
table.insert(name.toLower(), hex);
|
|
if (!is.matchEndTag()) throw TException("Expected end tag");
|
|
} else
|
|
throw TException("unexpected tag /" + tagName + "/");
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNames::saveColorTableXML(QMap<QString, QString> &table,
|
|
const TFilePath &fp) {
|
|
TOStream os(fp);
|
|
if (!os) throw TException("Can't write color names");
|
|
|
|
os.openChild("colors");
|
|
|
|
QMap<QString, QString>::const_iterator it;
|
|
std::map<std::string, std::string> attrs;
|
|
for (it = table.cbegin(); it != table.cend(); ++it) {
|
|
std::string nameStd = it.key().toStdString();
|
|
attrs.clear();
|
|
attrs.insert({"name", nameStd});
|
|
os.openChild("color", attrs);
|
|
os << it.value();
|
|
os.closeChild();
|
|
}
|
|
|
|
os.closeChild();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool HexColorNames::parseHexInternal(QString text, TPixel &outPixel) {
|
|
bool ok;
|
|
uint parsedValue = text.toUInt(&ok, 16);
|
|
if (!ok) return false;
|
|
|
|
switch (text.length()) {
|
|
case 8: // #RRGGBBAA
|
|
outPixel.r = parsedValue >> 24;
|
|
outPixel.g = parsedValue >> 16;
|
|
outPixel.b = parsedValue >> 8;
|
|
outPixel.m = parsedValue;
|
|
break;
|
|
case 6: // #RRGGBB
|
|
outPixel.r = parsedValue >> 16;
|
|
outPixel.g = parsedValue >> 8;
|
|
outPixel.b = parsedValue;
|
|
outPixel.m = 255;
|
|
break;
|
|
case 4: // #RGBA
|
|
outPixel.r = (parsedValue >> 12) & 15;
|
|
outPixel.r |= outPixel.r << 4;
|
|
outPixel.g = (parsedValue >> 8) & 15;
|
|
outPixel.g |= outPixel.g << 4;
|
|
outPixel.b = (parsedValue >> 4) & 15;
|
|
outPixel.b |= outPixel.b << 4;
|
|
outPixel.m = parsedValue & 15;
|
|
outPixel.m |= outPixel.m << 4;
|
|
break;
|
|
case 3: // #RGB
|
|
outPixel.r = (parsedValue >> 8) & 15;
|
|
outPixel.r |= outPixel.r << 4;
|
|
outPixel.g = (parsedValue >> 4) & 15;
|
|
outPixel.g |= outPixel.g << 4;
|
|
outPixel.b = parsedValue & 15;
|
|
outPixel.b |= outPixel.b << 4;
|
|
outPixel.m = 255;
|
|
break;
|
|
case 2: // #VV (non-standard)
|
|
outPixel.r = parsedValue;
|
|
outPixel.g = outPixel.r;
|
|
outPixel.b = outPixel.r;
|
|
outPixel.m = 255;
|
|
break;
|
|
case 1: // #V (non-standard)
|
|
outPixel.r = parsedValue & 15;
|
|
outPixel.r |= outPixel.r << 4;
|
|
outPixel.g = outPixel.r;
|
|
outPixel.b = outPixel.r;
|
|
outPixel.m = 255;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool HexColorNames::loadMainFile(bool reload) {
|
|
TFilePath mainCTFp = TEnv::getConfigDir() + COLORNAMES_FILE;
|
|
|
|
// Load main color names
|
|
try {
|
|
if (reload || s_maincolornames.size() == 0) {
|
|
s_maincolornames.clear();
|
|
loadColorTableXML(s_maincolornames, mainCTFp);
|
|
}
|
|
} catch (...) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool HexColorNames::hasUserFile() {
|
|
TFilePath userCTFp = ToonzFolder::getMyModuleDir() + COLORNAMES_FILE;
|
|
return TFileStatus(userCTFp).doesExist();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool HexColorNames::loadUserFile(bool reload) {
|
|
TFilePath userCTFp = ToonzFolder::getMyModuleDir() + COLORNAMES_FILE;
|
|
|
|
// Load user color names (if exists...)
|
|
if (TFileStatus(userCTFp).doesExist()) {
|
|
try {
|
|
if (reload || s_usercolornames.size() == 0) {
|
|
s_usercolornames.clear();
|
|
loadColorTableXML(s_usercolornames, userCTFp);
|
|
}
|
|
} catch (...) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool HexColorNames::loadTempFile(const TFilePath &fp) {
|
|
if (TFileStatus(fp).doesExist()) {
|
|
try {
|
|
s_tempcolornames.clear();
|
|
loadColorTableXML(s_tempcolornames, fp);
|
|
} catch (...) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool HexColorNames::saveUserFile() {
|
|
TFilePath userCTFp = ToonzFolder::getMyModuleDir() + COLORNAMES_FILE;
|
|
|
|
try {
|
|
saveColorTableXML(s_usercolornames, userCTFp);
|
|
} catch (...) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool HexColorNames::saveTempFile(const TFilePath &fp) {
|
|
try {
|
|
saveColorTableXML(s_tempcolornames, fp);
|
|
} catch (...) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNames::clearUserEntries() { s_usercolornames.clear(); }
|
|
|
|
void HexColorNames::clearTempEntries() { s_tempcolornames.clear(); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNames::setUserEntry(const QString &name, const QString &hex) {
|
|
s_usercolornames.insert(name, hex);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNames::setTempEntry(const QString &name, const QString &hex) {
|
|
s_tempcolornames.insert(name, hex);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool HexColorNames::parseText(QString text, TPixel &outPixel) {
|
|
static QRegExp space("\\s");
|
|
text.remove(space);
|
|
if (text.size() == 0) return false;
|
|
if (text[0] == "#") {
|
|
text.remove(0, 1);
|
|
return parseHexInternal(text, outPixel);
|
|
}
|
|
text = text.toLower(); // table names are lowercase
|
|
|
|
// Find color from tables, user takes priority
|
|
QMap<QString, QString>::const_iterator it;
|
|
it = s_usercolornames.constFind(text);
|
|
if (it == s_usercolornames.constEnd()) {
|
|
it = s_maincolornames.constFind(text);
|
|
if (it == s_maincolornames.constEnd()) return false;
|
|
}
|
|
|
|
QString hexText = it.value();
|
|
hexText.remove(space);
|
|
if (hexText[0] == "#") {
|
|
hexText.remove(0, 1);
|
|
return parseHexInternal(hexText, outPixel);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool HexColorNames::parseHex(QString text, TPixel &outPixel) {
|
|
static QRegExp space("\\s");
|
|
text.remove(space);
|
|
if (text.size() == 0) return false;
|
|
if (text[0] == "#") {
|
|
text.remove(0, 1);
|
|
}
|
|
return parseHexInternal(text, outPixel);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
QString HexColorNames::generateHex(TPixel pixel) {
|
|
if (pixel.m == 255) {
|
|
// Opaque, omit alpha
|
|
return QString("#%1%2%3")
|
|
.arg(pixel.r, 2, 16, QLatin1Char('0'))
|
|
.arg(pixel.g, 2, 16, QLatin1Char('0'))
|
|
.arg(pixel.b, 2, 16, QLatin1Char('0'))
|
|
.toUpper();
|
|
} else {
|
|
return QString("#%1%2%3%4")
|
|
.arg(pixel.r, 2, 16, QLatin1Char('0'))
|
|
.arg(pixel.g, 2, 16, QLatin1Char('0'))
|
|
.arg(pixel.b, 2, 16, QLatin1Char('0'))
|
|
.arg(pixel.m, 2, 16, QLatin1Char('0'))
|
|
.toUpper();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//*****************************************************************************
|
|
// Hex line widget
|
|
//*****************************************************************************
|
|
|
|
TEnv::IntVar HexLineEditAutoComplete("HexLineEditAutoComplete", 1);
|
|
|
|
HexLineEdit::HexLineEdit(const QString &contents, QWidget *parent)
|
|
: QLineEdit(contents, parent)
|
|
, m_editing(false)
|
|
, m_color(0, 0, 0)
|
|
, m_completer(nullptr) {
|
|
HexColorNames::loadMainFile(false);
|
|
HexColorNames::loadUserFile(false);
|
|
|
|
if (HexLineEditAutoComplete != 0) onAutoCompleteChanged(true);
|
|
|
|
bool ret = true;
|
|
|
|
ret = ret &&
|
|
connect(HexColorNames::instance(), SIGNAL(autoCompleteChanged(bool)),
|
|
this, SLOT(onAutoCompleteChanged(bool)));
|
|
ret = ret && connect(HexColorNames::instance(), SIGNAL(colorsChanged()), this,
|
|
SLOT(onColorsChanged()));
|
|
assert(ret);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexLineEdit::updateColor() {
|
|
setText(HexColorNames::generateHex(m_color));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexLineEdit::setStyle(TColorStyle &style, int index) {
|
|
setColor(style.getColorParamValue(index));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexLineEdit::setColor(TPixel color) {
|
|
if (m_color != color) {
|
|
m_color = color;
|
|
if (isVisible()) updateColor();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool HexLineEdit::fromText(QString text) {
|
|
bool res = HexColorNames::parseText(text, m_color);
|
|
if (res) updateColor();
|
|
return res;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool HexLineEdit::fromHex(QString text) {
|
|
bool res = HexColorNames::parseHex(text, m_color);
|
|
if (res) updateColor();
|
|
return res;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexLineEdit::mousePressEvent(QMouseEvent *event) {
|
|
QLineEdit::mousePressEvent(event);
|
|
// Make Ctrl key disable select all so the user can click a specific character
|
|
// after a focus-in, this likely will fall into a hidden feature thought.
|
|
bool ctrlDown = event->modifiers() & Qt::ControlModifier;
|
|
if (!m_editing && !ctrlDown) selectAll();
|
|
m_editing = true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexLineEdit::focusOutEvent(QFocusEvent *event) {
|
|
QLineEdit::focusOutEvent(event);
|
|
deselect();
|
|
m_editing = false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexLineEdit::showEvent(QShowEvent *event) {
|
|
QLineEdit::showEvent(event);
|
|
updateColor();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
QCompleter *HexLineEdit::getCompleter() {
|
|
QStringList autolist;
|
|
|
|
// Build words list from all color names tables
|
|
HexColorNames::iterator it;
|
|
for (it = HexColorNames::beginMain(); it != HexColorNames::endMain(); ++it) {
|
|
autolist.append(it.name());
|
|
}
|
|
for (it = HexColorNames::beginUser(); it != HexColorNames::endUser(); ++it) {
|
|
autolist.append(it.name());
|
|
}
|
|
|
|
QCompleter *completer = new QCompleter(autolist);
|
|
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
|
return completer;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexLineEdit::onAutoCompleteChanged(bool enable) {
|
|
if (m_completer) {
|
|
m_completer->deleteLater();
|
|
setCompleter(nullptr);
|
|
m_completer = nullptr;
|
|
}
|
|
if (enable) {
|
|
m_completer = getCompleter();
|
|
setCompleter(m_completer);
|
|
}
|
|
}
|
|
|
|
void HexLineEdit::onColorsChanged() { onAutoCompleteChanged(true); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//*****************************************************************************
|
|
// Hex color names editor
|
|
//*****************************************************************************
|
|
|
|
HexColorNamesEditor::HexColorNamesEditor(QWidget *parent)
|
|
: DVGui::Dialog(parent, true, false, "HexColorNamesEditor")
|
|
, m_selectedItem(nullptr)
|
|
, m_newEntry(false) {
|
|
setWindowTitle(tr("Hex Color Names Editor"));
|
|
setModal(false); // user may want to access main style editor and palettes
|
|
|
|
QPushButton *okButton = new QPushButton(tr("OK"), this);
|
|
QPushButton *applyButton = new QPushButton(tr("Apply"), this);
|
|
QPushButton *closeButton = new QPushButton(tr("Close"), this);
|
|
|
|
m_unsColorButton = new QPushButton(tr("Unselect"));
|
|
m_addColorButton = new QPushButton(tr("Add Color"));
|
|
m_delColorButton = new QPushButton(tr("Delete Color"));
|
|
m_hexLineEdit = new HexLineEdit("", this);
|
|
m_hexLineEdit->setObjectName("HexLineEdit");
|
|
m_hexLineEdit->setFixedWidth(75);
|
|
|
|
// Main default color names
|
|
QGridLayout *mainLay = new QGridLayout();
|
|
QWidget *mainTab = new QWidget();
|
|
mainTab->setLayout(mainLay);
|
|
|
|
m_mainTreeWidget = new QTreeWidget();
|
|
m_mainTreeWidget->setRootIsDecorated(false);
|
|
m_mainTreeWidget->setColumnCount(2);
|
|
m_mainTreeWidget->setColumnWidth(0, 175);
|
|
m_mainTreeWidget->setColumnWidth(1, 50);
|
|
m_mainTreeWidget->setHeaderLabels(QStringList() << "Name"
|
|
<< "Hex value");
|
|
mainLay->addWidget(m_mainTreeWidget, 0, 0);
|
|
|
|
// User defined color names
|
|
QGridLayout *userLay = new QGridLayout();
|
|
QWidget *userTab = new QWidget();
|
|
userTab->setLayout(userLay);
|
|
|
|
m_userTreeWidget = new QTreeWidget();
|
|
m_userTreeWidget->setRootIsDecorated(false);
|
|
m_userTreeWidget->setColumnCount(2);
|
|
m_userTreeWidget->setColumnWidth(0, 175);
|
|
m_userTreeWidget->setColumnWidth(1, 50);
|
|
m_userTreeWidget->setHeaderLabels(QStringList() << "Name"
|
|
<< "Hex value");
|
|
m_colorField = new ColorField(this, true);
|
|
userLay->addWidget(m_userTreeWidget, 0, 0, 1, 4);
|
|
userLay->addWidget(m_unsColorButton, 1, 0);
|
|
userLay->addWidget(m_addColorButton, 1, 1);
|
|
userLay->addWidget(m_delColorButton, 1, 2);
|
|
userLay->addWidget(m_hexLineEdit, 1, 3);
|
|
userLay->addWidget(m_colorField, 2, 0, 1, 4);
|
|
|
|
// Set delegate
|
|
m_userEditingDelegate = new HexColorNamesEditingDelegate(m_userTreeWidget);
|
|
m_mainTreeWidget->setItemDelegate(m_userEditingDelegate);
|
|
m_userTreeWidget->setItemDelegate(m_userEditingDelegate);
|
|
populateMainList(false);
|
|
|
|
// Tabs
|
|
QTabWidget *tab = new QTabWidget();
|
|
tab->addTab(userTab, tr("User Defined Colors"));
|
|
tab->addTab(mainTab, tr("Default Main Colors"));
|
|
tab->setObjectName("hexTabWidget");
|
|
|
|
// Bottom widgets
|
|
QHBoxLayout *bottomLay = new QHBoxLayout();
|
|
m_autoCompleteCb = new QCheckBox(tr("Enable Auto-Complete"));
|
|
m_autoCompleteCb->setChecked(HexLineEditAutoComplete != 0);
|
|
m_autoCompleteCb->setSizePolicy(QSizePolicy::Expanding,
|
|
QSizePolicy::Preferred);
|
|
m_importButton = new QPushButton(tr("Import"));
|
|
m_exportButton = new QPushButton(tr("Export"));
|
|
bottomLay->setMargin(8);
|
|
bottomLay->setSpacing(5);
|
|
bottomLay->addWidget(m_autoCompleteCb);
|
|
bottomLay->addWidget(m_importButton);
|
|
bottomLay->addWidget(m_exportButton);
|
|
|
|
m_topLayout->setContentsMargins(0, 0, 0, 0);
|
|
m_topLayout->addWidget(tab);
|
|
m_topLayout->addLayout(bottomLay);
|
|
|
|
addButtonBarWidget(okButton, applyButton, closeButton);
|
|
|
|
bool ret = true;
|
|
|
|
ret = ret && connect(m_userEditingDelegate,
|
|
SIGNAL(editingStarted(const QModelIndex &)), this,
|
|
SLOT(onEditingStarted(const QModelIndex &)));
|
|
ret = ret && connect(m_userEditingDelegate,
|
|
SIGNAL(editingFinished(const QModelIndex &)), this,
|
|
SLOT(onEditingFinished(const QModelIndex &)));
|
|
ret = ret && connect(m_userEditingDelegate, SIGNAL(editingClosed()), this,
|
|
SLOT(onEditingClosed()));
|
|
|
|
ret =
|
|
ret &&
|
|
connect(m_userTreeWidget,
|
|
SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
|
|
this,
|
|
SLOT(onCurrentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
|
|
ret =
|
|
ret && connect(m_colorField, SIGNAL(colorChanged(const TPixel32 &, bool)),
|
|
this, SLOT(onColorFieldChanged(const TPixel32 &, bool)));
|
|
ret = ret && connect(m_hexLineEdit, SIGNAL(editingFinished()), this,
|
|
SLOT(onHexChanged()));
|
|
|
|
ret = ret &&
|
|
connect(m_unsColorButton, SIGNAL(pressed()), this, SLOT(onDeselect()));
|
|
ret = ret &&
|
|
connect(m_addColorButton, SIGNAL(pressed()), this, SLOT(onAddColor()));
|
|
ret = ret &&
|
|
connect(m_delColorButton, SIGNAL(pressed()), this, SLOT(onDelColor()));
|
|
ret =
|
|
ret && connect(m_importButton, SIGNAL(pressed()), this, SLOT(onImport()));
|
|
ret =
|
|
ret && connect(m_exportButton, SIGNAL(pressed()), this, SLOT(onExport()));
|
|
|
|
ret = ret && connect(okButton, SIGNAL(pressed()), this, SLOT(onOK()));
|
|
ret = ret && connect(applyButton, SIGNAL(pressed()), this, SLOT(onApply()));
|
|
ret = ret && connect(closeButton, SIGNAL(pressed()), this, SLOT(close()));
|
|
assert(ret);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
QTreeWidgetItem *HexColorNamesEditor::addEntry(QTreeWidget *tree,
|
|
const QString &name,
|
|
const QString &hex,
|
|
bool editable) {
|
|
TPixel pixel = TPixel(0, 0, 0);
|
|
HexColorNames::parseHex(hex, pixel);
|
|
|
|
QPixmap pixm(16, 16);
|
|
pixm.fill(QColor(pixel.r, pixel.g, pixel.b, pixel.m));
|
|
|
|
QTreeWidgetItem *treeItem = new QTreeWidgetItem(tree);
|
|
treeItem->setText(0, name);
|
|
treeItem->setIcon(1, QIcon(pixm));
|
|
treeItem->setText(1, hex);
|
|
if (!editable)
|
|
treeItem->setFlags(treeItem->flags() & ~Qt::ItemIsSelectable);
|
|
else
|
|
treeItem->setFlags(treeItem->flags() | Qt::ItemIsEditable);
|
|
|
|
return treeItem;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool HexColorNamesEditor::updateUserHexEntry(QTreeWidgetItem *treeItem,
|
|
const TPixel32 &color) {
|
|
if (treeItem) {
|
|
QPixmap pixm(16, 16);
|
|
pixm.fill(QColor(color.r, color.g, color.b, color.m));
|
|
|
|
treeItem->setIcon(1, QIcon(pixm));
|
|
treeItem->setText(1, HexColorNames::generateHex(color));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool HexColorNamesEditor::nameValid(const QString& name) {
|
|
if (name.isEmpty()) return false;
|
|
return name.count(QRegExp("[\\\\#<>\"']")) == 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool HexColorNamesEditor::nameExists(const QString &name,
|
|
QTreeWidgetItem *self) {
|
|
for (int i = 0; i < m_userTreeWidget->topLevelItemCount(); ++i) {
|
|
QTreeWidgetItem *item = m_userTreeWidget->topLevelItem(i);
|
|
if (item == self) continue;
|
|
if (name.compare(item->text(0), Qt::CaseInsensitive) == 0) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::deselectItem(bool treeFocus) {
|
|
if (m_newEntry) return;
|
|
|
|
m_userTreeWidget->setCurrentItem(nullptr);
|
|
if (treeFocus) m_userTreeWidget->setFocus();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::deleteCurrentItem(bool deselect) {
|
|
if (m_newEntry) return;
|
|
|
|
QTreeWidgetItem *treeItem = m_userTreeWidget->currentItem();
|
|
if (treeItem) delete treeItem;
|
|
m_selectedItem = nullptr;
|
|
if (deselect) m_userTreeWidget->setCurrentItem(nullptr);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::populateMainList(bool reload) {
|
|
HexColorNames::loadMainFile(reload);
|
|
|
|
HexColorNames::iterator it;
|
|
m_mainTreeWidget->clear();
|
|
for (it = HexColorNames::beginMain(); it != HexColorNames::endMain(); ++it) {
|
|
addEntry(m_mainTreeWidget, it.name(), it.value(), false);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::populateUserList(bool reload) {
|
|
HexColorNames::loadUserFile(reload);
|
|
|
|
HexColorNames::iterator it;
|
|
m_userTreeWidget->clear();
|
|
for (it = HexColorNames::beginUser(); it != HexColorNames::endUser(); ++it) {
|
|
if (!nameExists(it.name(), nullptr))
|
|
addEntry(m_userTreeWidget, it.name(), it.value(), true);
|
|
}
|
|
|
|
m_userTreeWidget->sortItems(0, Qt::SortOrder::AscendingOrder);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::showEvent(QShowEvent *e) {
|
|
populateUserList(false);
|
|
|
|
deselectItem(false);
|
|
m_delColorButton->setEnabled(false);
|
|
m_unsColorButton->setEnabled(false);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::keyPressEvent(QKeyEvent *event) {
|
|
// Blocking dialog default actions is actually desirable
|
|
// for example when user press Esc or Enter twice while
|
|
// editing it might close the dialog by accident.
|
|
|
|
if (!m_userTreeWidget->hasFocus()) return;
|
|
|
|
switch (event->key()) {
|
|
case Qt::Key_F5:
|
|
populateMainList(true);
|
|
populateUserList(true);
|
|
m_mainTreeWidget->update();
|
|
m_userTreeWidget->update();
|
|
break;
|
|
case Qt::Key_Escape:
|
|
deselectItem(true);
|
|
break;
|
|
case Qt::Key_Delete:
|
|
deleteCurrentItem(false);
|
|
break;
|
|
case Qt::Key_Insert:
|
|
onAddColor();
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::focusInEvent(QFocusEvent *e) {
|
|
QWidget::focusInEvent(e);
|
|
qApp->installEventFilter(this);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::focusOutEvent(QFocusEvent *e) {
|
|
QWidget::focusOutEvent(e);
|
|
qApp->removeEventFilter(this);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool HexColorNamesEditor::eventFilter(QObject *obj, QEvent *e) {
|
|
if (e->type() == QEvent::Shortcut ||
|
|
e->type() == QEvent::ShortcutOverride) {
|
|
e->accept();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::onCurrentItemChanged(QTreeWidgetItem *current,
|
|
QTreeWidgetItem *previous) {
|
|
m_selectedItem = current;
|
|
m_delColorButton->setEnabled(current);
|
|
m_unsColorButton->setEnabled(current);
|
|
|
|
if (!current) return;
|
|
m_selectedName = current->text(0);
|
|
m_selectedHex = current->text(1);
|
|
m_selectedColn = 0;
|
|
|
|
// Modify color field
|
|
TPixel pixel(0, 0, 0);
|
|
if (HexColorNames::parseHex(m_selectedHex, pixel)) {
|
|
m_colorField->setColor(pixel);
|
|
m_hexLineEdit->setColor(pixel);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::onEditingStarted(const QModelIndex &modelIndex) {
|
|
QTreeWidgetItem *item = (QTreeWidgetItem *)modelIndex.internalPointer();
|
|
int column = modelIndex.column();
|
|
onItemStarted(item, column);
|
|
}
|
|
|
|
void HexColorNamesEditor::onEditingFinished(const QModelIndex &modelIndex) {
|
|
QTreeWidgetItem *item = (QTreeWidgetItem *)modelIndex.internalPointer();
|
|
int column = modelIndex.column();
|
|
onItemFinished(item, column);
|
|
}
|
|
|
|
void HexColorNamesEditor::onEditingClosed() {
|
|
onItemFinished(m_selectedItem, m_selectedColn);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::onItemStarted(QTreeWidgetItem *item, int column) {
|
|
m_selectedName = item->text(0);
|
|
m_selectedHex = item->text(1);
|
|
m_selectedColn = 0;
|
|
m_selectedItem = item;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::onItemFinished(QTreeWidgetItem *item, int column) {
|
|
if (!m_selectedItem || !item) return;
|
|
m_delColorButton->setEnabled(true);
|
|
m_unsColorButton->setEnabled(true);
|
|
|
|
try {
|
|
if (m_selectedItem == item) {
|
|
QString text = item->text(column);
|
|
if (column == 0) {
|
|
// Edit Name
|
|
static QRegExp space("\\s");
|
|
text.remove(space);
|
|
text = text.toLower();
|
|
if (text.isEmpty()) throw "";
|
|
if (!nameValid(text))
|
|
throw "Color name is not valid.\nFollowing characters can't be used: \\ # < > \" '";
|
|
if (nameExists(text, item)) throw "Color name already exists.\nPlease use another name.";
|
|
item->setText(0, text);
|
|
m_userTreeWidget->sortItems(0, Qt::SortOrder::AscendingOrder);
|
|
} else {
|
|
// Edit Hex
|
|
TPixel pixel;
|
|
if (HexColorNames::parseHex(text, pixel)) {
|
|
m_colorField->setColor(pixel);
|
|
m_hexLineEdit->setColor(pixel);
|
|
updateUserHexEntry(item, pixel);
|
|
} else {
|
|
item->setText(1, m_selectedHex);
|
|
}
|
|
}
|
|
}
|
|
} catch (const char *reason) {
|
|
m_selectedItem = nullptr;
|
|
if (m_selectedName.isEmpty()) {
|
|
delete item;
|
|
} else {
|
|
item->setText(0, m_selectedName);
|
|
}
|
|
if (strlen(reason)) DVGui::warning(tr(reason));
|
|
} catch (...) {
|
|
m_selectedItem = nullptr;
|
|
delete item;
|
|
}
|
|
m_newEntry = false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::onColorFieldChanged(const TPixel32 &color,
|
|
bool isDragging) {
|
|
QTreeWidgetItem *treeItem = m_userTreeWidget->currentItem();
|
|
if (updateUserHexEntry(treeItem, color)) {
|
|
m_userTreeWidget->setCurrentItem(treeItem);
|
|
}
|
|
m_hexLineEdit->setColor(color);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::onHexChanged() {
|
|
QTreeWidgetItem *treeItem = m_userTreeWidget->currentItem();
|
|
if (m_hexLineEdit->fromText(m_hexLineEdit->text())) {
|
|
TPixel pixel = m_hexLineEdit->getColor();
|
|
updateUserHexEntry(treeItem, pixel);
|
|
m_colorField->setColor(pixel);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::onAddColor() {
|
|
if (m_newEntry) return;
|
|
|
|
TPixel pixel = m_colorField->getColor();
|
|
QString hex = HexColorNames::generateHex(pixel);
|
|
QTreeWidgetItem *treeItem = addEntry(m_userTreeWidget, "", hex, true);
|
|
|
|
m_userTreeWidget->setCurrentItem(treeItem);
|
|
onItemStarted(treeItem, 0);
|
|
m_newEntry = true;
|
|
m_userTreeWidget->editItem(treeItem, 0);
|
|
|
|
m_delColorButton->setEnabled(false);
|
|
m_unsColorButton->setEnabled(false);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::onDelColor() {
|
|
if (m_newEntry) return;
|
|
|
|
deleteCurrentItem(true);
|
|
m_userTreeWidget->setFocus();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::onDeselect() { deselectItem(false); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::onImport() {
|
|
QString fileName = QFileDialog::getOpenFileName(
|
|
this, tr("Open Color Names"), QString(),
|
|
tr("Text or XML (*.txt *.xml);;Text files (*.txt);;XML files (*.xml)"));
|
|
if (fileName.isEmpty()) return;
|
|
|
|
QMessageBox::StandardButton ret = QMessageBox::question(
|
|
this, tr("Hex Color Names Import"), tr("Do you want to merge with existing entries?"),
|
|
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No |
|
|
QMessageBox::Cancel));
|
|
if (ret == QMessageBox::Cancel) return;
|
|
|
|
if (!HexColorNames::loadTempFile(TFilePath(fileName))) {
|
|
DVGui::warning(tr("Error importing color names XML"));
|
|
}
|
|
HexColorNames::iterator it;
|
|
if (ret == QMessageBox::No) m_userTreeWidget->clear();
|
|
for (it = HexColorNames::beginTemp(); it != HexColorNames::endTemp(); ++it) {
|
|
if (!nameExists(it.name(), nullptr))
|
|
addEntry(m_userTreeWidget, it.name(), it.value(), true);
|
|
}
|
|
HexColorNames::clearTempEntries();
|
|
m_userTreeWidget->sortItems(0, Qt::SortOrder::AscendingOrder);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::onExport() {
|
|
QString fileName = QFileDialog::getSaveFileName(
|
|
this, tr("Save Color Names"), QString(),
|
|
tr("XML files (*.xml);;Text files (*.txt)"));
|
|
if (fileName.isEmpty()) return;
|
|
|
|
HexColorNames::clearTempEntries();
|
|
for (int i = 0; i < m_userTreeWidget->topLevelItemCount(); ++i) {
|
|
QTreeWidgetItem *item = m_userTreeWidget->topLevelItem(i);
|
|
HexColorNames::setTempEntry(item->text(0), item->text(1));
|
|
}
|
|
if (!HexColorNames::saveTempFile(TFilePath(fileName))) {
|
|
DVGui::warning(tr("Error exporting color names XML"));
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HexColorNamesEditor::onOK() {
|
|
onApply();
|
|
close();
|
|
};
|
|
|
|
void HexColorNamesEditor::onApply() {
|
|
HexColorNames::clearUserEntries();
|
|
for (int i = 0; i < m_userTreeWidget->topLevelItemCount(); ++i) {
|
|
QTreeWidgetItem *item = m_userTreeWidget->topLevelItem(i);
|
|
HexColorNames::setUserEntry(item->text(0), item->text(1));
|
|
}
|
|
HexColorNames::saveUserFile();
|
|
HexColorNames::instance()->emitChanged();
|
|
|
|
bool oldAutoCompState = (HexLineEditAutoComplete != 0);
|
|
bool newAutoCompState = m_autoCompleteCb->isChecked();
|
|
if (oldAutoCompState != newAutoCompState) {
|
|
HexLineEditAutoComplete = newAutoCompState ? 1 : 0;
|
|
HexColorNames::instance()->emitAutoComplete(newAutoCompState);
|
|
}
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|