File indexing completed on 2024-05-12 16:02:01

0001 /*
0002  *  SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_palette_view.h"
0008 
0009 #include <QWheelEvent>
0010 #include <QHeaderView>
0011 #include <QFormLayout>
0012 #include <QLabel>
0013 #include <QLineEdit>
0014 #include <QCheckBox>
0015 #include <QComboBox>
0016 #include <QMenu>
0017 
0018 #include <KConfigGroup>
0019 #include <KSharedConfig>
0020 #include <KLocalizedString>
0021 
0022 #include <kis_icon_utils.h>
0023 
0024 #include <KisKineticScroller.h>
0025 
0026 #include <KoDialog.h>
0027 #include <KoColorDisplayRendererInterface.h>
0028 
0029 #include "KisPaletteDelegate.h"
0030 #include "KisPaletteModel.h"
0031 #include "kis_color_button.h"
0032 #include <KisSwatch.h>
0033 #include <KisResourceModel.h>
0034 #include <kis_debug.h>
0035 #include <KisResourceUserOperations.h>
0036 
0037 int KisPaletteView::MININUM_ROW_HEIGHT = 10;
0038 
0039 struct KisPaletteView::Private
0040 {
0041     QPointer<KisPaletteModel> model;
0042     bool allowPaletteModification {false}; // if modification is allowed from this widget
0043 };
0044 
0045 KisPaletteView::KisPaletteView(QWidget *parent)
0046     : QTableView(parent)
0047     , m_d(new Private)
0048 {
0049     setItemDelegate(new KisPaletteDelegate(this));
0050 
0051     setShowGrid(true);
0052     setDropIndicatorShown(true);
0053     setDragDropMode(QAbstractItemView::DragDrop);
0054     setSelectionMode(QAbstractItemView::SingleSelection);
0055     setDragEnabled(true);
0056     setAcceptDrops(false);
0057 
0058     /*
0059      * without this, a cycle might be created:
0060      * the view stretches to right border, and this make it need a scroll bar;
0061      * after the bar is added, the view shrinks to the bar, and this makes it
0062      * no longer need the bar any more, and the bar is removed again
0063      */
0064     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
0065 
0066     // set the size of swatches
0067     horizontalHeader()->setVisible(false);
0068     verticalHeader()->setVisible(false);
0069     horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
0070     horizontalHeader()->setMinimumSectionSize(MININUM_ROW_HEIGHT);
0071     verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
0072     verticalHeader()->setMinimumSectionSize(MININUM_ROW_HEIGHT);
0073 
0074     connect(horizontalHeader(), SIGNAL(sectionResized(int,int,int)),
0075             SLOT(slotHorizontalHeaderResized(int,int,int)));
0076     setAutoFillBackground(true);
0077 
0078     QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(this);
0079     if (scroller) {
0080         connect(scroller, SIGNAL(stateChanged(QScroller::State)),
0081                 this, SLOT(slotScrollerStateChanged(QScroller::State)));
0082     }
0083 
0084     connect(this, SIGNAL(clicked(QModelIndex)), SLOT(slotCurrentSelectionChanged(QModelIndex)));
0085 }
0086 
0087 KisPaletteView::~KisPaletteView()
0088 {
0089 }
0090 
0091 void KisPaletteView::setCrossedKeyword(const QString &value)
0092 {
0093     KisPaletteDelegate *delegate =
0094         dynamic_cast<KisPaletteDelegate*>(itemDelegate());
0095     KIS_ASSERT_RECOVER_RETURN(delegate);
0096 
0097     delegate->setCrossedKeyword(value);
0098 }
0099 
0100 bool KisPaletteView::addEntryWithDialog(KoColor color)
0101 {
0102     KoDialog dialog;
0103     dialog.setWindowTitle(i18nc("@title:dialog", "Add a new Color Swatch"));
0104     QFormLayout *editableItems = new QFormLayout(dialog.mainWidget());
0105 
0106     QComboBox *cmbGroups = new QComboBox;
0107     QString defaultGroupName = i18nc("Name for default swatch group", "Default");
0108     cmbGroups->addItem(defaultGroupName);
0109     cmbGroups->addItems(m_d->model->colorSet()->getGroupNames());
0110     QLineEdit *lnIDName = new QLineEdit;
0111     QLineEdit *lnName = new QLineEdit;
0112     KisColorButton *bnColor = new KisColorButton;
0113     QCheckBox *chkSpot = new QCheckBox;
0114     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."));
0115     editableItems->addRow(i18n("Swatch Group:"), cmbGroups);
0116     editableItems->addRow(i18n("Swatch ID:"), lnIDName);
0117     editableItems->addRow(i18n("Color swatch name:"), lnName);
0118     editableItems->addRow(i18nc("Color as the Color of a Swatch in a Palette", "Color:"), bnColor);
0119     editableItems->addRow(i18n("Spot color:"), chkSpot);
0120     cmbGroups->setCurrentIndex(0);
0121     lnName->setText(i18nc("Prefix of a color swatch default name, as in Color 1","Color")+" " + QString::number(m_d->model->colorSet()->colorCount()+1));
0122     lnIDName->setText(QString::number(m_d->model->colorSet()->colorCount() + 1));
0123     bnColor->setColor(color);
0124     chkSpot->setChecked(false);
0125 
0126     if (dialog.exec() == KoDialog::Accepted) {
0127         QString groupName = cmbGroups->currentText();
0128         if (groupName == defaultGroupName) {
0129             groupName = QString();
0130         }
0131         KisSwatch newEntry;
0132         newEntry.setColor(bnColor->color());
0133         newEntry.setName(lnName->text());
0134         newEntry.setId(lnIDName->text());
0135         newEntry.setSpotColor(chkSpot->isChecked());
0136         m_d->model->addEntry(newEntry, groupName);
0137         saveModification();
0138         return true;
0139     }
0140 
0141     return false;
0142 }
0143 
0144 bool KisPaletteView::addGroupWithDialog()
0145 {
0146     KoDialog dialog;
0147     dialog.setWindowTitle(i18nc("@title:dialog","Add a new group"));
0148     QFormLayout *editableItems = new QFormLayout(dialog.mainWidget());
0149     QLineEdit *lnName = new QLineEdit();
0150     lnName->setText(i18nc("Part of default name for a new group", "Color Group")+""+QString::number(m_d->model->colorSet()->getGroupNames().size()+1));
0151     editableItems->addRow(i18nc("Name for a group", "Name"), lnName);
0152 
0153     if (dialog.exec() == KoDialog::Accepted) {
0154         KisSwatchGroup group;
0155         group.setName(lnName->text());
0156         m_d->model->addGroup(group);
0157         saveModification();
0158         return true;
0159     }
0160     return false;
0161 }
0162 
0163 bool KisPaletteView::removeEntryWithDialog(QModelIndex index)
0164 {
0165     bool keepColors = false;
0166     if (qvariant_cast<bool>(index.data(KisPaletteModel::IsGroupNameRole))) {
0167         KoDialog dialog;
0168         dialog.setWindowTitle(i18nc("@title:dialog","Removing Group"));
0169         QFormLayout *editableItems = new QFormLayout(dialog.mainWidget());
0170         QCheckBox *chkKeep = new QCheckBox();
0171         editableItems->addRow(i18nc("Shows up when deleting a swatch group", "Keep the Colors"), chkKeep);
0172 
0173         if (dialog.exec() != KoDialog::Accepted) { return false; }
0174         keepColors = chkKeep->isChecked();
0175     }
0176     m_d->model->removeEntry(index, keepColors);
0177 
0178     saveModification();
0179 
0180     return true;
0181 }
0182 
0183 void KisPaletteView::selectClosestColor(const KoColor &color)
0184 {
0185     KoColorSetSP color_set = m_d->model->colorSet();
0186     if (!color_set) {
0187         return;
0188     }
0189     //also don't select if the color is the same as the current selection
0190     if (m_d->model->getEntry(currentIndex()).color() == color) {
0191         return;
0192     }
0193 
0194     selectionModel()->clearSelection();
0195     QModelIndex index = m_d->model->indexForClosest(color);
0196 
0197     selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select);
0198 }
0199 
0200 const KoColor KisPaletteView::closestColor(const KoColor &color) const
0201 {
0202     QModelIndex index = m_d->model->indexForClosest(color);
0203     KisSwatch swatch = m_d->model->getEntry(index);
0204     return swatch.color();
0205 }
0206 
0207 void KisPaletteView::slotFGColorChanged(const KoColor &color)
0208 {
0209     selectClosestColor(color);
0210 }
0211 
0212 void KisPaletteView::setPaletteModel(KisPaletteModel *model)
0213 {
0214     if (m_d->model) {
0215         disconnect(m_d->model, 0, this, 0);
0216     }
0217     m_d->model = model;
0218     setModel(model);
0219     slotAdditionalGuiUpdate();
0220 
0221     connect(model, SIGNAL(sigPaletteModified()), SLOT(slotAdditionalGuiUpdate()));
0222     connect(model, SIGNAL(sigPaletteChanged()), SLOT(slotAdditionalGuiUpdate()));
0223 }
0224 
0225 KisPaletteModel* KisPaletteView::paletteModel() const
0226 {
0227     return m_d->model;
0228 }
0229 
0230 void KisPaletteView::setAllowModification(bool allow)
0231 {
0232     m_d->allowPaletteModification = allow;
0233     setAcceptDrops(allow);
0234 }
0235 
0236 void KisPaletteView::slotHorizontalHeaderResized(int, int, int newSize)
0237 {
0238     resizeRows(newSize);
0239     slotAdditionalGuiUpdate();
0240 }
0241 
0242 void KisPaletteView::resizeRows(int newSize)
0243 {
0244     verticalHeader()->setDefaultSectionSize(newSize);
0245     verticalHeader()->resizeSections(QHeaderView::Fixed);
0246 }
0247 
0248 void KisPaletteView::saveModification()
0249 {
0250     //qDebug() << "saving modification in palette view" << m_d->model->colorSet()->filename() << m_d->model->colorSet()->storageLocation();
0251     KisResourceUserOperations::updateResourceWithUserInput(this, m_d->model->colorSet());
0252 }
0253 
0254 void KisPaletteView::removeSelectedEntry()
0255 {
0256     if (selectedIndexes().size() <= 0) {
0257         return;
0258     }
0259     m_d->model->removeEntry(currentIndex());
0260 }
0261 
0262 void KisPaletteView::slotAdditionalGuiUpdate()
0263 {
0264     clearSpans();
0265     resizeRows(verticalHeader()->defaultSectionSize());
0266     for (int groupNameRowNumber : m_d->model->m_rowGroupNameMap.keys()) {
0267         if (groupNameRowNumber == -1) { continue; }
0268         setSpan(groupNameRowNumber, 0, 1, m_d->model->columnCount());
0269         setRowHeight(groupNameRowNumber, fontMetrics().lineSpacing() + 6);
0270         verticalHeader()->resizeSection(groupNameRowNumber, fontMetrics().lineSpacing() + 6);
0271     }
0272     emit sigPaletteUpdatedFromModel();
0273 }
0274 
0275 void KisPaletteView::slotCurrentSelectionChanged(const QModelIndex &newCurrent)
0276 {
0277     if (!newCurrent.isValid()) { return; }
0278 
0279     const bool isGroupName = newCurrent.data(KisPaletteModel::IsGroupNameRole).toBool();
0280     const bool isCheckSlot = newCurrent.data(KisPaletteModel::CheckSlotRole).toBool();
0281 
0282     const KisSwatch newEntry = m_d->model->getEntry(newCurrent);
0283 
0284     emit sigIndexSelected(newCurrent);
0285     if (isGroupName) {
0286         return;
0287     }
0288 
0289     if (isCheckSlot) {
0290         emit sigColorSelected(newEntry.color());
0291     }
0292 }
0293 
0294 void KisPaletteView::setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer)
0295 {
0296     Q_ASSERT(m_d->model);
0297     m_d->model->setDisplayRenderer(displayRenderer);
0298 }