File indexing completed on 2024-05-19 04:29:25

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