File indexing completed on 2024-05-12 16:01:53
0001 /* 0002 * SPDX-FileCopyrightText: 2018 Michael Zhou <simeirxh@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include <QHash> 0008 #include <QString> 0009 #include <QScopedPointer> 0010 #include <QPointer> 0011 #include <QFormLayout> 0012 #include <QCheckBox> 0013 #include <QLineEdit> 0014 #include <QLabel> 0015 #include <QSpinBox> 0016 #include <QComboBox> 0017 #include <QMessageBox> 0018 0019 #include <KoDialog.h> 0020 #include <KoFileDialog.h> 0021 #include <KoColorSet.h> 0022 #include <KisSwatchGroup.h> 0023 #include <kis_signal_compressor.h> 0024 #include <KisViewManager.h> 0025 #include <KisDocument.h> 0026 #include <KoResourceServer.h> 0027 #include <KisStorageModel.h> 0028 #include <KoResourceServerProvider.h> 0029 #include <KisPaletteModel.h> 0030 #include <kis_color_button.h> 0031 0032 #include "KisPaletteEditor.h" 0033 #include <KisGlobalResourcesInterface.h> 0034 #include <KisResourceUserOperations.h> 0035 0036 struct KisPaletteEditor::PaletteInfo { 0037 QString name; 0038 QString filename; 0039 int columnCount; 0040 QString storageLocation; 0041 QHash<QString, KisSwatchGroup> groups; 0042 }; 0043 0044 struct KisPaletteEditor::Private 0045 { 0046 bool isNameModified {false}; 0047 bool isColumnCountModified {false}; 0048 QSet<QString> modifiedGroupNames; // key is original group name 0049 QSet<QString> newGroupNames; 0050 QSet<QString> keepColorGroups; 0051 QSet<QString> pathsToRemove; 0052 QString groupBeingRenamed; 0053 QPointer<KisPaletteModel> model; 0054 QPointer<KisViewManager> view; 0055 PaletteInfo modified; 0056 QPointer<KoDialog> query; 0057 KoResourceServer<KoColorSet> *rServer {0}; 0058 0059 QPalette normalPalette; 0060 QPalette warnPalette; 0061 0062 }; 0063 0064 KisPaletteEditor::KisPaletteEditor(QObject *parent) 0065 : QObject(parent) 0066 , m_d(new Private) 0067 { 0068 m_d->rServer = KoResourceServerProvider::instance()->paletteServer(); 0069 m_d->warnPalette.setColor(QPalette::Text, Qt::red); 0070 } 0071 0072 KisPaletteEditor::~KisPaletteEditor() 0073 { } 0074 0075 void KisPaletteEditor::setPaletteModel(KisPaletteModel *model) 0076 { 0077 if (!model) { return; } 0078 m_d->model = model; 0079 slotPaletteChanged(); 0080 connect(model, SIGNAL(sigPaletteChanged()), SLOT(slotPaletteChanged())); 0081 connect(model, SIGNAL(sigPaletteModified()), SLOT(slotSetDocumentModified())); 0082 } 0083 0084 void KisPaletteEditor::setView(KisViewManager *view) 0085 { 0086 m_d->view = view; 0087 } 0088 0089 KoColorSetSP KisPaletteEditor::addPalette() 0090 { 0091 if (!m_d->view) { return 0; } 0092 if (!m_d->view->document()) { return 0; } 0093 0094 KoColorSetSP colorSet(new KoColorSet()); 0095 0096 KoDialog dialog; 0097 QFormLayout *layout = new QFormLayout(dialog.mainWidget()); 0098 QLineEdit *le = new QLineEdit(i18nc("Default name for a new palette","New Palette")); 0099 layout->addRow(i18n("New palette name:"), le); 0100 0101 QString saveLocation = m_d->rServer->saveLocation(); 0102 0103 0104 QCheckBox *chkSaveInDocument = new QCheckBox(i18n("Save Palette in the Current Document")); 0105 chkSaveInDocument->setChecked(false); 0106 layout->addRow(chkSaveInDocument); 0107 0108 if (dialog.exec() != QDialog::Accepted) { return 0; } 0109 0110 QString name = le->text(); 0111 colorSet->setPaletteType(KoColorSet::KPL); 0112 colorSet->setValid(true); 0113 colorSet->setName(name); 0114 colorSet->setFilename(name.split(" ").join("_")+colorSet->defaultFileExtension()); 0115 0116 QString resourceLocation = ""; 0117 if (chkSaveInDocument->isChecked()) { 0118 resourceLocation = m_d->view->document()->linkedResourcesStorageId(); 0119 } 0120 0121 if (KisResourceUserOperations::addResourceWithUserInput(m_d->view->mainWindowAsQWidget(), colorSet, resourceLocation)) { 0122 0123 return colorSet; 0124 0125 } 0126 0127 return 0; 0128 } 0129 0130 KoColorSetSP KisPaletteEditor::importPalette() 0131 { 0132 KoFileDialog dialog(nullptr, KoFileDialog::OpenFile, "Open Palette"); 0133 dialog.setCaption(i18n("Import Palette")); 0134 0135 dialog.setDefaultDir(QDir::homePath()); 0136 dialog.setMimeTypeFilters(QStringList() << "application/x-krita-palette" << "application/x-gimp-color-palette"); 0137 0138 QString filename = dialog.filename(); 0139 if (filename.isEmpty()) { 0140 return nullptr; 0141 } 0142 0143 QMessageBox messageBox; 0144 messageBox.setText(i18n("Do you want to store this palette in your current image?")); 0145 messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); 0146 QString storageLocation = ""; 0147 if (messageBox.exec() == QMessageBox::Yes) { 0148 storageLocation = m_d->view->document()->linkedResourcesStorageId(); 0149 } 0150 KoResourceSP resource = KisResourceUserOperations::importResourceFileWithUserInput(m_d->view->mainWindowAsQWidget(), storageLocation, ResourceType::Palettes, filename); 0151 0152 KoColorSetSP palette; 0153 if (resource) { 0154 palette = resource.dynamicCast<KoColorSet>(); 0155 } 0156 0157 return palette; 0158 } 0159 0160 void KisPaletteEditor::removePalette(KoColorSetSP cs) 0161 { 0162 if (!m_d->view) { return; } 0163 if (!m_d->view->document()) { return; } 0164 if (!cs) { return; } 0165 m_d->rServer->removeResourceFromServer(cs); 0166 } 0167 0168 int KisPaletteEditor::rowNumberOfGroup(const QString &oriName) const 0169 { 0170 if (!m_d->modified.groups.contains(oriName)) { return 0; } 0171 return m_d->modified.groups[oriName].rowCount(); 0172 } 0173 0174 bool KisPaletteEditor::duplicateExistsGroupName(const QString &name) const 0175 { 0176 if (name == m_d->groupBeingRenamed) { return false; } 0177 Q_FOREACH (const KisSwatchGroup &g, m_d->modified.groups.values()) { 0178 if (name == g.name()) { return true; } 0179 } 0180 return false; 0181 } 0182 0183 bool KisPaletteEditor::duplicateExistsOriginalGroupName(const QString &name) const 0184 { 0185 return m_d->modified.groups.contains(name); 0186 } 0187 0188 QString KisPaletteEditor::oldNameFromNewName(const QString &newName) const 0189 { 0190 Q_FOREACH (const QString &oldGroupName, m_d->modified.groups.keys()) { 0191 if (m_d->modified.groups[oldGroupName].name() == newName) { 0192 return oldGroupName; 0193 } 0194 } 0195 return QString(); 0196 } 0197 0198 void KisPaletteEditor::rename(const QString &newName) 0199 { 0200 if (newName.isEmpty()) { return; } 0201 m_d->isNameModified = true; 0202 m_d->modified.name = newName; 0203 } 0204 0205 void KisPaletteEditor::changeColCount(int newCount) 0206 { 0207 m_d->isColumnCountModified = true; 0208 m_d->modified.columnCount = newCount; 0209 } 0210 0211 QString KisPaletteEditor::addGroup() 0212 { 0213 KoDialog dialog; 0214 m_d->query = &dialog; 0215 0216 QVBoxLayout *layout = new QVBoxLayout(dialog.mainWidget()); 0217 0218 layout->addWidget(new QLabel(i18n("New swatch group name:"))); 0219 QLineEdit *leName = new QLineEdit(newGroupName()); 0220 connect(leName, SIGNAL(textChanged(QString)), SLOT(slotGroupNameChanged(QString))); 0221 layout->addWidget(leName); 0222 layout->addWidget(new QLabel(i18n("Rows of swatches in group:"))); 0223 QSpinBox *spxRow = new QSpinBox(); 0224 spxRow->setValue(20); 0225 layout->addWidget(spxRow); 0226 0227 if (dialog.exec() != QDialog::Accepted) { return QString(); } 0228 if (duplicateExistsGroupName(leName->text())) { return QString(); } 0229 0230 QString realName = leName->text(); 0231 QString name = realName; 0232 if (duplicateExistsOriginalGroupName(name)) { 0233 name = newGroupName(); 0234 } 0235 m_d->modified.groups[name] = KisSwatchGroup(); 0236 KisSwatchGroup &newGroup = m_d->modified.groups[name]; 0237 newGroup.setName(realName); 0238 m_d->newGroupNames.insert(name); 0239 newGroup.setRowCount(spxRow->value()); 0240 return realName; 0241 } 0242 0243 bool KisPaletteEditor::removeGroup(const QString &name) 0244 { 0245 KoDialog dialog; 0246 dialog.setWindowTitle(i18nc("@title:dialog", "Removing Swatch Group")); 0247 QFormLayout *editableItems = new QFormLayout(dialog.mainWidget()); 0248 QCheckBox *chkKeep = new QCheckBox(); 0249 0250 editableItems->addRow(i18nc("Shows up when deleting a swatch group", "Keep the Colors"), chkKeep); 0251 if (dialog.exec() != KoDialog::Accepted) { return false; } 0252 0253 m_d->modified.groups.remove(name); 0254 m_d->newGroupNames.remove(name); 0255 if (chkKeep->isChecked()) { 0256 m_d->keepColorGroups.insert(name); 0257 } 0258 return true; 0259 } 0260 0261 QString KisPaletteEditor::renameGroup(const QString &oldName) 0262 { 0263 if (oldName.isEmpty() || oldName == KoColorSet::GLOBAL_GROUP_NAME) { return QString(); } 0264 0265 KoDialog dialog; 0266 m_d->query = &dialog; 0267 m_d->groupBeingRenamed = m_d->modified.groups[oldName].name(); 0268 0269 QFormLayout *form = new QFormLayout(dialog.mainWidget()); 0270 0271 QLineEdit *leNewName = new QLineEdit(); 0272 connect(leNewName, SIGNAL(textChanged(QString)), SLOT(slotGroupNameChanged(QString))); 0273 leNewName->setText(m_d->modified.groups[oldName].name()); 0274 0275 form->addRow(i18n("New swatch group name:"), leNewName); 0276 0277 if (dialog.exec() != KoDialog::Accepted) { return QString(); } 0278 if (leNewName->text().isEmpty()) { return QString(); } 0279 if (duplicateExistsGroupName(leNewName->text())) { return QString(); } 0280 0281 m_d->modified.groups[oldName].setName(leNewName->text()); 0282 m_d->modifiedGroupNames.insert(oldName); 0283 0284 return leNewName->text(); 0285 } 0286 0287 void KisPaletteEditor::slotGroupNameChanged(const QString &newName) 0288 { 0289 QLineEdit *leGroupName = qobject_cast<QLineEdit*>(sender()); 0290 if (duplicateExistsGroupName(newName) || newName == QString()) { 0291 leGroupName->setPalette(m_d->warnPalette); 0292 if (m_d->query->button(KoDialog::Ok)) { 0293 m_d->query->button(KoDialog::Ok)->setEnabled(false); 0294 } 0295 return; 0296 } 0297 leGroupName->setPalette(m_d->normalPalette); 0298 if (m_d->query->button(KoDialog::Ok)) { 0299 m_d->query->button(KoDialog::Ok)->setEnabled(true); 0300 } 0301 } 0302 0303 void KisPaletteEditor::changeGroupRowCount(const QString &name, int newRowCount) 0304 { 0305 if (!m_d->modified.groups.contains(name)) { return; } 0306 m_d->modified.groups[name].setRowCount(newRowCount); 0307 m_d->modifiedGroupNames.insert(name); 0308 } 0309 0310 void KisPaletteEditor::setStorageLocation(QString location) 0311 { 0312 m_d->modified.storageLocation = location; 0313 } 0314 0315 void KisPaletteEditor::setEntry(const KoColor &color, const QModelIndex &index) 0316 { 0317 Q_ASSERT(m_d->model); 0318 if (!m_d->view) { return; } 0319 if (!m_d->view->document()) { return; } 0320 KisSwatch c = KisSwatch(color); 0321 c.setId(QString::number(m_d->model->colorSet()->colorCount() + 1)); 0322 c.setName(i18nc("Default name for a color swatch","Color %1", QString::number(m_d->model->colorSet()->colorCount()+1))); 0323 m_d->model->setEntry(c, index); 0324 } 0325 0326 void KisPaletteEditor::slotSetDocumentModified() 0327 { 0328 if (m_d->modified.storageLocation == m_d->view->document()->linkedResourcesStorageId()) { 0329 updatePalette(); 0330 KisResourceUserOperations::updateResourceWithUserInput(m_d->view->mainWindowAsQWidget(), m_d->model->colorSet()); 0331 m_d->view->document()->setModified(true); 0332 } 0333 m_d->model->colorSet()->setDirty(true); 0334 } 0335 0336 void KisPaletteEditor::removeEntry(const QModelIndex &index) 0337 { 0338 Q_ASSERT(m_d->model); 0339 if (!m_d->view) { return; } 0340 if (!m_d->view->document()) { return; } 0341 if (qvariant_cast<bool>(index.data(KisPaletteModel::IsGroupNameRole))) { 0342 removeGroup(qvariant_cast<QString>(index.data(KisPaletteModel::GroupNameRole))); 0343 } else { 0344 m_d->model->removeEntry(index, false); 0345 } 0346 updatePalette(); 0347 } 0348 0349 void KisPaletteEditor::modifyEntry(const QModelIndex &index) 0350 { 0351 if (!m_d->view) { return; } 0352 if (!m_d->view->document()) { return; } 0353 0354 KoDialog dialog; 0355 dialog.setCaption(i18nc("@title:dialog", "Add a new Color Swatch")); 0356 QFormLayout *editableItems = new QFormLayout(dialog.mainWidget()); 0357 0358 QString groupName = qvariant_cast<QString>(index.data(Qt::DisplayRole)); 0359 if (qvariant_cast<bool>(index.data(KisPaletteModel::IsGroupNameRole))) { 0360 renameGroup(groupName); 0361 updatePalette(); 0362 } 0363 else { 0364 0365 QLineEdit *lnIDName = new QLineEdit(); 0366 QLineEdit *lnGroupName = new QLineEdit(); 0367 KisColorButton *bnColor = new KisColorButton(); 0368 QCheckBox *chkSpot = new QCheckBox(); 0369 chkSpot->setToolTip(i18nc("@info:tooltip", "A spot color is a color that the printer is able to print without mixing the paints it has available to it. The opposite is called a process color.")); 0370 0371 KisSwatch entry = m_d->model->getEntry(index); 0372 0373 editableItems->addRow(i18n("Swatch ID:"), lnIDName); 0374 editableItems->addRow(i18n("Color swatch name:"), lnGroupName); 0375 editableItems->addRow(i18nc("Color as the Color of a Swatch in a Palette", "Color:"), bnColor); 0376 editableItems->addRow(i18n("Spot color:"), chkSpot); 0377 0378 lnGroupName->setText(entry.name()); 0379 lnIDName->setText(entry.id()); 0380 bnColor->setColor(entry.color()); 0381 chkSpot->setChecked(entry.spotColor()); 0382 0383 if (dialog.exec() == KoDialog::Accepted) { 0384 entry.setName(lnGroupName->text()); 0385 entry.setId(lnIDName->text()); 0386 entry.setColor(bnColor->color()); 0387 entry.setSpotColor(chkSpot->isChecked()); 0388 m_d->model->setEntry(entry, index); 0389 } 0390 } 0391 } 0392 0393 void KisPaletteEditor::addEntry(const KoColor &color) 0394 { 0395 Q_ASSERT(m_d->model); 0396 if (!m_d->view) { return; } 0397 if (!m_d->view->document()) { return; } 0398 0399 KoDialog dialog; 0400 dialog.setWindowTitle(i18nc("@title:dialog", "Add a new Color Swatch")); 0401 0402 QFormLayout *editableItems = new QFormLayout(dialog.mainWidget()); 0403 0404 QComboBox *cmbGroups = new QComboBox(); 0405 cmbGroups->addItems(m_d->model->colorSet()->getGroupNames()); 0406 cmbGroups->setCurrentIndex(0); 0407 0408 QLineEdit *lnIDName = new QLineEdit(); 0409 lnIDName->setText(QString::number(m_d->model->colorSet()->colorCount() + 1)); 0410 0411 QLineEdit *lnName = new QLineEdit(); 0412 lnName->setText(i18nc("Default name for a color swatch","Color %1", QString::number(m_d->model->colorSet()->colorCount()+1))); 0413 0414 KisColorButton *bnColor = new KisColorButton(); 0415 bnColor->setColor(color); 0416 0417 QCheckBox *chkSpot = new QCheckBox(); 0418 chkSpot->setChecked(false); 0419 chkSpot->setToolTip(i18nc("@info:tooltip", "A spot color is a color that the printer is able to print without mixing the paints it has available to it. The opposite is called a process color.")); 0420 0421 editableItems->addRow(i18n("Swatch Group:"), cmbGroups); 0422 editableItems->addRow(i18n("Swatch ID:"), lnIDName); 0423 editableItems->addRow(i18n("Color swatch name:"), lnName); 0424 editableItems->addRow(i18nc("Color as the Color of a Swatch in a Palette", "Color:"), bnColor); 0425 editableItems->addRow(i18n("Spot color:"), chkSpot); 0426 0427 if (dialog.exec() != KoDialog::Accepted) { return; } 0428 0429 QString groupName = cmbGroups->currentText(); 0430 0431 KisSwatch newEntry; 0432 newEntry.setColor(bnColor->color()); 0433 newEntry.setName(lnName->text()); 0434 newEntry.setId(lnIDName->text()); 0435 newEntry.setSpotColor(chkSpot->isChecked()); 0436 m_d->model->addEntry(newEntry, groupName); 0437 m_d->modifiedGroupNames.insert(groupName); 0438 m_d->modified.groups[groupName].addEntry(newEntry); 0439 } 0440 0441 bool KisPaletteEditor::isModified() const 0442 { 0443 if (m_d->model->colorSet()) { 0444 return m_d->model->colorSet()->isDirty(); 0445 } else { 0446 return false; 0447 } 0448 } 0449 0450 void KisPaletteEditor::updatePalette() 0451 { 0452 dbgResources << Q_FUNC_INFO << "updating the palette model inside the palette editor object"; 0453 Q_ASSERT(m_d->model); 0454 Q_ASSERT(m_d->model->colorSet()); 0455 if (!m_d->view) { return; } 0456 if (!m_d->view->document()) { return; } 0457 KoColorSetSP palette = m_d->model->colorSet(); 0458 PaletteInfo &modified = m_d->modified; 0459 0460 if (m_d->isColumnCountModified) { 0461 palette->setColumnCount(modified.columnCount); 0462 } 0463 if (m_d->isNameModified) { 0464 KisResourceUserOperations::renameResourceWithUserInput(m_d->view->mainWindowAsQWidget(), palette, m_d->modified.name); 0465 } 0466 QString resourceLocation = m_d->model->colorSet()->storageLocation(); 0467 if (resourceLocation != m_d->modified.storageLocation) { 0468 // We need functionality for moving the resource to the new resource storage... 0469 } 0470 0471 Q_FOREACH (const QString &groupName, palette->getGroupNames()) { 0472 if (!modified.groups.contains(groupName)) { 0473 m_d->model->removeGroup(groupName, m_d->keepColorGroups.contains(groupName)); 0474 } 0475 } 0476 m_d->keepColorGroups.clear(); 0477 Q_FOREACH (const QString &groupName, palette->getGroupNames()) { 0478 if (m_d->modifiedGroupNames.contains(groupName)) { 0479 m_d->model->setRowNumber(groupName, modified.groups[groupName].rowCount()); 0480 if (groupName != modified.groups[groupName].name()) { 0481 m_d->model->renameGroup(groupName, modified.groups[groupName].name()); 0482 modified.groups[modified.groups[groupName].name()] = modified.groups[groupName]; 0483 modified.groups.remove(groupName); 0484 } 0485 } 0486 } 0487 m_d->modifiedGroupNames.clear(); 0488 Q_FOREACH (const QString &newGroupName, m_d->newGroupNames) { 0489 m_d->model->addGroup(modified.groups[newGroupName]); 0490 } 0491 m_d->newGroupNames.clear(); 0492 } 0493 0494 void KisPaletteEditor::saveNewPaletteVersion() 0495 { 0496 if (!m_d->model || !m_d->model->colorSet()) { return; } 0497 0498 QModelIndex index = m_d->rServer->resourceModel()->indexForResource(m_d->model->colorSet()); 0499 bool isGlobal = false; 0500 if (index.isValid()) { 0501 bool ok = false; 0502 int storageId = m_d->rServer->resourceModel()->data(index, Qt::UserRole + KisAllResourcesModel::StorageId).toInt(&ok); 0503 if (ok) { 0504 KisStorageModel storageModel; 0505 KisResourceStorageSP storage = storageModel.storageForId(storageId); 0506 isGlobal = storage->type() != KisResourceStorage::StorageType::Memory; 0507 } 0508 } 0509 bool res = false; 0510 if (isGlobal) { 0511 if (m_d->view) { 0512 res = KisResourceUserOperations::updateResourceWithUserInput(m_d->view->mainWindowAsQWidget(), m_d->model->colorSet()); 0513 } else if (m_d->model->colorSet()->version() >= 0) { 0514 //if the version is -1, then the resource should not be updated, because it was never saved to begin with... 0515 res = m_d->rServer->resourceModel()->updateResource(m_d->model->colorSet()); 0516 dbgResources << Q_FUNC_INFO << "-- Updating resource without user input: " << m_d->model->colorSet()->name() << "Result:" << res; 0517 } 0518 } 0519 0520 // let's not change it to true if it wasn't modified at all 0521 m_d->model->colorSet()->setDirty(m_d->model->colorSet()->isDirty() && !res); 0522 } 0523 0524 void KisPaletteEditor::slotPaletteChanged() 0525 { 0526 Q_ASSERT(m_d->model); 0527 if (!m_d->model->colorSet()) { return; } 0528 KoColorSetSP palette = m_d->model->colorSet(); 0529 m_d->modified.groups.clear(); 0530 m_d->keepColorGroups.clear(); 0531 m_d->newGroupNames.clear(); 0532 m_d->modifiedGroupNames.clear(); 0533 0534 m_d->modified.name = palette->name(); 0535 m_d->modified.storageLocation = palette->storageLocation(); 0536 m_d->modified.columnCount = palette->columnCount(); 0537 0538 Q_FOREACH (const QString &groupName, palette->getGroupNames()) { 0539 KisSwatchGroup *cs = palette->getGroup(groupName); 0540 m_d->modified.groups[groupName] = KisSwatchGroup(*cs); 0541 } 0542 } 0543 0544 QString KisPaletteEditor::relativePathFromSaveLocation() const 0545 { 0546 return filenameFromPath(m_d->modified.filename); 0547 } 0548 0549 QString KisPaletteEditor::newGroupName() const 0550 { 0551 int i = 1; 0552 QString groupname = i18nc("Default new group name", "New Group %1", QString::number(i)); 0553 while (m_d->modified.groups.contains(groupname)) { 0554 i++; 0555 groupname = i18nc("Default new group name", "New Group %1", QString::number(i)); 0556 } 0557 return groupname; 0558 } 0559 0560 QString KisPaletteEditor::filenameFromPath(const QString &path) const 0561 { 0562 return QDir::fromNativeSeparators(path).section('/', -1, -1); 0563 }