File indexing completed on 2024-12-22 04:14:54

0001 /*
0002  *  SPDX-FileCopyrightText: 2013 Sven Langkamp <sven.langkamp@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 
0008 #include "palettedocker_dock.h"
0009 
0010 #include <QPainter>
0011 #include <QGridLayout>
0012 #include <QTableView>
0013 #include <QHeaderView>
0014 #include <QWheelEvent>
0015 #include <QCompleter>
0016 #include <QComboBox>
0017 #include <QAction>
0018 #include <QMenu>
0019 #include <QCheckBox>
0020 #include <QFormLayout>
0021 #include <QLineEdit>
0022 #include <QString>
0023 
0024 #include <kundo2stack.h>
0025 #include <KisSqueezedComboBox.h>
0026 #include <klocalizedstring.h>
0027 #include <KoResourceServerProvider.h>
0028 #include <KisResourceLocator.h>
0029 #include <KoColorSpaceRegistry.h>
0030 #include <KoFileDialog.h>
0031 #include <kis_floating_message.h>
0032 #include <kis_icon.h>
0033 #include <kis_config.h>
0034 #include <kis_node_manager.h>
0035 #include <kis_canvas_resource_provider.h>
0036 #include <KisMainWindow.h>
0037 #include <KisViewManager.h>
0038 #include <kis_display_color_converter.h>
0039 #include <kis_canvas2.h>
0040 #include <KoDialog.h>
0041 #include <kis_color_button.h>
0042 #include <KisDocument.h>
0043 #include <KisPart.h>
0044 #include <KisPaletteEditor.h>
0045 
0046 #include <KisStorageModel.h>
0047 
0048 #include <KisPaletteModel.h>
0049 #include <KisPaletteDelegate.h>
0050 #include <kis_palette_view.h>
0051 #include <KisPaletteChooser.h>
0052 
0053 #include <KisPaletteEditor.h>
0054 #include <dialogs/KisDlgPaletteEditor.h>
0055 
0056 #include "ui_wdgpalettedock.h"
0057 
0058 PaletteDockerDock::PaletteDockerDock( )
0059     : QDockWidget(i18n("Palette"))
0060     , m_ui(new Ui_WdgPaletteDock())
0061     , m_model(new KisPaletteModel(this))
0062     , m_paletteChooser(new KisPaletteChooser(this))
0063     , m_view(0)
0064     , m_resourceProvider(0)
0065     , m_rServer(KoResourceServerProvider::instance()->paletteServer())
0066     , m_activeDocument(0)
0067     , m_paletteEditor(new KisPaletteEditor)
0068     , m_actAdd(new QAction(KisIconUtils::loadIcon("list-add"), i18n("Add a new color swatch")))
0069     , m_actRemove(new QAction(KisIconUtils::loadIcon("edit-delete"), i18n("Remove swatch or group")))
0070     , m_actModify(new QAction(KisIconUtils::loadIcon("document-edit"), i18n("Edit swatch or group")))
0071     , m_actEditPalette(new QAction(KisIconUtils::loadIcon("palette-edit"), i18n("Edit current palette")))
0072     , m_actSavePalette(new QAction(KisIconUtils::loadIcon("document-save-16"), i18n("Save current palette")))
0073     , m_colorSelfUpdate(false)
0074 {
0075     QWidget *mainWidget = new QWidget(this);
0076     setWidget(mainWidget);
0077     m_ui->setupUi(mainWidget);
0078 
0079     connect(KisResourceLocator::instance(), SIGNAL(storageRemoved(QString)), this, SLOT(slotStoragesChanged(QString)));
0080 
0081     m_ui->bnAdd->setDefaultAction(m_actAdd.data());
0082     m_ui->bnRemove->setDefaultAction(m_actRemove.data());
0083     m_ui->bnRename->setDefaultAction(m_actModify.data());
0084     m_ui->bnEditPalette->setDefaultAction(m_actEditPalette.data());
0085     m_ui->bnSavePalette->setDefaultAction(m_actSavePalette.data());
0086 
0087     // to make sure their icons have the same size
0088     m_ui->bnRemove->setIconSize(QSize(16, 16));
0089     m_ui->bnRename->setIconSize(QSize(16, 16));
0090     m_ui->bnAdd->setIconSize(QSize(16, 16));
0091     m_ui->bnEditPalette->setIconSize(QSize(16, 16));
0092     m_ui->bnSavePalette->setIconSize(QSize(16, 16));
0093 
0094 
0095     m_ui->paletteView->setPaletteModel(m_model);
0096     m_ui->paletteView->setAllowModification(true);
0097     m_ui->cmbNameList->setCompanionView(m_ui->paletteView);
0098 
0099     m_paletteEditor->setPaletteModel(m_model);
0100 
0101     connect(m_actAdd.data(), SIGNAL(triggered()), SLOT(slotAddColor()));
0102     connect(m_actRemove.data(), SIGNAL(triggered()), SLOT(slotRemoveColor()));
0103     connect(m_actModify.data(), SIGNAL(triggered()), SLOT(slotEditEntry()));
0104     connect(m_actEditPalette.data(), SIGNAL(triggered()), SLOT(slotEditPalette()));
0105     connect(m_actSavePalette.data(), SIGNAL(triggered()), SLOT(slotSavePalette()));
0106     connect(m_ui->paletteView, SIGNAL(sigIndexSelected(QModelIndex)), SLOT(slotPaletteIndexSelected(QModelIndex)));
0107     connect(m_ui->paletteView, SIGNAL(clicked(QModelIndex)), SLOT(slotPaletteIndexClicked(QModelIndex)));
0108     connect(m_ui->paletteView, SIGNAL(doubleClicked(QModelIndex)),
0109             SLOT(slotPaletteIndexDoubleClicked(QModelIndex)));
0110     connect(m_ui->paletteView, SIGNAL(sigPaletteUpdatedFromModel()), SLOT(slotUpdateLblPaletteName()));
0111     connect(m_ui->cmbNameList, SIGNAL(sigColorSelected(const KoColor&)), SLOT(slotNameListSelection(const KoColor&)));
0112     connect(m_ui->bnLock, SIGNAL(toggled(bool)), SLOT(slotLockPalette(bool)));
0113 
0114     m_viewContextMenu.addAction(m_actModify.data());
0115     m_viewContextMenu.addAction(m_actRemove.data());
0116     connect(m_ui->paletteView, SIGNAL(pressed(QModelIndex)), SLOT(slotContextMenu(QModelIndex)));
0117 
0118     connect(m_paletteChooser, SIGNAL(sigPaletteSelected(KoColorSetSP)), SLOT(slotSetColorSet(KoColorSetSP)));
0119     connect(m_paletteChooser, SIGNAL(sigAddPalette()), SLOT(slotAddPalette()));
0120     connect(m_paletteChooser, SIGNAL(sigImportPalette()), SLOT(slotImportPalette()));
0121     connect(m_paletteChooser, SIGNAL(sigRemovePalette(KoColorSetSP)), SLOT(slotRemovePalette(KoColorSetSP)));
0122     connect(m_paletteChooser, SIGNAL(sigExportPalette(KoColorSetSP)), SLOT(slotExportPalette(KoColorSetSP)));
0123 
0124     m_ui->bnColorSets->setIcon(KisIconUtils::loadIcon("palette-library"));
0125     m_ui->bnColorSets->setToolTip(i18n("Load a palette"));
0126     m_ui->bnColorSets->setPopupWidget(m_paletteChooser);
0127 
0128     KisConfig cfg(true);
0129     QString defaultPaletteName = cfg.defaultPalette();
0130     KoColorSetSP defaultPalette = m_rServer->resource("", "", defaultPaletteName);
0131     if (defaultPalette) {
0132         slotSetColorSet(defaultPalette);
0133         m_paletteChooser->setCurrentItem(defaultPalette);
0134     } else {
0135         m_ui->bnAdd->setEnabled(false);
0136         m_ui->bnUndo->setEnabled(false);
0137         m_ui->bnRedo->setEnabled(false);
0138         m_ui->bnRename->setEnabled(false);
0139         m_ui->bnRemove->setEnabled(false);
0140         m_ui->bnEditPalette->setEnabled(false);
0141         m_ui->bnSavePalette->setEnabled(false);
0142         m_ui->bnLock->setEnabled(false);
0143 
0144         m_ui->paletteView->setAllowModification(false);
0145     }
0146 
0147     m_ui->bnUndo->setVisible(false);
0148     m_ui->bnRedo->setVisible(false);
0149 
0150 
0151     KoResourceServer<KoColorSet> *srv = KoResourceServerProvider::instance()->paletteServer();
0152     srv->addObserver(this);
0153 }
0154 
0155 PaletteDockerDock::~PaletteDockerDock()
0156 {
0157     if (m_paletteEditor->isModified()) {
0158         m_paletteEditor->saveNewPaletteVersion();
0159     }
0160     KoResourceServer<KoColorSet> *srv = KoResourceServerProvider::instance()->paletteServer();
0161     srv->removeObserver(this);
0162 }
0163 
0164 void PaletteDockerDock::setViewManager(KisViewManager* kisview)
0165 {
0166     m_view = kisview;
0167     m_resourceProvider = kisview->canvasResourceProvider();
0168     connect(m_resourceProvider, SIGNAL(sigFGColorChanged(KoColor)),
0169             this, SLOT(slotFGColorResourceChanged(KoColor)));
0170     kisview->nodeManager()->disconnect(m_model);
0171 }
0172 
0173 void PaletteDockerDock::unsetResourceServer()
0174 {
0175     KoResourceServer<KoColorSet> *srv = KoResourceServerProvider::instance()->paletteServer();
0176     srv->removeObserver(this);
0177 }
0178 
0179 void PaletteDockerDock::resourceAdded(QSharedPointer<KoColorSet> resource)
0180 {
0181     Q_UNUSED(resource);
0182 }
0183 
0184 void PaletteDockerDock::removingResource(QSharedPointer<KoColorSet> resource)
0185 {
0186     Q_UNUSED(resource);
0187 }
0188 
0189 void PaletteDockerDock::resourceChanged(QSharedPointer<KoColorSet> resource)
0190 {
0191     m_model->slotExternalPaletteModified(resource);
0192 }
0193 
0194 void PaletteDockerDock::slotContextMenu(const QModelIndex &)
0195 {
0196     if (QApplication::mouseButtons() == Qt::RightButton) {
0197         m_viewContextMenu.exec(QCursor::pos());
0198     }
0199 }
0200 
0201 void PaletteDockerDock::slotAddPalette()
0202 {
0203     KoColorSetSP palette = m_paletteEditor->addPalette();
0204 
0205     if (palette) {
0206         m_paletteChooser->setCurrentItem(palette);
0207     }
0208 }
0209 
0210 void PaletteDockerDock::slotRemovePalette(KoColorSetSP cs)
0211 {
0212     m_paletteEditor->removePalette(cs);
0213 }
0214 
0215 void PaletteDockerDock::slotImportPalette()
0216 {
0217     KoColorSetSP palette = m_paletteEditor->importPalette();
0218     if (palette) {
0219         m_paletteChooser->setCurrentItem(palette);
0220     }
0221 }
0222 
0223 void PaletteDockerDock::slotExportPalette(KoColorSetSP palette)
0224 {
0225     KoFileDialog dialog(this, KoFileDialog::SaveFile, "Save Palette");
0226     dialog.setCaption(i18n("Export Palette"));
0227     dialog.setDefaultDir(palette->filename());
0228     dialog.setMimeTypeFilters(QStringList() << "application/x-krita-palette");
0229     QString newPath;
0230     if ((newPath = dialog.filename()).isEmpty()) { return; }
0231 
0232     QFile file(newPath);
0233     if (!file.open(QIODevice::WriteOnly)) {
0234         warnKrita << "Could not open the file for writing:" << newPath;
0235         return;
0236     }
0237     if (palette->saveToDevice(&file)) {
0238         m_view->showFloatingMessage(
0239             i18nc("Floating message about exporting successful", "Palette exported successfully"), QIcon(),
0240             500, KisFloatingMessage::Low);
0241     } else {
0242         warnKrita << "Could export to the file:" << newPath;
0243     }
0244 }
0245 
0246 void PaletteDockerDock::setCanvas(KoCanvasBase *canvas)
0247 {
0248     setEnabled(canvas != 0);
0249     if (canvas) {
0250         KisCanvas2 *cv = qobject_cast<KisCanvas2*>(canvas);
0251         m_ui->paletteView->setDisplayRenderer(cv->displayColorConverter()->displayRendererInterface());
0252     }
0253 
0254     if (m_view && m_view->document()) {
0255         m_activeDocument = m_view->document();
0256         m_paletteEditor->setView(m_view);
0257     }
0258 
0259     if (!m_currentColorSet) {
0260         slotSetColorSet(0);
0261     }
0262 }
0263 
0264 void PaletteDockerDock::unsetCanvas()
0265 {
0266     setEnabled(false);
0267     m_ui->paletteView->setDisplayRenderer(0);
0268     m_paletteEditor->setView(0);
0269 
0270     if (!m_currentColorSet) {
0271         slotSetColorSet(0);
0272     }
0273 }
0274 
0275 void PaletteDockerDock::slotSetColorSet(KoColorSetSP colorSet)
0276 {
0277     if (m_currentColorSet == colorSet) {
0278         slotUpdateLblPaletteName();
0279         m_paletteEditor->updatePalette();
0280         return;
0281     }
0282 
0283     if (m_currentColorSet) {
0284         disconnect(m_currentColorSet->undoStack());
0285     }
0286 
0287     // needs to save the palette before switching to another one
0288     if (m_paletteEditor->isModified() && m_currentColorSet != colorSet) {
0289         m_paletteEditor->saveNewPaletteVersion();
0290     }
0291     else if (colorSet) {
0292 
0293         m_model->setColorSet(colorSet);
0294 
0295         m_ui->bnUndo->setEnabled(colorSet->undoStack()->canUndo() && !colorSet->isLocked());
0296         connect(colorSet->undoStack(), SIGNAL(canUndoChanged(bool)), m_ui->bnUndo, SLOT(setEnabled(bool)));
0297         connect(colorSet->undoStack(), SIGNAL(undoTextChanged(QString)), this, SLOT(setUndoToolTip(QString)));
0298 
0299         m_ui->bnRedo->setEnabled(colorSet->undoStack()->canRedo() && colorSet->isLocked());
0300         connect(colorSet->undoStack(), SIGNAL(canRedoChanged(bool)), m_ui->bnRedo, SLOT(setEnabled(bool)));
0301         connect(colorSet->undoStack(), SIGNAL(redoTextChanged(QString)), this, SLOT(setRedoToolTip(QString)));
0302 
0303         connect(m_ui->bnUndo, SIGNAL(clicked()), this, SLOT(undo()));
0304         connect(m_ui->bnRedo, SIGNAL(clicked()), this, SLOT(redo()));
0305 
0306         m_ui->bnLock->setChecked(colorSet->isLocked());
0307     }
0308     bool state = (bool)colorSet;
0309     if (state != (bool)m_currentColorSet) {
0310         m_ui->bnAdd->setEnabled(state);
0311         m_ui->bnRename->setEnabled(state);
0312         m_ui->bnRemove->setEnabled(state);
0313         m_ui->bnEditPalette->setEnabled(state);
0314         m_ui->bnSavePalette->setEnabled(state);
0315         m_ui->paletteView->setAllowModification(state);
0316         m_ui->bnLock->setEnabled(state);
0317     }
0318 
0319     m_currentColorSet = colorSet;
0320 
0321     if (colorSet) {
0322         KisConfig cfg(true);
0323         cfg.setDefaultPalette(colorSet->name());
0324         m_ui->lblPaletteName->setTextElideMode(Qt::ElideMiddle);
0325         m_ui->lblPaletteName->setText(colorSet->name());
0326     }
0327     else {
0328         m_ui->lblPaletteName->setText("");
0329     }
0330     slotUpdateLblPaletteName();
0331 }
0332 
0333 void PaletteDockerDock::slotEditPalette()
0334 {
0335     KisDlgPaletteEditor dlg;
0336     if (!m_currentColorSet) { return; }
0337     dlg.setPaletteModel(m_model);
0338     dlg.setView(m_view);
0339     if (dlg.exec() != QDialog::Accepted){ return; }
0340 
0341     slotSetColorSet(m_currentColorSet); // update GUI
0342 }
0343 
0344 void PaletteDockerDock::slotSavePalette()
0345 {
0346     if (m_paletteEditor->isModified()) {
0347         m_paletteEditor->saveNewPaletteVersion();
0348         slotUpdateLblPaletteName();
0349     }
0350 }
0351 
0352 
0353 void PaletteDockerDock::slotAddColor()
0354 {
0355     if (m_resourceProvider) {
0356         m_paletteEditor->addEntry(m_resourceProvider->fgColor());
0357     }
0358     slotUpdateLblPaletteName();
0359 }
0360 
0361 void PaletteDockerDock::slotRemoveColor()
0362 {
0363     QModelIndex index = m_ui->paletteView->currentIndex();
0364     if (!index.isValid()) {
0365         return;
0366     }
0367     m_paletteEditor->removeEntry(index);
0368     m_ui->bnRemove->setEnabled(false);
0369     slotUpdateLblPaletteName();
0370 }
0371 
0372 void PaletteDockerDock::setFGColorByPalette(const KisSwatch &entry)
0373 {
0374     if (m_resourceProvider) {
0375         m_colorSelfUpdate = true;
0376         m_resourceProvider->setFGColor(entry.color());
0377         m_colorSelfUpdate = false;
0378     }
0379 }
0380 
0381 void PaletteDockerDock::slotUpdateLblPaletteName()
0382 {
0383     if (m_currentColorSet) {
0384         m_ui->lblPaletteName->setTextElideMode(Qt::ElideLeft);
0385         QString name = m_currentColorSet->name();
0386 
0387         bool isGlobal = true;
0388         KisResourceModel model(ResourceType::Palettes);
0389         QModelIndex index = model.indexForResource(m_currentColorSet);
0390         if (index.isValid()) {
0391             bool ok;
0392             int storageId = model.data(index, Qt::UserRole + KisAllResourcesModel::StorageId).toInt(&ok);
0393             if (ok) {
0394                 KisStorageModel storageModel;
0395                 KisResourceStorageSP storage = storageModel.storageForId(storageId);
0396                 isGlobal = storage->type() != KisResourceStorage::StorageType::Memory;
0397             }
0398         }
0399         m_actSavePalette.data()->setEnabled(isGlobal);
0400         if (isGlobal) {
0401             m_actSavePalette.data()->setToolTip(i18nc("@tooltip", "Save palette explicitly, will also happen automatically on exiting Krita."));
0402         }
0403         else {
0404             m_actSavePalette.data()->setToolTip(i18nc("@tooltip", "Saving for document palettes is done by saving the document."));
0405         }
0406         // if the palette is not global, then let's not indicate that the changes has been made
0407         // (it's easier than tracking whether the document has been saved or maybe exported etc.)
0408         if (m_paletteEditor->isModified() && isGlobal) {
0409             name = "* " + name;
0410             QFont font = m_ui->lblPaletteName->font();
0411             font.setItalic(true);
0412             m_ui->lblPaletteName->setFont(font);
0413         }
0414         else {
0415             QFont font = m_ui->lblPaletteName->font();
0416             font.setItalic(false);
0417             m_ui->lblPaletteName->setFont(font);
0418         }
0419 
0420         m_ui->lblPaletteName->setText(name);
0421     }
0422     else {
0423         m_ui->lblPaletteName->setText("");
0424     }
0425 }
0426 
0427 void PaletteDockerDock::slotLockPalette(bool locked)
0428 {
0429     m_currentColorSet->setLocked(locked);
0430     QIcon icon = locked ? KisIconUtils::loadIcon(koIconName("object-locked"))
0431                         : KisIconUtils::loadIcon(koIconName("object-unlocked"));
0432     m_ui->bnLock->setIcon(icon);
0433     m_ui->bnAdd->setEnabled(!locked);
0434     m_ui->bnRename->setEnabled(!locked);
0435     m_ui->bnRemove->setEnabled(!locked);
0436     m_ui->bnEditPalette->setEnabled(!locked);
0437     m_ui->bnSavePalette->setEnabled(!locked);
0438     m_ui->paletteView->setAllowModification(!locked);
0439 }
0440 
0441 void PaletteDockerDock::setUndoToolTip(const QString &text)
0442 {
0443     m_ui->bnUndo->setToolTip(text);
0444 }
0445 
0446 void PaletteDockerDock::setRedoToolTip(const QString &text)
0447 {
0448     m_ui->bnRedo->setToolTip(text);
0449 }
0450 
0451 void PaletteDockerDock::undo()
0452 {
0453     m_currentColorSet->undoStack()->undo();
0454     m_model->slotExternalPaletteModified(m_currentColorSet);
0455     m_paletteEditor->updatePalette();
0456     slotUpdateLblPaletteName();
0457 }
0458 
0459 void PaletteDockerDock::redo()
0460 {
0461     m_currentColorSet->undoStack()->redo();
0462     m_model->slotExternalPaletteModified(m_currentColorSet);
0463     m_paletteEditor->updatePalette();
0464     slotUpdateLblPaletteName();
0465 
0466 }
0467 
0468 
0469 void PaletteDockerDock::slotFGColorResourceChanged(const KoColor &color)
0470 {
0471     if (!m_colorSelfUpdate) {
0472         m_ui->paletteView->slotFGColorChanged(color);
0473     }
0474 }
0475 
0476 void PaletteDockerDock::slotStoragesChanged(const QString &/*location*/)
0477 {
0478     if (m_activeDocument.isNull()) {
0479         slotSetColorSet(0);
0480     }
0481     if (m_currentColorSet) {
0482         if (!m_rServer->resource(m_currentColorSet->md5Sum(), "", "")) {
0483             slotSetColorSet(0);
0484         }
0485     }
0486 }
0487 
0488 void PaletteDockerDock::slotPaletteIndexSelected(const QModelIndex &index)
0489 {
0490     bool occupied = qvariant_cast<bool>(index.data(KisPaletteModel::CheckSlotRole));
0491     if (occupied) {
0492         if (!qvariant_cast<bool>(index.data(KisPaletteModel::IsGroupNameRole))) {
0493             m_ui->bnRemove->setEnabled(true);
0494             KisSwatch entry = m_model->getSwatch(index);
0495             setFGColorByPalette(entry);
0496         }
0497     }
0498     m_ui->bnRemove->setEnabled(occupied);
0499     slotUpdateLblPaletteName();
0500 }
0501 
0502 void PaletteDockerDock::slotPaletteIndexClicked(const QModelIndex &index)
0503 {
0504     if (!(qvariant_cast<bool>(index.data(KisPaletteModel::CheckSlotRole)))) {
0505         setEntryByForeground(index);
0506     }
0507     slotUpdateLblPaletteName();
0508 }
0509 
0510 void PaletteDockerDock::slotPaletteIndexDoubleClicked(const QModelIndex &index)
0511 {
0512     m_paletteEditor->modifyEntry(index);
0513     slotUpdateLblPaletteName();
0514 }
0515 
0516 void PaletteDockerDock::setEntryByForeground(const QModelIndex &index)
0517 {
0518     m_paletteEditor->setEntry(m_resourceProvider->fgColor(), index);
0519     m_ui->bnRemove->setEnabled(true);
0520     slotUpdateLblPaletteName();
0521 }
0522 
0523 void PaletteDockerDock::slotEditEntry()
0524 {
0525     QModelIndex index = m_ui->paletteView->currentIndex();
0526     if (!index.isValid()) {
0527         return;
0528     }
0529     m_paletteEditor->modifyEntry(index);
0530     slotUpdateLblPaletteName();
0531 }
0532 
0533 void PaletteDockerDock::slotNameListSelection(const KoColor &color)
0534 {
0535     m_colorSelfUpdate = true;
0536     m_ui->paletteView->selectClosestColor(color);
0537     m_resourceProvider->setFGColor(color);
0538     m_colorSelfUpdate = false;
0539 }