diff --git a/libs/ui/KisPaletteEditor.cpp b/libs/ui/KisPaletteEditor.cpp index 2c5632a8d4..2277508df6 100644 --- a/libs/ui/KisPaletteEditor.cpp +++ b/libs/ui/KisPaletteEditor.cpp @@ -1,663 +1,658 @@ /* * Copyright (c) 2018 Michael Zhou * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisPaletteEditor.h" struct KisPaletteEditor::PaletteInfo { QString name; QString filename; int columnCount; bool isGlobal; bool isReadOnly; QHash groups; }; struct KisPaletteEditor::Private { bool isGlobalModified {false}; bool isNameModified {false}; bool isFilenameModified {false}; bool isColumnCountModified {false}; QSet modifiedGroupNames; // key is original group name QSet newGroupNames; QSet keepColorGroups; QSet pathsToRemove; QString groupBeingRenamed; QPointer model; QPointer view; PaletteInfo modified; QPointer query; KoResourceServer *rServer; QPalette normalPalette; QPalette warnPalette; }; KisPaletteEditor::KisPaletteEditor(QObject *parent) : QObject(parent) , m_d(new Private) { m_d->rServer = KoResourceServerProvider::instance()->paletteServer(); m_d->warnPalette.setColor(QPalette::Text, Qt::red); } KisPaletteEditor::~KisPaletteEditor() { } void KisPaletteEditor::setPaletteModel(KisPaletteModel *model) { if (!model) { return; } m_d->model = model; slotPaletteChanged(); connect(model, SIGNAL(sigPaletteChanged()), SLOT(slotPaletteChanged())); connect(model, SIGNAL(sigPaletteModified()), SLOT(slotSetDocumentModified())); } void KisPaletteEditor::setView(KisViewManager *view) { m_d->view = view; } void KisPaletteEditor::addPalette() { if (!m_d->view) { return; } if (!m_d->view->document()) { return; } KoDialog dlg; QFormLayout layout; dlg.mainWidget()->setLayout(&layout); QLabel lbl(i18nc("Label for line edit to set a palette name.","Name")); QLineEdit le(i18nc("Default name for a new palette","New Palette")); layout.addRow(&lbl, &le); if (dlg.exec() != QDialog::Accepted) { return; } KoColorSetSP newColorSet(new KoColorSet(newPaletteFileName(false))); newColorSet->setPaletteType(KoColorSet::KPL); newColorSet->setIsGlobal(false); newColorSet->setIsEditable(true); newColorSet->setValid(true); newColorSet->setName(le.text()); m_d->rServer->addResource(newColorSet); //m_d->rServer->removeFromBlacklist(newColorSet); uploadPaletteList(); } void KisPaletteEditor::importPalette() { KoFileDialog dialog(Q_NULLPTR, KoFileDialog::OpenFile, "Open Palette"); dialog.setDefaultDir(QDir::homePath()); dialog.setMimeTypeFilters(QStringList() << "krita/x-colorset" << "application/x-gimp-color-palette"); QString filename = dialog.filename(); if (filename.isEmpty()) { return; } if (duplicateExistsFilename(filename, false)) { QMessageBox message; message.setWindowTitle(i18n("Can't Import Palette")); message.setText(i18n("Can't import palette: there's already imported with the same filename")); message.exec(); return; } KoColorSetSP colorSet(new KoColorSet(filename)); colorSet->load(); QString name = filenameFromPath(colorSet->filename()); if (duplicateExistsFilename(name, false)) { colorSet->setFilename(newPaletteFileName(false)); } else { colorSet->setFilename(name); } colorSet->setIsGlobal(false); m_d->rServer->addResource(colorSet); //m_d->rServer->removeFromBlacklist(colorSet); uploadPaletteList(); } void KisPaletteEditor::removePalette(KoColorSetSP cs) { if (!m_d->view) { return; } if (!m_d->view->document()) { return; } if (!cs || !cs->isEditable()) { return; } - if (cs->isGlobal()) { - m_d->rServer->removeResourceAndBlacklist(cs); - QFile::remove(cs->filename()); - return; - } m_d->rServer->removeResourceFromServer(cs); uploadPaletteList(); } int KisPaletteEditor::rowNumberOfGroup(const QString &oriName) const { if (!m_d->modified.groups.contains(oriName)) { return 0; } return m_d->modified.groups[oriName].rowCount(); } bool KisPaletteEditor::duplicateExistsGroupName(const QString &name) const { if (name == m_d->groupBeingRenamed) { return false; } Q_FOREACH (const KisSwatchGroup &g, m_d->modified.groups.values()) { if (name == g.name()) { return true; } } return false; } bool KisPaletteEditor::duplicateExistsOriginalGroupName(const QString &name) const { return m_d->modified.groups.contains(name); } QString KisPaletteEditor::oldNameFromNewName(const QString &newName) const { Q_FOREACH (const QString &oldGroupName, m_d->modified.groups.keys()) { if (m_d->modified.groups[oldGroupName].name() == newName) { return oldGroupName; } } return QString(); } void KisPaletteEditor::rename(const QString &newName) { if (newName.isEmpty()) { return; } m_d->isNameModified = true; m_d->modified.name = newName; } void KisPaletteEditor::changeFilename(const QString &newName) { if (newName.isEmpty()) { return; } m_d->isFilenameModified = true; m_d->pathsToRemove.insert(m_d->modified.filename); if (m_d->modified.isGlobal) { m_d->modified.filename = m_d->rServer->saveLocation() + newName; } else { m_d->modified.filename = newName; } } void KisPaletteEditor::changeColCount(int newCount) { m_d->isColumnCountModified = true; m_d->modified.columnCount = newCount; } QString KisPaletteEditor::addGroup() { KoDialog dlg; m_d->query = &dlg; QVBoxLayout layout(&dlg); dlg.mainWidget()->setLayout(&layout); QLabel lblName(i18n("Name"), &dlg); layout.addWidget(&lblName); QLineEdit leName(&dlg); leName.setText(newGroupName()); connect(&leName, SIGNAL(textChanged(QString)), SLOT(slotGroupNameChanged(QString))); layout.addWidget(&leName); QLabel lblRowCount(i18n("Row count"), &dlg); layout.addWidget(&lblRowCount); QSpinBox spxRow(&dlg); spxRow.setValue(20); layout.addWidget(&spxRow); if (dlg.exec() != QDialog::Accepted) { return QString(); } if (duplicateExistsGroupName(leName.text())) { return QString(); } QString realName = leName.text(); QString name = realName; if (duplicateExistsOriginalGroupName(name)) { name = newGroupName(); } m_d->modified.groups[name] = KisSwatchGroup(); KisSwatchGroup &newGroup = m_d->modified.groups[name]; newGroup.setName(realName); m_d->newGroupNames.insert(name); newGroup.setRowCount(spxRow.value()); return realName; } bool KisPaletteEditor::removeGroup(const QString &name) { KoDialog window; window.setWindowTitle(i18nc("@title:window", "Removing Group")); QFormLayout editableItems(&window); QCheckBox chkKeep(&window); window.mainWidget()->setLayout(&editableItems); editableItems.addRow(i18nc("Shows up when deleting a swatch group", "Keep the Colors"), &chkKeep); if (window.exec() != KoDialog::Accepted) { return false; } m_d->modified.groups.remove(name); m_d->newGroupNames.remove(name); if (chkKeep.isChecked()) { m_d->keepColorGroups.insert(name); } return true; } QString KisPaletteEditor::renameGroup(const QString &oldName) { if (oldName.isEmpty() || oldName == KoColorSet::GLOBAL_GROUP_NAME) { return QString(); } KoDialog dlg; m_d->query = &dlg; m_d->groupBeingRenamed = m_d->modified.groups[oldName].name(); QFormLayout form(&dlg); dlg.mainWidget()->setLayout(&form); QLineEdit leNewName; connect(&leNewName, SIGNAL(textChanged(QString)), SLOT(slotGroupNameChanged(QString))); leNewName.setText(m_d->modified.groups[oldName].name()); form.addRow(i18nc("Renaming swatch group", "New name"), &leNewName); if (dlg.exec() != KoDialog::Accepted) { return QString(); } if (leNewName.text().isEmpty()) { return QString(); } if (duplicateExistsGroupName(leNewName.text())) { return QString(); } m_d->modified.groups[oldName].setName(leNewName.text()); m_d->modifiedGroupNames.insert(oldName); return leNewName.text(); } void KisPaletteEditor::slotGroupNameChanged(const QString &newName) { QLineEdit *leGroupName = qobject_cast(sender()); if (duplicateExistsGroupName(newName) || newName == QString()) { leGroupName->setPalette(m_d->warnPalette); if (m_d->query->button(KoDialog::Ok)) { m_d->query->button(KoDialog::Ok)->setEnabled(false); } return; } leGroupName->setPalette(m_d->normalPalette); if (m_d->query->button(KoDialog::Ok)) { m_d->query->button(KoDialog::Ok)->setEnabled(true); } } void KisPaletteEditor::changeGroupRowCount(const QString &name, int newRowCount) { if (!m_d->modified.groups.contains(name)) { return; } m_d->modified.groups[name].setRowCount(newRowCount); m_d->modifiedGroupNames.insert(name); } void KisPaletteEditor::setGlobal(bool isGlobal) { m_d->isGlobalModified = true; m_d->modified.isGlobal = isGlobal; } void KisPaletteEditor::setEntry(const KoColor &color, const QModelIndex &index) { Q_ASSERT(m_d->model); if (!m_d->model->colorSet()->isEditable()) { return; } if (!m_d->view) { return; } if (!m_d->view->document()) { return; } KisSwatch c = KisSwatch(color); c.setId(QString::number(m_d->model->colorSet()->colorCount() + 1)); c.setName(i18nc("Default name for a color swatch","Color %1", QString::number(m_d->model->colorSet()->colorCount()+1))); m_d->model->setEntry(c, index); } void KisPaletteEditor::slotSetDocumentModified() { m_d->view->document()->setModified(true); } void KisPaletteEditor::removeEntry(const QModelIndex &index) { Q_ASSERT(m_d->model); if (!m_d->model->colorSet()->isEditable()) { return; } if (!m_d->view) { return; } if (!m_d->view->document()) { return; } if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { removeGroup(qvariant_cast(index.data(KisPaletteModel::GroupNameRole))); updatePalette(); } else { m_d->model->removeEntry(index, false); } if (m_d->model->colorSet()->isGlobal()) { m_d->model->colorSet()->save(); return; } } void KisPaletteEditor::modifyEntry(const QModelIndex &index) { if (!m_d->model->colorSet()->isEditable()) { return; } if (!m_d->view) { return; } if (!m_d->view->document()) { return; } KoDialog dlg; dlg.setCaption(i18nc("@title:window", "Add a Color")); QFormLayout *editableItems = new QFormLayout(&dlg); dlg.mainWidget()->setLayout(editableItems); QString groupName = qvariant_cast(index.data(Qt::DisplayRole)); if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { renameGroup(groupName); updatePalette(); } else { QLineEdit *lnIDName = new QLineEdit(&dlg); QLineEdit *lnGroupName = new QLineEdit(&dlg); KisColorButton *bnColor = new KisColorButton(&dlg); QCheckBox *chkSpot = new QCheckBox(&dlg); 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.")); KisSwatch entry = m_d->model->getEntry(index); editableItems->addRow(i18n("ID"), lnIDName); editableItems->addRow(i18nc("Name for a swatch group", "Swatch group name"), lnGroupName); editableItems->addRow(i18n("Color"), bnColor); editableItems->addRow(i18n("Spot"), chkSpot); lnGroupName->setText(entry.name()); lnIDName->setText(entry.id()); bnColor->setColor(entry.color()); chkSpot->setChecked(entry.spotColor()); if (dlg.exec() == KoDialog::Accepted) { entry.setName(lnGroupName->text()); entry.setId(lnIDName->text()); entry.setColor(bnColor->color()); entry.setSpotColor(chkSpot->isChecked()); m_d->model->setEntry(entry, index); } } } void KisPaletteEditor::addEntry(const KoColor &color) { Q_ASSERT(m_d->model); if (!m_d->view) { return; } if (!m_d->view->document()) { return; } if (!m_d->model->colorSet()->isEditable()) { return; } KoDialog window; window.setWindowTitle(i18nc("@title:window", "Add a new Colorset Entry")); QFormLayout editableItems(&window); window.mainWidget()->setLayout(&editableItems); QComboBox cmbGroups(&window); cmbGroups.addItems(m_d->model->colorSet()->getGroupNames()); QLineEdit lnIDName(&window); QLineEdit lnName(&window); KisColorButton bnColor(&window); QCheckBox chkSpot(&window); 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.")); editableItems.addRow(i18n("Group"), &cmbGroups); editableItems.addRow(i18n("ID"), &lnIDName); editableItems.addRow(i18n("Name"), &lnName); editableItems.addRow(i18n("Color"), &bnColor); editableItems.addRow(i18nc("Spot color", "Spot"), &chkSpot); cmbGroups.setCurrentIndex(0); lnName.setText(i18nc("Default name for a color swatch","Color %1", QString::number(m_d->model->colorSet()->colorCount()+1))); lnIDName.setText(QString::number(m_d->model->colorSet()->colorCount() + 1)); bnColor.setColor(color); chkSpot.setChecked(false); if (window.exec() != KoDialog::Accepted) { return; } QString groupName = cmbGroups.currentText(); KisSwatch newEntry; newEntry.setColor(bnColor.color()); newEntry.setName(lnName.text()); newEntry.setId(lnIDName.text()); newEntry.setSpotColor(chkSpot.isChecked()); m_d->model->addEntry(newEntry, groupName); if (m_d->model->colorSet()->isGlobal()) { m_d->model->colorSet()->save(); return; } m_d->modifiedGroupNames.insert(groupName); m_d->modified.groups[groupName].addEntry(newEntry); } void KisPaletteEditor::updatePalette() { Q_ASSERT(m_d->model); Q_ASSERT(m_d->model->colorSet()); if (!m_d->model->colorSet()->isEditable()) { return; } if (!m_d->view) { return; } if (!m_d->view->document()) { return; } KoColorSetSP palette = m_d->model->colorSet(); PaletteInfo &modified = m_d->modified; if (m_d->isColumnCountModified) { palette->setColumnCount(modified.columnCount); } if (m_d->isNameModified) { palette->setName(modified.name); } if (m_d->isFilenameModified) { QString originalPath = palette->filename(); palette->setFilename(modified.filename); if (palette->isGlobal()) { if (!palette->save()) { palette->setFilename(newPaletteFileName(true)); palette->save(); } QFile::remove(originalPath); } } if (m_d->isGlobalModified) { palette->setIsGlobal(modified.isGlobal); if (modified.isGlobal) { setGlobal(); } else { setNonGlobal(); } } Q_FOREACH (const QString &groupName, palette->getGroupNames()) { if (!modified.groups.contains(groupName)) { m_d->model->removeGroup(groupName, m_d->keepColorGroups.contains(groupName)); } } m_d->keepColorGroups.clear(); Q_FOREACH (const QString &groupName, palette->getGroupNames()) { if (m_d->modifiedGroupNames.contains(groupName)) { m_d->model->setRowNumber(groupName, modified.groups[groupName].rowCount()); if (groupName != modified.groups[groupName].name()) { m_d->model->renameGroup(groupName, modified.groups[groupName].name()); modified.groups[modified.groups[groupName].name()] = modified.groups[groupName]; modified.groups.remove(groupName); } } } m_d->modifiedGroupNames.clear(); Q_FOREACH (const QString &newGroupName, m_d->newGroupNames) { m_d->model->addGroup(modified.groups[newGroupName]); } m_d->newGroupNames.clear(); if (m_d->model->colorSet()->isGlobal()) { m_d->model->colorSet()->save(); } } void KisPaletteEditor::slotPaletteChanged() { Q_ASSERT(m_d->model); if (!m_d->model->colorSet()) { return; } KoColorSetSP palette = m_d->model->colorSet(); m_d->modified.groups.clear(); m_d->keepColorGroups.clear(); m_d->newGroupNames.clear(); m_d->modifiedGroupNames.clear(); m_d->modified.name = palette->name(); m_d->modified.filename = palette->filename(); m_d->modified.columnCount = palette->columnCount(); m_d->modified.isGlobal = palette->isGlobal(); m_d->modified.isReadOnly = !palette->isEditable(); Q_FOREACH (const QString &groupName, palette->getGroupNames()) { KisSwatchGroup *cs = palette->getGroup(groupName); m_d->modified.groups[groupName] = KisSwatchGroup(*cs); } } void KisPaletteEditor::setGlobal() { Q_ASSERT(m_d->model); if (!m_d->view) { return; } if (!m_d->view->document()) { return; } if (!m_d->model->colorSet()) { return; } KoColorSetSP colorSet = m_d->model->colorSet(); QString saveLocation = m_d->rServer->saveLocation(); QString name = filenameFromPath(colorSet->filename()); QFileInfo fileInfo(saveLocation + name); colorSet->setFilename(fileInfo.filePath()); colorSet->setIsGlobal(true); //m_d->rServer->removeFromBlacklist(colorSet); if (!colorSet->save()) { QMessageBox message; message.setWindowTitle(i18n("Saving palette failed")); message.setText(i18n("Failed to save global palette file. Please set it to non-global, or you will lose the file when you close Krita")); message.exec(); } uploadPaletteList(); } bool KisPaletteEditor::duplicateExistsFilename(const QString &filename, bool global) const { QString prefix; if (global) { prefix = m_d->rServer->saveLocation(); } Q_FOREACH (const KoResourceSP r, KoResourceServerProvider::instance()->paletteServer()->resources()) { if (r->filename() == prefix + filename && r != m_d->model->colorSet()) { return true; } } return false; } QString KisPaletteEditor::relativePathFromSaveLocation() const { return filenameFromPath(m_d->modified.filename); } void KisPaletteEditor::setNonGlobal() { Q_ASSERT(m_d->model); if (!m_d->view) { return; } if (!m_d->view->document()) { return; } if (!m_d->model->colorSet()) { return; } KoColorSetSP colorSet = m_d->model->colorSet(); QString name = filenameFromPath(colorSet->filename()); QFile::remove(colorSet->filename()); if (duplicateExistsFilename(name, false)) { colorSet->setFilename(newPaletteFileName(false)); } else { colorSet->setFilename(name); } colorSet->setIsGlobal(false); uploadPaletteList(); } QString KisPaletteEditor::newPaletteFileName(bool isGlobal) { QSet nameSet; Q_FOREACH (const KoResourceSP r, m_d->rServer->resources()) { nameSet.insert(r->filename()); } KoColorSet tmpColorSet; QString result = "new_palette_"; if (isGlobal) { result = m_d->rServer->saveLocation() + result; } int i = 0; while (nameSet.contains(result + QString::number(i) + tmpColorSet.defaultFileExtension())) { i++; } result = result + QString::number(i) + tmpColorSet.defaultFileExtension(); return result; } QString KisPaletteEditor::newGroupName() const { int i = 1; QString groupname = i18nc("Default new group name", "New Group %1", QString::number(i)); while (m_d->modified.groups.contains(groupname)) { i++; groupname = i18nc("Default new group name", "New Group %1", QString::number(i)); } return groupname; } void KisPaletteEditor::uploadPaletteList() const { QList list; Q_FOREACH (KoResourceSP paletteResource, m_d->rServer->resources()) { KoColorSetSP palette = paletteResource.staticCast(); Q_ASSERT(palette); if (!palette->isGlobal()) { list.append(palette); } } m_d->view->document()->setPaletteList(list); } QString KisPaletteEditor::filenameFromPath(const QString &path) const { return QDir::fromNativeSeparators(path).section('/', -1, -1); } diff --git a/libs/ui/widgets/kis_paintop_presets_popup.cpp b/libs/ui/widgets/kis_paintop_presets_popup.cpp index 77eeb8ed96..62ec310189 100644 --- a/libs/ui/widgets/kis_paintop_presets_popup.cpp +++ b/libs/ui/widgets/kis_paintop_presets_popup.cpp @@ -1,850 +1,850 @@ /* This file is part of the KDE project * Copyright (C) 2008 Boudewijn Rempt * Copyright (C) 2010 Lukáš Tvrdý * Copyright (C) 2011 Silvio Heinrich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "widgets/kis_paintop_presets_popup.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_config.h" #include "KisResourceServerProvider.h" #include "kis_lod_availability_widget.h" #include "kis_signal_auto_connection.h" #include // ones from brush engine selector #include #include struct KisPaintOpPresetsPopup::Private { public: Ui_WdgPaintOpSettings uiWdgPaintOpPresetSettings; QGridLayout *layout; KisPaintOpConfigWidget *settingsWidget; QFont smallFont; KisCanvasResourceProvider *resourceProvider; KisFavoriteResourceManager *favoriteResManager; bool detached; bool ignoreHideEvents; bool isCreatingBrushFromScratch = false; QSize minimumSettingsWidgetSize; QRect detachedGeometry; KisSignalAutoConnectionsStore widgetConnections; }; KisPaintOpPresetsPopup::KisPaintOpPresetsPopup(KisCanvasResourceProvider * resourceProvider, KisFavoriteResourceManager* favoriteResourceManager, KisPresetSaveWidget* savePresetWidget, QWidget * parent) : QWidget(parent) , m_d(new Private()) { setObjectName("KisPaintOpPresetsPopup"); setFont(KoDockRegistry::dockFont()); current_paintOpId = ""; m_d->resourceProvider = resourceProvider; m_d->favoriteResManager = favoriteResourceManager; m_d->uiWdgPaintOpPresetSettings.setupUi(this); m_d->layout = new QGridLayout(m_d->uiWdgPaintOpPresetSettings.frmOptionWidgetContainer); m_d->layout->setSizeConstraint(QLayout::SetFixedSize); m_d->uiWdgPaintOpPresetSettings.scratchPad->setupScratchPad(resourceProvider, Qt::white); m_d->uiWdgPaintOpPresetSettings.scratchPad->setCutoutOverlayRect(QRect(25, 25, 200, 200)); m_d->uiWdgPaintOpPresetSettings.dirtyPresetIndicatorButton->setToolTip(i18n("The settings for this preset have changed from their default.")); m_d->uiWdgPaintOpPresetSettings.showPresetsButton->setToolTip(i18n("Toggle showing presets")); m_d->uiWdgPaintOpPresetSettings.showScratchpadButton->setToolTip(i18n("Toggle showing scratchpad")); m_d->uiWdgPaintOpPresetSettings.reloadPresetButton->setToolTip(i18n("Reload the brush preset")); m_d->uiWdgPaintOpPresetSettings.renameBrushPresetButton->setToolTip(i18n("Rename the brush preset")); // creating a new preset from scratch. Part of the brush presets area // the menu options will get filled up later when we are generating all available paintops // in the filter drop-down newPresetBrushEnginesMenu = new QMenu(); // overwrite existing preset and saving a new preset use the same dialog saveDialog = savePresetWidget; saveDialog->scratchPadSetup(resourceProvider); saveDialog->setFavoriteResourceManager(m_d->favoriteResManager); // this is needed when saving the preset saveDialog->hide(); // the area on the brush editor for renaming the brush. make sure edit fields are hidden by default toggleBrushRenameUIActive(false); // DETAIL and THUMBNAIL view changer QMenu* menu = new QMenu(this); menu->setStyleSheet("margin: 6px"); menu->addSection(i18n("Display")); QActionGroup *actionGroup = new QActionGroup(this); KisPresetChooser::ViewMode mode = (KisPresetChooser::ViewMode)KisConfig(true).presetChooserViewMode(); QAction* action = menu->addAction(KisIconUtils::loadIcon("view-preview"), i18n("Thumbnails"), m_d->uiWdgPaintOpPresetSettings.presetWidget, SLOT(slotThumbnailMode())); action->setCheckable(true); action->setChecked(mode == KisPresetChooser::THUMBNAIL); action->setActionGroup(actionGroup); action = menu->addAction(KisIconUtils::loadIcon("view-list-details"), i18n("Details"), m_d->uiWdgPaintOpPresetSettings.presetWidget, SLOT(slotDetailMode())); action->setCheckable(true); action->setChecked(mode == KisPresetChooser::DETAIL); action->setActionGroup(actionGroup); // add horizontal slider for the icon size QSlider* iconSizeSlider = new QSlider(this); iconSizeSlider->setOrientation(Qt::Horizontal); iconSizeSlider->setRange(30, 80); iconSizeSlider->setValue(m_d->uiWdgPaintOpPresetSettings.presetWidget->iconSize()); iconSizeSlider->setMinimumHeight(20); iconSizeSlider->setMinimumWidth(40); iconSizeSlider->setTickInterval(10); QWidgetAction *sliderAction= new QWidgetAction(this); sliderAction->setDefaultWidget(iconSizeSlider); menu->addSection(i18n("Icon Size")); menu->addAction(sliderAction); // configure the button and assign menu m_d->uiWdgPaintOpPresetSettings.presetChangeViewToolButton->setMenu(menu); m_d->uiWdgPaintOpPresetSettings.presetChangeViewToolButton->setPopupMode(QToolButton::InstantPopup); // loading preset from scratch option m_d->uiWdgPaintOpPresetSettings.newPresetEngineButton->setPopupMode(QToolButton::InstantPopup); // show/hide buttons KisConfig cfg(true); m_d->uiWdgPaintOpPresetSettings.showScratchpadButton->setCheckable(true); m_d->uiWdgPaintOpPresetSettings.showScratchpadButton->setChecked(cfg.scratchpadVisible()); if (cfg.scratchpadVisible()) { slotSwitchScratchpad(true); // show scratchpad } else { slotSwitchScratchpad(false); } m_d->uiWdgPaintOpPresetSettings.showPresetsButton->setCheckable(true); m_d->uiWdgPaintOpPresetSettings.showPresetsButton->setChecked(false); slotSwitchShowPresets(false); // hide presets by default // Connections connect(m_d->uiWdgPaintOpPresetSettings.paintPresetIcon, SIGNAL(clicked()), m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(paintPresetImage())); connect(saveDialog, SIGNAL(resourceSelected(KoResourceSP )), this, SLOT(resourceSelected(KoResourceSP ))); connect (m_d->uiWdgPaintOpPresetSettings.renameBrushPresetButton, SIGNAL(clicked(bool)), this, SLOT(slotRenameBrushActivated())); connect (m_d->uiWdgPaintOpPresetSettings.cancelBrushNameUpdateButton, SIGNAL(clicked(bool)), this, SLOT(slotRenameBrushDeactivated())); connect(m_d->uiWdgPaintOpPresetSettings.updateBrushNameButton, SIGNAL(clicked(bool)), this, SLOT(slotSaveRenameCurrentBrush())); connect(m_d->uiWdgPaintOpPresetSettings.renameBrushNameTextField, SIGNAL(returnPressed()), SLOT(slotSaveRenameCurrentBrush())); connect(iconSizeSlider, SIGNAL(sliderMoved(int)), m_d->uiWdgPaintOpPresetSettings.presetWidget, SLOT(slotSetIconSize(int))); connect(iconSizeSlider, SIGNAL(sliderReleased()), m_d->uiWdgPaintOpPresetSettings.presetWidget, SLOT(slotSaveIconSize())); connect(m_d->uiWdgPaintOpPresetSettings.showScratchpadButton, SIGNAL(clicked(bool)), this, SLOT(slotSwitchScratchpad(bool))); connect(m_d->uiWdgPaintOpPresetSettings.showPresetsButton, SIGNAL(clicked(bool)), this, SLOT(slotSwitchShowPresets(bool))); connect(m_d->uiWdgPaintOpPresetSettings.eraseScratchPad, SIGNAL(clicked()), m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(fillDefault())); connect(m_d->uiWdgPaintOpPresetSettings.fillLayer, SIGNAL(clicked()), m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(fillLayer())); connect(m_d->uiWdgPaintOpPresetSettings.fillGradient, SIGNAL(clicked()), m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(fillGradient())); connect(m_d->uiWdgPaintOpPresetSettings.fillSolid, SIGNAL(clicked()), m_d->uiWdgPaintOpPresetSettings.scratchPad, SLOT(fillBackground())); m_d->settingsWidget = 0; setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); connect(m_d->uiWdgPaintOpPresetSettings.saveBrushPresetButton, SIGNAL(clicked()), this, SLOT(slotSaveBrushPreset())); connect(m_d->uiWdgPaintOpPresetSettings.saveNewBrushPresetButton, SIGNAL(clicked()), this, SLOT(slotSaveNewBrushPreset())); connect(m_d->uiWdgPaintOpPresetSettings.reloadPresetButton, SIGNAL(clicked()), this, SIGNAL(reloadPresetClicked())); connect(m_d->uiWdgPaintOpPresetSettings.dirtyPresetCheckBox, SIGNAL(toggled(bool)), this, SIGNAL(dirtyPresetToggled(bool))); connect(m_d->uiWdgPaintOpPresetSettings.eraserBrushSizeCheckBox, SIGNAL(toggled(bool)), this, SIGNAL(eraserBrushSizeToggled(bool))); connect(m_d->uiWdgPaintOpPresetSettings.eraserBrushOpacityCheckBox, SIGNAL(toggled(bool)), this, SIGNAL(eraserBrushOpacityToggled(bool))); // preset widget connections connect(m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser, SIGNAL(resourceSelected(KoResourceSP )), this, SIGNAL(signalResourceSelected(KoResourceSP ))); connect(m_d->uiWdgPaintOpPresetSettings.reloadPresetButton, SIGNAL(clicked()), m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser, SLOT(updateViewSettings())); connect(m_d->uiWdgPaintOpPresetSettings.reloadPresetButton, SIGNAL(clicked()), SLOT(slotUpdatePresetSettings())); m_d->detached = false; m_d->ignoreHideEvents = false; m_d->minimumSettingsWidgetSize = QSize(0, 0); m_d->detachedGeometry = QRect(100, 100, 0, 0); m_d->uiWdgPaintOpPresetSettings.dirtyPresetCheckBox->setChecked(cfg.useDirtyPresets()); m_d->uiWdgPaintOpPresetSettings.eraserBrushSizeCheckBox->setChecked(cfg.useEraserBrushSize()); m_d->uiWdgPaintOpPresetSettings.eraserBrushOpacityCheckBox->setChecked(cfg.useEraserBrushOpacity()); m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability->setCanvasResourceManager(resourceProvider->resourceManager()); connect(resourceProvider->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), SLOT(slotResourceChanged(int,QVariant))); connect(m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability, SIGNAL(sigUserChangedLodAvailability(bool)), SLOT(slotLodAvailabilityChanged(bool))); connect(m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability, SIGNAL(sigUserChangedLodThreshold(qreal)), SLOT(slotLodThresholdChanged(qreal))); slotResourceChanged(KisCanvasResourceProvider::LodAvailability, resourceProvider->resourceManager()-> resource(KisCanvasResourceProvider::LodAvailability)); slotResourceChanged(KisCanvasResourceProvider::LodSizeThreshold, resourceProvider->resourceManager()-> resource(KisCanvasResourceProvider::LodSizeThreshold)); connect(m_d->uiWdgPaintOpPresetSettings.brushEgineComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdatePaintOpFilter())); connect(m_d->uiWdgPaintOpPresetSettings.bnBlacklistPreset, SIGNAL(clicked()), this, SLOT(slotBlackListCurrentPreset())); updateThemedIcons(); // setup things like the scene construct images, layers, etc that is a one-time thing m_d->uiWdgPaintOpPresetSettings.liveBrushPreviewView->setup(); } void KisPaintOpPresetsPopup::slotBlackListCurrentPreset() { KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); KisPaintOpPresetSP curPreset = m_d->resourceProvider->currentPreset(); if (rServer->resourceByName(curPreset->name())) { - rServer->removeResourceAndBlacklist(curPreset); + rServer->removeResourceFromServer(curPreset); } } void KisPaintOpPresetsPopup::slotRenameBrushActivated() { toggleBrushRenameUIActive(true); } void KisPaintOpPresetsPopup::slotRenameBrushDeactivated() { toggleBrushRenameUIActive(false); } void KisPaintOpPresetsPopup::toggleBrushRenameUIActive(bool isRenaming) { // This function doesn't really do anything except get the UI in a state to rename a brush preset m_d->uiWdgPaintOpPresetSettings.renameBrushNameTextField->setVisible(isRenaming); m_d->uiWdgPaintOpPresetSettings.updateBrushNameButton->setVisible(isRenaming); m_d->uiWdgPaintOpPresetSettings.cancelBrushNameUpdateButton->setVisible(isRenaming); // hide these below areas while renaming m_d->uiWdgPaintOpPresetSettings.currentBrushNameLabel->setVisible(!isRenaming); m_d->uiWdgPaintOpPresetSettings.renameBrushPresetButton->setVisible(!isRenaming); m_d->uiWdgPaintOpPresetSettings.saveBrushPresetButton->setEnabled(!isRenaming); m_d->uiWdgPaintOpPresetSettings.saveBrushPresetButton->setVisible(!isRenaming); m_d->uiWdgPaintOpPresetSettings.saveNewBrushPresetButton->setEnabled(!isRenaming); m_d->uiWdgPaintOpPresetSettings.saveNewBrushPresetButton->setVisible(!isRenaming); // if the presets area is shown, only then can you show/hide the load default brush // need to think about weird state when you are in the middle of renaming a brush // what happens if you try to change presets. maybe we should auto-hide (or disable) // the presets area in this case if (m_d->uiWdgPaintOpPresetSettings.presetWidget->isVisible()) { m_d->uiWdgPaintOpPresetSettings.newPresetEngineButton->setVisible(!isRenaming); m_d->uiWdgPaintOpPresetSettings.bnBlacklistPreset->setVisible(!isRenaming); } } void KisPaintOpPresetsPopup::slotSaveRenameCurrentBrush() { // if you are renaming a brush, that is different than updating the settings // make sure we are in a clean state before renaming. This logic might change, // but that is what we are going with for now emit reloadPresetClicked(); m_d->favoriteResManager->setBlockUpdates(true); // get a reference to the existing (and new) file name and path that we are working with KisPaintOpPresetSP curPreset = m_d->resourceProvider->currentPreset(); if (!curPreset) return; KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); QString saveLocation = rServer->saveLocation(); QString originalPresetName = curPreset->name(); QString renamedPresetName = m_d->uiWdgPaintOpPresetSettings.renameBrushNameTextField->text(); QString originalPresetPathAndFile = saveLocation + originalPresetName + curPreset->defaultFileExtension(); QString renamedPresetPathAndFile = saveLocation + renamedPresetName + curPreset->defaultFileExtension(); // create a new brush preset with the name specified and add to resource provider KisPaintOpPresetSP newPreset = curPreset->clone(); newPreset->setFilename(renamedPresetPathAndFile); // this also contains the path newPreset->setName(renamedPresetName); newPreset->setImage(curPreset->image()); // use existing thumbnail (might not need to do this) newPreset->setDirty(false); newPreset->setValid(true); rServer->addResource(newPreset); resourceSelected(newPreset); // refresh and select our freshly renamed resource // Now blacklist the original file if (rServer->resourceByName(originalPresetName)) { - rServer->removeResourceAndBlacklist(curPreset); + rServer->removeResourceFromServer(curPreset); } m_d->favoriteResManager->setBlockUpdates(false); toggleBrushRenameUIActive(false); // this returns the UI to its original state after saving slotUpdatePresetSettings(); // update visibility of dirty preset and icon } void KisPaintOpPresetsPopup::slotResourceChanged(int key, const QVariant &value) { if (key == KisCanvasResourceProvider::LodAvailability) { m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability->slotUserChangedLodAvailability(value.toBool()); } else if (key == KisCanvasResourceProvider::LodSizeThreshold) { m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability->slotUserChangedLodThreshold(value.toDouble()); } else if (key == KisCanvasResourceProvider::Size) { m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability->slotUserChangedSize(value.toDouble()); } } void KisPaintOpPresetsPopup::slotLodAvailabilityChanged(bool value) { m_d->resourceProvider->resourceManager()->setResource(KisCanvasResourceProvider::LodAvailability, QVariant(value)); } void KisPaintOpPresetsPopup::slotLodThresholdChanged(qreal value) { m_d->resourceProvider->resourceManager()->setResource(KisCanvasResourceProvider::LodSizeThreshold, QVariant(value)); } KisPaintOpPresetsPopup::~KisPaintOpPresetsPopup() { if (m_d->settingsWidget) { m_d->layout->removeWidget(m_d->settingsWidget); m_d->settingsWidget->hide(); m_d->settingsWidget->setParent(0); m_d->settingsWidget = 0; } delete m_d; delete newPresetBrushEnginesMenu; } void KisPaintOpPresetsPopup::setPaintOpSettingsWidget(QWidget * widget) { if (m_d->settingsWidget) { m_d->layout->removeWidget(m_d->settingsWidget); m_d->uiWdgPaintOpPresetSettings.frmOptionWidgetContainer->updateGeometry(); } m_d->layout->update(); updateGeometry(); m_d->widgetConnections.clear(); m_d->settingsWidget = 0; if (widget) { m_d->settingsWidget = dynamic_cast(widget); KIS_ASSERT_RECOVER_RETURN(m_d->settingsWidget); KisConfig cfg(true); if (m_d->settingsWidget->supportScratchBox() && cfg.scratchpadVisible()) { slotSwitchScratchpad(true); } else { slotSwitchScratchpad(false); } m_d->widgetConnections.addConnection(m_d->settingsWidget, SIGNAL(sigConfigurationItemChanged()), this, SLOT(slotUpdateLodAvailability())); widget->setFont(m_d->smallFont); QSize hint = widget->sizeHint(); m_d->minimumSettingsWidgetSize = QSize(qMax(hint.width(), m_d->minimumSettingsWidgetSize.width()), qMax(hint.height(), m_d->minimumSettingsWidgetSize.height())); widget->setMinimumSize(m_d->minimumSettingsWidgetSize); m_d->layout->addWidget(widget); // hook up connections that will monitor if our preset is dirty or not. Show a notification if it is if (m_d->resourceProvider && m_d->resourceProvider->currentPreset() ) { KisPaintOpPresetSP preset = m_d->resourceProvider->currentPreset(); m_d->widgetConnections.addConnection(preset->updateProxy(), SIGNAL(sigSettingsChanged()), this, SLOT(slotUpdatePresetSettings())); } m_d->layout->update(); widget->show(); } slotUpdateLodAvailability(); } void KisPaintOpPresetsPopup::slotUpdateLodAvailability() { if (!m_d->settingsWidget) return; KisPaintopLodLimitations l = m_d->settingsWidget->lodLimitations(); m_d->uiWdgPaintOpPresetSettings.wdgLodAvailability->setLimitations(l); } QImage KisPaintOpPresetsPopup::cutOutOverlay() { return m_d->uiWdgPaintOpPresetSettings.scratchPad->cutoutOverlay(); } void KisPaintOpPresetsPopup::contextMenuEvent(QContextMenuEvent *e) { Q_UNUSED(e); } void KisPaintOpPresetsPopup::switchDetached(bool show) { if (parentWidget()) { m_d->detached = !m_d->detached; if (m_d->detached) { m_d->ignoreHideEvents = true; if (show) { parentWidget()->show(); } m_d->ignoreHideEvents = false; } else { parentWidget()->hide(); } KisConfig cfg(false); cfg.setPaintopPopupDetached(m_d->detached); } } void KisPaintOpPresetsPopup::setCreatingBrushFromScratch(bool enabled) { m_d->isCreatingBrushFromScratch = enabled; } void KisPaintOpPresetsPopup::resourceSelected(KoResourceSP resource) { // this gets called every time the brush editor window is opened // TODO: this gets called multiple times whenever the preset is changed in the presets area // the connections probably need to be thought about with this a bit more to keep things in sync m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser->setCurrentResource(resource); // find the display name of the brush engine and append it to the selected preset display QString currentBrushEngineName; QPixmap currentBrushEngineIcon = QPixmap(26, 26); currentBrushEngineIcon.fill(Qt::transparent); for(int i=0; i < sortedBrushEnginesList.length(); i++) { if (sortedBrushEnginesList.at(i).id == currentPaintOpId() ) { currentBrushEngineName = sortedBrushEnginesList.at(i).name; currentBrushEngineIcon = sortedBrushEnginesList.at(i).icon.pixmap(26, 26); } } // brush names have underscores as part of the file name (to help with building). We don't really need underscores // when viewing the names, so replace them with spaces QString formattedBrushName = resource->name().replace("_", " "); m_d->uiWdgPaintOpPresetSettings.currentBrushNameLabel->setText(formattedBrushName); m_d->uiWdgPaintOpPresetSettings.currentBrushEngineLabel->setText(i18nc("%1 is the name of a brush engine", "%1 Engine", currentBrushEngineName)); m_d->uiWdgPaintOpPresetSettings.currentBrushEngineIcon->setPixmap(currentBrushEngineIcon); m_d->uiWdgPaintOpPresetSettings.renameBrushNameTextField->setText(resource->name()); // use file name // get the preset image and pop it into the thumbnail area on the top of the brush editor m_d->uiWdgPaintOpPresetSettings.presetThumbnailicon->setPixmap(QPixmap::fromImage(resource->image().scaled(55, 55, Qt::KeepAspectRatio, Qt::SmoothTransformation))); toggleBrushRenameUIActive(false); // reset the UI state of renaming a brush if we are changing brush presets slotUpdatePresetSettings(); // check to see if the dirty preset icon needs to be shown } bool variantLessThan(const KisPaintOpInfo v1, const KisPaintOpInfo v2) { return v1.priority < v2.priority; } void KisPaintOpPresetsPopup::setPaintOpList(const QList< KisPaintOpFactory* >& list) { m_d->uiWdgPaintOpPresetSettings.brushEgineComboBox->clear(); // reset combobox list just in case // create a new list so we can sort it and populate the brush engine combo box sortedBrushEnginesList.clear(); // just in case this function is called again, don't keep adding to the list for(int i=0; i < list.length(); i++) { KisPaintOpInfo paintOpInfo; paintOpInfo.id = list.at(i)->id(); paintOpInfo.name = list.at(i)->name(); paintOpInfo.icon = list.at(i)->icon(); paintOpInfo.priority = list.at(i)->priority(); sortedBrushEnginesList.append(paintOpInfo); } std::stable_sort(sortedBrushEnginesList.begin(), sortedBrushEnginesList.end(), variantLessThan ); // add an "All" option at the front to show all presets QPixmap emptyPixmap = QPixmap(22,22); emptyPixmap.fill(Qt::transparent); // if we create a new brush from scratch, we need a full list of paintops to choose from // we don't want "All", so populate the list before that is added newPresetBrushEnginesMenu->actions().clear(); // clean out list in case we run this again newBrushEngineOptions.clear(); for (int j = 0; j < sortedBrushEnginesList.length(); j++) { KisAction * newEngineAction = static_cast( newPresetBrushEnginesMenu->addAction(sortedBrushEnginesList[j].name)); newEngineAction->setObjectName(sortedBrushEnginesList[j].id); // we need the ID for changing the paintop when action triggered newEngineAction->setIcon(sortedBrushEnginesList[j].icon); newBrushEngineOptions.append(newEngineAction); connect(newEngineAction, SIGNAL(triggered()), this, SLOT(slotCreateNewBrushPresetEngine())); } m_d->uiWdgPaintOpPresetSettings.newPresetEngineButton->setMenu(newPresetBrushEnginesMenu); // fill the list into the brush combo box sortedBrushEnginesList.push_front(KisPaintOpInfo(QString("all_options"), i18n("All"), QString(""), QIcon(emptyPixmap), 0 )); for (int m = 0; m < sortedBrushEnginesList.length(); m++) { m_d->uiWdgPaintOpPresetSettings.brushEgineComboBox->addItem(sortedBrushEnginesList[m].icon, sortedBrushEnginesList[m].name, QVariant(sortedBrushEnginesList[m].id)); } } void KisPaintOpPresetsPopup::setCurrentPaintOpId(const QString& paintOpId) { current_paintOpId = paintOpId; } QString KisPaintOpPresetsPopup::currentPaintOpId() { return current_paintOpId; } void KisPaintOpPresetsPopup::setPresetImage(const QImage& image) { m_d->uiWdgPaintOpPresetSettings.scratchPad->setPresetImage(image); saveDialog->brushPresetThumbnailWidget->setPresetImage(image); } void KisPaintOpPresetsPopup::hideEvent(QHideEvent *event) { if (m_d->ignoreHideEvents) { return; } if (m_d->detached) { m_d->detachedGeometry = window()->geometry(); } QWidget::hideEvent(event); } void KisPaintOpPresetsPopup::showEvent(QShowEvent *) { if (m_d->detached) { window()->setGeometry(m_d->detachedGeometry); } emit brushEditorShown(); } void KisPaintOpPresetsPopup::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); if (parentWidget()) { // Make sure resizing doesn't push this widget out of the screen QRect screenRect = QApplication::desktop()->availableGeometry(this); QRect newPositionRect = kisEnsureInRect(parentWidget()->geometry(), screenRect); parentWidget()->setGeometry(newPositionRect); } } bool KisPaintOpPresetsPopup::detached() const { return m_d->detached; } void KisPaintOpPresetsPopup::slotSwitchScratchpad(bool visible) { // hide all the internal controls except the toggle button m_d->uiWdgPaintOpPresetSettings.scratchPad->setVisible(visible); m_d->uiWdgPaintOpPresetSettings.paintPresetIcon->setVisible(visible); m_d->uiWdgPaintOpPresetSettings.fillGradient->setVisible(visible); m_d->uiWdgPaintOpPresetSettings.fillLayer->setVisible(visible); m_d->uiWdgPaintOpPresetSettings.fillSolid->setVisible(visible); m_d->uiWdgPaintOpPresetSettings.eraseScratchPad->setVisible(visible); m_d->uiWdgPaintOpPresetSettings.scratchpadSidebarLabel->setVisible(visible); if (visible) { m_d->uiWdgPaintOpPresetSettings.showScratchpadButton->setIcon(KisIconUtils::loadIcon("arrow-left")); } else { m_d->uiWdgPaintOpPresetSettings.showScratchpadButton->setIcon(KisIconUtils::loadIcon("arrow-right")); } KisConfig cfg(false); cfg.setScratchpadVisible(visible); } void KisPaintOpPresetsPopup::slotSwitchShowEditor(bool visible) { m_d->uiWdgPaintOpPresetSettings.brushEditorSettingsControls->setVisible(visible); } void KisPaintOpPresetsPopup::slotSwitchShowPresets(bool visible) { m_d->uiWdgPaintOpPresetSettings.presetWidget->setVisible(visible); m_d->uiWdgPaintOpPresetSettings.presetChangeViewToolButton->setVisible(visible); m_d->uiWdgPaintOpPresetSettings.brushEgineComboBox->setVisible(visible); m_d->uiWdgPaintOpPresetSettings.engineFilterLabel->setVisible(visible); m_d->uiWdgPaintOpPresetSettings.presetsSidebarLabel->setVisible(visible); m_d->uiWdgPaintOpPresetSettings.newPresetEngineButton->setVisible(visible); m_d->uiWdgPaintOpPresetSettings.bnBlacklistPreset->setVisible(visible); // we only want a spacer to work when the toggle icon is present. Otherwise the list of presets will shrink // which is something we don't want if (visible) { m_d->uiWdgPaintOpPresetSettings.presetsSpacer->changeSize(0,0, QSizePolicy::Ignored,QSizePolicy::Ignored); m_d->uiWdgPaintOpPresetSettings.showPresetsButton->setIcon(KisIconUtils::loadIcon("arrow-right")); } else { m_d->uiWdgPaintOpPresetSettings.presetsSpacer->changeSize(0,0, QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding); m_d->uiWdgPaintOpPresetSettings.showPresetsButton->setIcon(KisIconUtils::loadIcon("arrow-left")); } } void KisPaintOpPresetsPopup::slotUpdatePaintOpFilter() { QVariant userData = m_d->uiWdgPaintOpPresetSettings.brushEgineComboBox->currentData(); // grab paintOpID from data QString filterPaintOpId = userData.toString(); if (filterPaintOpId == "all_options") { filterPaintOpId = ""; } m_d->uiWdgPaintOpPresetSettings.presetWidget->setPresetFilter(filterPaintOpId); } void KisPaintOpPresetsPopup::slotSaveBrushPreset() { // here we are assuming that people want to keep their existing preset icon. We will just update the // settings and save a new copy with the same name. // there is a dialog with save options, but we don't need to show it in this situation saveDialog->useNewBrushDialog(false); // this mostly just makes sure we keep the existing brush preset name when saving saveDialog->loadExistingThumbnail(); // This makes sure we use the existing preset icon when updating the existing brush preset saveDialog->savePreset(); // refresh the view settings so the brush doesn't appear dirty slotUpdatePresetSettings(); } void KisPaintOpPresetsPopup::slotSaveNewBrushPreset() { saveDialog->useNewBrushDialog(true); saveDialog->saveScratchPadThumbnailArea(m_d->uiWdgPaintOpPresetSettings.scratchPad->cutoutOverlay()); saveDialog->showDialog(); } void KisPaintOpPresetsPopup::slotCreateNewBrushPresetEngine() { emit createPresetFromScratch(sender()->objectName()); } void KisPaintOpPresetsPopup::updateViewSettings() { m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser->updateViewSettings(); } void KisPaintOpPresetsPopup::currentPresetChanged(KisPaintOpPresetSP preset) { if (preset) { m_d->uiWdgPaintOpPresetSettings.presetWidget->smallPresetChooser->setCurrentResource(preset); setCurrentPaintOpId(preset->paintOp().id()); } } void KisPaintOpPresetsPopup::updateThemedIcons() { m_d->uiWdgPaintOpPresetSettings.paintPresetIcon->setIcon(KisIconUtils::loadIcon("krita_tool_freehand")); m_d->uiWdgPaintOpPresetSettings.fillLayer->setIcon(KisIconUtils::loadIcon("document-new")); m_d->uiWdgPaintOpPresetSettings.fillLayer->hide(); m_d->uiWdgPaintOpPresetSettings.fillGradient->setIcon(KisIconUtils::loadIcon("krita_tool_gradient")); m_d->uiWdgPaintOpPresetSettings.fillSolid->setIcon(KisIconUtils::loadIcon("krita_tool_color_fill")); m_d->uiWdgPaintOpPresetSettings.eraseScratchPad->setIcon(KisIconUtils::loadIcon("edit-delete")); m_d->uiWdgPaintOpPresetSettings.newPresetEngineButton->setIcon(KisIconUtils::loadIcon("addlayer")); m_d->uiWdgPaintOpPresetSettings.bnBlacklistPreset->setIcon(KisIconUtils::loadIcon("deletelayer")); m_d->uiWdgPaintOpPresetSettings.reloadPresetButton->setIcon(KisIconUtils::loadIcon("updateColorize")); // refresh icon m_d->uiWdgPaintOpPresetSettings.renameBrushPresetButton->setIcon(KisIconUtils::loadIcon("dirty-preset")); // edit icon m_d->uiWdgPaintOpPresetSettings.dirtyPresetIndicatorButton->setIcon(KisIconUtils::loadIcon("warning")); m_d->uiWdgPaintOpPresetSettings.newPresetEngineButton->setIcon(KisIconUtils::loadIcon("addlayer")); m_d->uiWdgPaintOpPresetSettings.bnBlacklistPreset->setIcon(KisIconUtils::loadIcon("deletelayer")); m_d->uiWdgPaintOpPresetSettings.presetChangeViewToolButton->setIcon(KisIconUtils::loadIcon("configure")); // if we cannot see the "Preset label", we know it is not visible // maybe this can also be stored in the config like the scratchpad? if (m_d->uiWdgPaintOpPresetSettings.presetsSidebarLabel->isVisible()) { m_d->uiWdgPaintOpPresetSettings.presetsSpacer->changeSize(0,0, QSizePolicy::Ignored,QSizePolicy::Ignored); m_d->uiWdgPaintOpPresetSettings.showPresetsButton->setIcon(KisIconUtils::loadIcon("arrow-right")); } else { m_d->uiWdgPaintOpPresetSettings.presetsSpacer->changeSize(0,0, QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding); m_d->uiWdgPaintOpPresetSettings.showPresetsButton->setIcon(KisIconUtils::loadIcon("arrow-left")); } // we store whether the scratchpad if visible in the config. KisConfig cfg(true); if (cfg.scratchpadVisible()) { m_d->uiWdgPaintOpPresetSettings.showScratchpadButton->setIcon(KisIconUtils::loadIcon("arrow-left")); } else { m_d->uiWdgPaintOpPresetSettings.showScratchpadButton->setIcon(KisIconUtils::loadIcon("arrow-right")); } } void KisPaintOpPresetsPopup::slotUpdatePresetSettings() { if (!m_d->resourceProvider) { return; } if (!m_d->resourceProvider->currentPreset()) { return; } // hide options on UI if we are creating a brush preset from scratch to prevent confusion if (m_d->isCreatingBrushFromScratch) { m_d->uiWdgPaintOpPresetSettings.dirtyPresetIndicatorButton->setVisible(false); m_d->uiWdgPaintOpPresetSettings.reloadPresetButton->setVisible(false); m_d->uiWdgPaintOpPresetSettings.saveBrushPresetButton->setVisible(false); m_d->uiWdgPaintOpPresetSettings.renameBrushPresetButton->setVisible(false); } else { bool isPresetDirty = m_d->resourceProvider->currentPreset()->isDirty(); // don't need to reload or overwrite a clean preset m_d->uiWdgPaintOpPresetSettings.dirtyPresetIndicatorButton->setVisible(isPresetDirty); m_d->uiWdgPaintOpPresetSettings.reloadPresetButton->setVisible(isPresetDirty); m_d->uiWdgPaintOpPresetSettings.saveBrushPresetButton->setEnabled(isPresetDirty); m_d->uiWdgPaintOpPresetSettings.renameBrushPresetButton->setVisible(true); } // update live preview area in here... // don't update the live preview if the widget is not visible. if (m_d->uiWdgPaintOpPresetSettings.liveBrushPreviewView->isVisible()) { m_d->uiWdgPaintOpPresetSettings.liveBrushPreviewView->setCurrentPreset(m_d->resourceProvider->currentPreset()); m_d->uiWdgPaintOpPresetSettings.liveBrushPreviewView->updateStroke(); } } diff --git a/libs/ui/widgets/kis_paintop_presets_save.cpp b/libs/ui/widgets/kis_paintop_presets_save.cpp index 266c422a59..89a7e21a5f 100644 --- a/libs/ui/widgets/kis_paintop_presets_save.cpp +++ b/libs/ui/widgets/kis_paintop_presets_save.cpp @@ -1,271 +1,272 @@ /* This file is part of the KDE project * Copyright (C) 2017 Scott Petrovic * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "widgets/kis_paintop_presets_save.h" #include #include #include #include #include #include #include "KisImportExportManager.h" #include "QDesktopServices" #include "KisResourceServerProvider.h" #include KisPresetSaveWidget::KisPresetSaveWidget(QWidget * parent) : KisPaintOpPresetSaveDialog(parent) { // this is setting the area we will "capture" for saving the brush preset. It can potentially be a different // area that the entire scratchpad brushPresetThumbnailWidget->setCutoutOverlayRect(QRect(0, 0, brushPresetThumbnailWidget->height(), brushPresetThumbnailWidget->width())); // we will default to reusing the previous preset thumbnail // have that checked by default, hide the other elements, and load the last preset image connect(clearBrushPresetThumbnailButton, SIGNAL(clicked(bool)), brushPresetThumbnailWidget, SLOT(fillDefault())); connect(loadImageIntoThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadImageFromFile())); connect(loadScratchPadThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadScratchpadThumbnail())); connect(loadExistingThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadExistingThumbnail())); connect(loadIconLibraryThumbnailButton, SIGNAL(clicked(bool)), this, SLOT(loadImageFromLibrary())); connect(savePresetButton, SIGNAL(clicked(bool)), this, SLOT(savePreset())); connect(cancelButton, SIGNAL(clicked(bool)), this, SLOT(close())); } KisPresetSaveWidget::~KisPresetSaveWidget() { } void KisPresetSaveWidget::scratchPadSetup(KisCanvasResourceProvider* resourceProvider) { m_resourceProvider = resourceProvider; brushPresetThumbnailWidget->setupScratchPad(m_resourceProvider, Qt::white); } void KisPresetSaveWidget::showDialog() { setModal(true); // set the name of the current brush preset area. KisPaintOpPresetSP preset = m_resourceProvider->currentPreset(); // UI will look a bit different if we are saving a new brush if (m_useNewBrushDialog) { setWindowTitle(i18n("Save New Brush Preset")); newBrushNameTexField->setVisible(true); clearBrushPresetThumbnailButton->setVisible(true); loadImageIntoThumbnailButton->setVisible(true); currentBrushNameLabel->setVisible(false); if (preset) { newBrushNameTexField->setText(preset->name().append(" ").append(i18n("Copy"))); } } else { setWindowTitle(i18n("Save Brush Preset")); if (preset) { currentBrushNameLabel->setText(preset->name()); } newBrushNameTexField->setVisible(false); currentBrushNameLabel->setVisible(true); } brushPresetThumbnailWidget->paintPresetImage(); show(); } void KisPresetSaveWidget::loadImageFromFile() { // create a dialog to retrieve an image file. KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument"); dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import)); dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); QString filename = dialog.filename(); // the filename() returns the entire path & file name, not just the file name if (filename != "") { // empty if "cancel" is pressed // take that file and load it into the thumbnail are const QImage imageToLoad(filename); brushPresetThumbnailWidget->fillTransparent(); // clear the background in case our new image has transparency brushPresetThumbnailWidget->paintCustomImage(imageToLoad); } } void KisPresetSaveWidget::loadScratchpadThumbnail() { brushPresetThumbnailWidget->paintCustomImage(scratchPadThumbnailArea); } void KisPresetSaveWidget::loadExistingThumbnail() { brushPresetThumbnailWidget->paintPresetImage(); } void KisPresetSaveWidget::loadImageFromLibrary() { //add dialog code here. QDialog *dlg = new QDialog(this); dlg->setWindowTitle(i18n("Preset Icon Library")); QVBoxLayout *layout = new QVBoxLayout(); dlg->setLayout(layout); KisPaintopPresetIconLibrary *libWidget = new KisPaintopPresetIconLibrary(dlg); layout->addWidget(libWidget); QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel, dlg); connect(buttons, SIGNAL(accepted()), dlg, SLOT(accept())); connect(buttons, SIGNAL(rejected()), dlg, SLOT(reject())); layout->addWidget(buttons); //if dialog accepted, get image. if (dlg->exec()==QDialog::Accepted) { QImage presetImage = libWidget->getImage(); brushPresetThumbnailWidget->paintCustomImage(presetImage); } } void KisPresetSaveWidget::setFavoriteResourceManager(KisFavoriteResourceManager * favManager) { m_favoriteResourceManager = favManager; } void KisPresetSaveWidget::savePreset() { KisPaintOpPresetSP curPreset = m_resourceProvider->currentPreset(); if (!curPreset) return; m_favoriteResourceManager->setBlockUpdates(true); KisPaintOpPresetSP oldPreset = curPreset->clone(); // tags are not cloned with this oldPreset->load(); KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); QString saveLocation = rServer->saveLocation(); // if we are saving a new brush, use what we type in for the input QString presetName = m_useNewBrushDialog ? newBrushNameTexField->text() : curPreset->name(); QString currentPresetFileName = saveLocation + presetName.replace(" ", "_") + curPreset->defaultFileExtension(); bool isSavingOverExistingPreset = rServer->resourceByName(presetName); // make a back up of the existing preset if we are saving over it if (isSavingOverExistingPreset) { QString currentDate = QDate::currentDate().toString(Qt::ISODate); QString currentTime = QTime::currentTime().toString(Qt::ISODate).remove(QChar(':')); QString presetFilename = saveLocation + presetName.replace(" ", "_") + "_backup_" + currentDate + "-" + currentTime + oldPreset->defaultFileExtension(); oldPreset->setFilename(presetFilename); oldPreset->setName(presetName); oldPreset->setDirty(false); oldPreset->setValid(true); // add backup resource to the blacklist - rServer->addResource(oldPreset); - rServer->removeResourceAndBlacklist(oldPreset); + qDebug() << "Broken stuff going on in KisPresetSaveWidget::savePreset"; +// rServer->addResource(oldPreset); +// rServer->removeResource(oldPreset); QStringList tags; tags = rServer->assignedTagsList(curPreset); Q_FOREACH (const QString & tag, tags) { rServer->addTag(oldPreset, tag); } } if (m_useNewBrushDialog) { KisPaintOpPresetSP newPreset = curPreset->clone(); newPreset->setFilename(currentPresetFileName); newPreset->setName(presetName); newPreset->setImage(brushPresetThumbnailWidget->cutoutOverlay()); newPreset->setDirty(false); newPreset->setValid(true); // keep tags if we are saving over existing preset if (isSavingOverExistingPreset) { QStringList tags; tags = rServer->assignedTagsList(curPreset); Q_FOREACH (const QString & tag, tags) { rServer->addTag(newPreset, tag); } } rServer->addResource(newPreset); // trying to get brush preset to load after it is created emit resourceSelected(newPreset); } else { // saving a preset that is replacing an existing one + qDebug() << "KisPresetSaveWidget::save: broken resource stuff going on. Manual blacklisting and updating or resources"; if (curPreset->filename().contains(saveLocation) == false || curPreset->filename().contains(presetName) == false) { - rServer->removeResourceAndBlacklist(curPreset); + rServer->removeResourceFromServer(curPreset); curPreset->setFilename(currentPresetFileName); curPreset->setName(presetName); } if (!rServer->resourceByFilename(curPreset->filename())){ //this is necessary so that we can get the preset afterwards. rServer->addResource(curPreset, false); - //rServer->removeFromBlacklist(curPreset); } if (curPreset->image().isNull()) { curPreset->setImage(brushPresetThumbnailWidget->cutoutOverlay()); } // we should not load() the brush right after saving because it will reset all our saved // eraser size and opacity values curPreset->save(); } // // HACK ALERT! the server does not notify the observers // // automatically, so we need to call theupdate manually! // rServer->tagCategoryMembersChanged(); m_favoriteResourceManager->setBlockUpdates(false); close(); // we are done... so close the save brush dialog } void KisPresetSaveWidget::saveScratchPadThumbnailArea(QImage image) { scratchPadThumbnailArea = image; } void KisPresetSaveWidget::useNewBrushDialog(bool show) { m_useNewBrushDialog = show; } #include "moc_kis_paintop_presets_save.cpp" diff --git a/libs/widgets/KoResourceServer.h b/libs/widgets/KoResourceServer.h index d3d949a201..6f53347c92 100644 --- a/libs/widgets/KoResourceServer.h +++ b/libs/widgets/KoResourceServer.h @@ -1,349 +1,313 @@ /* This file is part of the KDE project Copyright (c) 1999 Matthias Elter Copyright (c) 2003 Patrick Julien Copyright (c) 2005 Sven Langkamp Copyright (c) 2007 Jan Hambrecht Copyright (C) 2011 Srikanth Tiyyagura Copyright (c) 2013 Sascha Suelzer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KORESOURCESERVER_H #define KORESOURCESERVER_H -#include #include #include #include #include #include #include #include #include "KoResource.h" #include "KoResourceServerObserver.h" #include "KoResourcePaths.h" #include #include #include #include #include "kritawidgets_export.h" #include "WidgetsDebug.h" class KoResource; /** * KoResourceServerBase is the base class of all resource servers */ class KRITAWIDGETS_EXPORT KoResourceServerBase { public: /** * Constructs a KoResourceServerBase * @param type type, has to be the same as used by KoResourcePaths */ KoResourceServerBase(const QString& type) : m_resourceModel(KisResourceModelProvider::resourceModel(type)) , m_type(type) { qDebug() << "Creating KoResourceServerBase" << m_type; } virtual ~KoResourceServerBase() {} virtual int resourceCount() const = 0; protected: KisResourceModel *m_resourceModel {0}; QString m_type; }; /** * KoResourceServer manages the resources of one type. It stores, * loads and saves the resources. To keep track of changes the server * can be observed with a KoResourceServerObserver */ template class KoResourceServer : public KoResourceServerBase { public: typedef KoResourceServerObserver ObserverType; KoResourceServer(const QString& type) : KoResourceServerBase(type) { } ~KoResourceServer() override { Q_FOREACH (ObserverType* observer, m_observers) { observer->unsetResourceServer(); } } int resourceCount() const override { return m_resourceModel->rowCount(); } /// Adds an already loaded resource to the server bool addResource(QSharedPointer resource, bool save = true) { if (!resource->valid()) { warnWidgets << "Tried to add an invalid resource!"; return false; } - if (save) { - QFileInfo fileInfo(resource->filename()); - - QDir d(fileInfo.path()); - if (!d.exists()) { - d.mkdir(fileInfo.path()); - } - - if (fileInfo.exists()) { - QString filename = fileInfo.path() + "/" + fileInfo.baseName() + "XXXXXX" + "." + fileInfo.suffix(); - debugWidgets << "fileName is " << filename; - QTemporaryFile file(filename); - if (file.open()) { - debugWidgets << "now " << file.fileName(); - resource->setFilename(file.fileName()); - } - } - - if (!resource->save()) { - warnWidgets << "Could not save resource!"; - return false; - } - } - - Q_ASSERT(!resource->filename().isEmpty() || !resource->name().isEmpty()); - if (resource->filename().isEmpty()) { - resource->setFilename(resource->name()); + if (m_resourceModel->addResource(resource, save)) { + notifyResourceAdded(resource); + return true; } - else if (resource->name().isEmpty()) { - resource->setName(resource->filename()); - } - - notifyResourceAdded(resource); - return true; + return false; } /// Remove a resource from Resource Server but not from a file bool removeResourceFromServer(QSharedPointer resource){ - return true; - } - - /// Remove a resource from the resourceserver and blacklist it - - bool removeResourceAndBlacklist(QSharedPointer resource) { - return true; + return m_resourceModel->removeResource(resource); } QList> resources() { QList> resourceList; for (int row = 0; row < m_resourceModel->rowCount(); ++row) { resourceList << m_resourceModel->resourceForIndex(m_resourceModel->index(row, 0)).dynamicCast(); } return resourceList; } /// Returns path where to save user defined and imported resources to virtual QString saveLocation() { return KoResourcePaths::saveLocation(m_type.toLatin1()); } /** * Creates a new resource from a given file and adds them to the resource server * The base implementation does only load one resource per file, override to implement collections * @param filename file name of the resource file to be imported * @param fileCreation decides whether to create the file in the saveLocation() directory */ virtual bool importResourceFile(const QString & filename , bool fileCreation=true) { // QFileInfo fi(filename); // if (!fi.exists()) // return false; // if ( fi.size() == 0) // return false; // QSharedPointer resource = createResource( filename ); // resource->load(); // if (!resource->valid()) { // warnWidgets << "Import failed! Resource is not valid"; // return false; // } // if (fileCreation) { // Q_ASSERT(!resource->defaultFileExtension().isEmpty()); // Q_ASSERT(!saveLocation().isEmpty()); // QString newFilename = saveLocation() + fi.baseName() + resource->defaultFileExtension(); // QFileInfo fileInfo(newFilename); // int i = 1; // while (fileInfo.exists()) { // fileInfo.setFile(saveLocation() + fi.baseName() + QString("%1").arg(i) + resource->defaultFileExtension()); // i++; // } // resource->setFilename(fileInfo.filePath()); // } return true; } /// Removes the resource file from the resource server void removeResourceFile(const QString & filename) { QFileInfo fi(filename); QSharedPointer resource = resourceByFilename(fi.fileName()); if (!resource) { warnWidgets << "Resource file do not exist "; return; } removeResourceFromServer(resource); } /** * Addes an observer to the server * @param observer the observer to be added * @param notifyLoadedResources determines if the observer should be notified about the already loaded resources */ void addObserver(ObserverType* observer, bool notifyLoadedResources = true) { if (observer && !m_observers.contains(observer)) { m_observers.append(observer); if(notifyLoadedResources) { // Q_FOREACH (QSharedPointer resource, ByFilename) { // observer->resourceAdded(resource); // } } } } /** * Removes an observer from the server * @param observer the observer to be removed */ void removeObserver(ObserverType* observer) { int index = m_observers.indexOf( observer ); if( index < 0 ) return; m_observers.removeAt( index ); } QSharedPointer resourceByFilename(const QString& /*filename*/) const { // if (m_resourcesByFilename.contains(filename)) { // return m_resourcesByFilename[filename]; // } return 0; } QSharedPointer resourceByName( const QString& /*name */) const { // if (m_resourcesByName.contains(name)) { // return m_resourcesByName[name]; // } return 0; } QSharedPointer resourceByMD5(const QByteArray& /*md5*/) const { // return m_resourcesByMd5.value(md5); return 0; } /** * Call after changing the content of a resource; * Notifies the connected views. */ void updateResource(QSharedPointer resource) { notifyResourceChanged(resource); } // don't use these method directly since it doesn't update views! void addTag(KoResourceSP resource, const QString& tag) { // m_tagStore->addTag(resource, tag); } // don't use these method directly since it doesn't update views! void delTag(KoResourceSP resource, const QString& tag) { // m_tagStore->delTag(resource, tag); } QStringList assignedTagsList(KoResourceSP resource) const { return QStringList(); //m_tagStore->assignedTagsList(resource); } /// Return the currently stored resources in alphabetical order, overwrite for customized sorting virtual QList> sortedResources() { QMap> sortedNames; // Q_FOREACH (const QString &name, m_resourcesByName.keys()) { // sortedNames.insert(name.toLower(), m_resourcesByName[name]); // } return sortedNames.values(); } protected: void notifyResourceAdded(QSharedPointer resource) { Q_FOREACH (ObserverType* observer, m_observers) { observer->resourceAdded(resource); } } void notifyRemovingResource(QSharedPointer resource) { Q_FOREACH (ObserverType* observer, m_observers) { observer->removingResource(resource); } } void notifyResourceChanged(QSharedPointer resource) { Q_FOREACH (ObserverType* observer, m_observers) { observer->resourceChanged(resource); } } private: QList m_observers; }; #endif // KORESOURCESERVER_H diff --git a/plugins/dockers/gamutmask/gamutmask_dock.cpp b/plugins/dockers/gamutmask/gamutmask_dock.cpp index 460ece3d18..9e089f6a6a 100644 --- a/plugins/dockers/gamutmask/gamutmask_dock.cpp +++ b/plugins/dockers/gamutmask/gamutmask_dock.cpp @@ -1,628 +1,628 @@ /* * Copyright (c) 2018 Anna Medonosova * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gamutmask_dock.h" #include #include #include #include #include #include "ui_wdgGamutMaskChooser.h" class KisMainWindow; struct GamutMaskChooserUI: public QWidget, public Ui_wdgGamutMaskChooser { GamutMaskChooserUI() { setupUi(this); } }; GamutMaskDock::GamutMaskDock() : QDockWidget(i18n("Gamut Masks")) , m_resourceProvider(0) , m_selfClosingTemplate(false) , m_externalTemplateClose(false) , m_creatingNewMask(false) , m_templatePrevSaved(false) , m_selfSelectingMask(false) , m_selectedMask(nullptr) , m_maskDocument(nullptr) , m_view(nullptr) { m_dockerUI = new GamutMaskChooserUI(); m_dockerUI->bnMaskEditor->setIcon(KisIconUtils::loadIcon("dirty-preset")); m_dockerUI->bnMaskDelete->setIcon(KisIconUtils::loadIcon("deletelayer")); m_dockerUI->bnMaskNew->setIcon(KisIconUtils::loadIcon("list-add")); m_dockerUI->bnMaskDuplicate->setIcon(KisIconUtils::loadIcon("duplicatelayer")); m_dockerUI->maskPropertiesBox->setVisible(false); m_dockerUI->bnSaveMask->setIcon(KisIconUtils::loadIcon("document-save")); m_dockerUI->bnCancelMaskEdit->setIcon(KisIconUtils::loadIcon("dialog-cancel")); m_dockerUI->bnPreviewMask->setIcon(KisIconUtils::loadIcon("visible")); QRegularExpression maskTitleRegex("^[-_\\(\\)\\sA-Za-z0-9]+$"); QRegularExpressionValidator* m_maskTitleValidator = new QRegularExpressionValidator(maskTitleRegex, this); m_dockerUI->maskTitleEdit->setValidator(m_maskTitleValidator); KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); rServer->addObserver(this); // gamut mask connections connect(m_dockerUI->bnSaveMask , SIGNAL(clicked()) , SLOT(slotGamutMaskSave())); connect(m_dockerUI->bnCancelMaskEdit , SIGNAL(clicked()) , SLOT(slotGamutMaskCancelEdit())); connect(m_dockerUI->bnPreviewMask , SIGNAL(clicked()) , SLOT(slotGamutMaskPreview())); connect(m_dockerUI->bnMaskEditor , SIGNAL(clicked()) , SLOT(slotGamutMaskEdit())); connect(m_dockerUI->maskChooser, SIGNAL(sigGamutMaskSelected(KoGamutMaskSP)), SLOT(slotGamutMaskSelected(KoGamutMaskSP))); connect(m_dockerUI->bnMaskNew , SIGNAL(clicked()) , SLOT(slotGamutMaskCreateNew())); connect(m_dockerUI->bnMaskDelete , SIGNAL(clicked()) , SLOT(slotGamutMaskDelete())); connect(m_dockerUI->bnMaskDuplicate , SIGNAL(clicked()) , SLOT(slotGamutMaskDuplicate())); setWidget(m_dockerUI); } GamutMaskDock::~GamutMaskDock() { KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); rServer->removeObserver(this); } void GamutMaskDock::setViewManager(KisViewManager* kisview) { m_resourceProvider = kisview->canvasResourceProvider(); selectMask(m_resourceProvider->currentGamutMask()); connect(this, SIGNAL(sigGamutMaskSet(KoGamutMaskSP)), m_resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMaskSP)), Qt::UniqueConnection); connect(this, SIGNAL(sigGamutMaskChanged(KoGamutMaskSP)), m_resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMaskSP)), Qt::UniqueConnection); connect(this, SIGNAL(sigGamutMaskUnset()), m_resourceProvider, SLOT(slotGamutMaskUnset()), Qt::UniqueConnection); connect(this, SIGNAL(sigGamutMaskPreviewUpdate()), m_resourceProvider, SLOT(slotGamutMaskPreviewUpdate()), Qt::UniqueConnection); connect(KisPart::instance(), SIGNAL(sigDocumentRemoved(QString)), this, SLOT(slotDocumentRemoved(QString)), Qt::UniqueConnection); } void GamutMaskDock::slotGamutMaskEdit() { if (!m_selectedMask) { return; } openMaskEditor(); } bool GamutMaskDock::openMaskEditor() { if (!m_selectedMask) { return false; } // find the template resource first, so we can abort the action early on QString maskTemplateFile = KoResourcePaths::findResource(ResourceType::GamutMasks, "GamutMaskTemplate.kra"); if (maskTemplateFile.isEmpty() || maskTemplateFile.isNull() || !QFile::exists(maskTemplateFile)) { dbgPlugins << "GamutMaskDock::openMaskEditor(): maskTemplateFile (" << maskTemplateFile << ") was not found on the system"; getUserFeedback(i18n("Could not open gamut mask for editing."), i18n("The editor template was not found."), QMessageBox::Ok, QMessageBox::Ok, QMessageBox::Critical); return false; } m_dockerUI->maskPropertiesBox->setVisible(true); m_dockerUI->maskPropertiesBox->setEnabled(true); m_dockerUI->editControlsBox->setEnabled(false); m_dockerUI->editControlsBox->setVisible(false); m_dockerUI->maskTitleEdit->setText(m_selectedMask->title()); m_dockerUI->maskDescriptionEdit->setPlainText(m_selectedMask->description()); m_maskDocument = KisPart::instance()->createDocument(); KisPart::instance()->addDocument(m_maskDocument); m_maskDocument->openUrl(QUrl::fromLocalFile(maskTemplateFile), KisDocument::DontAddToRecent); // template document needs a proper autogenerated filename, // to avoid collision with other documents, // otherwise bugs happen when slotDocumentRemoved is called // (e.g. user closes another view, the template stays open, but the edit operation is canceled) m_maskDocument->setInfiniteAutoSaveInterval(); QString maskPath = QString("%1%2%3_%4.kra") .arg(QDir::tempPath()) .arg(QDir::separator()) .arg("GamutMaskTemplate") .arg(std::time(nullptr)); m_maskDocument->setUrl(QUrl::fromLocalFile(maskPath)); m_maskDocument->setLocalFilePath(maskPath); KisShapeLayerSP shapeLayer = getShapeLayer(); // pass only copies of shapes to the layer, // so the originals don't disappear from the mask later for (KoShape *shape: m_selectedMask->koShapes()) { KoShape* newShape = shape->cloneShape(); newShape->setStroke(KoShapeStrokeModelSP()); newShape->setBackground(QSharedPointer(new KoColorBackground(QColor(255,255,255)))); shapeLayer->addShape(newShape); } m_maskDocument->setPreActivatedNode(shapeLayer); // set document as active KisMainWindow* mainWindow = KisPart::instance()->currentMainwindow(); KIS_ASSERT(mainWindow); m_view = mainWindow->addViewAndNotifyLoadingCompleted(m_maskDocument); KIS_ASSERT(m_view); for(KisView *view: KisPart::instance()->views()) { if (view->document() == m_maskDocument) { view->activateWindow(); break; } } connect(m_view->viewManager(), SIGNAL(viewChanged()), this, SLOT(slotViewChanged())); connect(m_maskDocument, SIGNAL(completed()), this, SLOT(slotDocumentSaved())); return true; } void GamutMaskDock::cancelMaskEdit() { if (m_creatingNewMask) { deleteMask(); } if (m_selectedMask) { m_selectedMask->clearPreview(); if (m_resourceProvider->currentGamutMask() == m_selectedMask) { emit sigGamutMaskChanged(m_selectedMask); } } closeMaskDocument(); } void GamutMaskDock::selectMask(KoGamutMaskSP mask, bool notifyItemChooser) { if (!mask) { return; } m_selectedMask = mask; if (notifyItemChooser) { m_selfSelectingMask = true; m_dockerUI->maskChooser->setCurrentResource(m_selectedMask); m_selfSelectingMask = false; } emit sigGamutMaskSet(m_selectedMask); } bool GamutMaskDock::saveSelectedMaskResource() { if (!m_selectedMask || !m_maskDocument) { return false; } bool maskSaved = false; if (m_selectedMask) { QList shapes = getShapesFromLayer(); if (shapes.count() > 0) { m_selectedMask->setMaskShapes(shapes); m_selectedMask->setImage( m_maskDocument->image()->convertToQImage(m_maskDocument->image()->bounds() , m_maskDocument->image()->profile() ) ); m_selectedMask->setDescription(m_dockerUI->maskDescriptionEdit->toPlainText()); m_selectedMask->clearPreview(); m_selectedMask->save(); maskSaved = true; } else { getUserFeedback(i18n("Saving of gamut mask '%1' was aborted.", m_selectedMask->title()), i18n("

The mask template is invalid.

" "

Please check that:" "

    " "
  • your template contains a vector layer named 'maskShapesLayer'
  • " "
  • there are one or more vector shapes on the 'maskShapesLayer'
  • " "

" ), QMessageBox::Ok, QMessageBox::Ok); } } return maskSaved; } void GamutMaskDock::deleteMask() { KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); - rServer->removeResourceAndBlacklist(m_selectedMask); + rServer->removeResourceFromServer(m_selectedMask); m_selectedMask = nullptr; } int GamutMaskDock::getUserFeedback(QString text, QString informativeText, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton, QMessageBox::Icon severity) { QMessageBox msgBox; msgBox.setWindowTitle(i18nc("@title:window", "Krita")); msgBox.setText(QString("

%1

").arg(text)); msgBox.setInformativeText(informativeText); msgBox.setStandardButtons(buttons); msgBox.setDefaultButton(defaultButton); msgBox.setIcon(severity); int res = msgBox.exec(); return res; } int GamutMaskDock::saveOrCancel(QMessageBox::StandardButton defaultAction) { int response = 0; if (m_maskDocument->isModified()) { response = getUserFeedback(i18n("Gamut mask '%1' has been modified.", m_selectedMask->title()), i18n("Do you want to save it?"), QMessageBox::Cancel | QMessageBox::Close | QMessageBox::Save, defaultAction); } else if (m_templatePrevSaved && defaultAction != QMessageBox::Close) { response = QMessageBox::Save; } else if (!m_templatePrevSaved) { response = QMessageBox::Close; } else { response = defaultAction; } switch (response) { case QMessageBox::Save : { slotGamutMaskSave(); break; } case QMessageBox::Close : { cancelMaskEdit(); break; } } return response; } KoGamutMaskSP GamutMaskDock::createMaskResource(KoGamutMaskSP sourceMask, QString newTitle) { m_creatingNewMask = true; KoGamutMaskSP newMask; if (sourceMask) { newMask = KoGamutMaskSP(new KoGamutMask(sourceMask.data())); newMask->setImage(sourceMask->image()); } else { newMask = KoGamutMaskSP(new KoGamutMask()); QString defaultPreviewPath = KoResourcePaths::findResource(ResourceType::GamutMasks, "empty_mask_preview.png"); KIS_SAFE_ASSERT_RECOVER_NOOP(!(defaultPreviewPath.isEmpty() || defaultPreviewPath.isNull() || !QFile::exists(defaultPreviewPath))); newMask->setImage(QImage(defaultPreviewPath, "PNG")); } QPair maskFile = resolveMaskTitle(newTitle); QString maskTitle = maskFile.first; QFileInfo fileInfo = maskFile.second; newMask->setTitle(maskTitle); newMask->setFilename(fileInfo.filePath()); newMask->setValid(true); KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); //rServer->removeFromBlacklist(newMask); rServer->addResource(newMask, false); return newMask; } QPair GamutMaskDock::resolveMaskTitle(QString suggestedTitle) { KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); QString saveLocation = rServer->saveLocation(); QString processedTitle = suggestedTitle.trimmed(); QString resourceName = processedTitle; while (rServer->resourceByName(resourceName)) { resourceName = resourceName + QString(" (Copy)"); } QString maskTitle = resourceName; QString maskFile = maskTitle + ".kgm"; QString path = saveLocation + maskFile.replace(QRegularExpression("\\s+"), "_"); QFileInfo fileInfo(path); return QPair(maskTitle, fileInfo); } void GamutMaskDock::closeMaskDocument() { if (!m_externalTemplateClose) { if (m_maskDocument) { // set the document to not modified to bypass confirmation dialog // the close is already confirmed m_maskDocument->setModified(false); m_maskDocument->closeUrl(); m_view->closeView(); m_view->deleteLater(); // set a flag that we are doing it ourselves, so the docker does not react to // removing signal from KisPart m_selfClosingTemplate = true; KisPart::instance()->removeView(m_view); KisPart::instance()->removeDocument(m_maskDocument); m_selfClosingTemplate = false; } } m_dockerUI->maskPropertiesBox->setVisible(false); m_dockerUI->editControlsBox->setVisible(true); m_dockerUI->editControlsBox->setEnabled(true); disconnect(m_view->viewManager(), SIGNAL(viewChanged()), this, SLOT(slotViewChanged())); disconnect(m_maskDocument, SIGNAL(completed()), this, SLOT(slotDocumentSaved())); // the template file is meant as temporary, if the user saved it, delete now if (QFile::exists(m_maskDocument->localFilePath())) { QFile::remove(m_maskDocument->localFilePath()); } m_maskDocument = nullptr; m_view = nullptr; m_creatingNewMask = false; m_templatePrevSaved = false; } QList GamutMaskDock::getShapesFromLayer() { KisShapeLayerSP shapeLayer = getShapeLayer(); // create a deep copy of the shapes to save in the mask, // otherwise they vanish when the template closes QList newShapes; if (shapeLayer) { for (KoShape* sh: shapeLayer->shapes()) { KoShape* newShape = sh->cloneShape(); KoShapeStrokeSP border(new KoShapeStroke(0.5f, Qt::white)); newShape->setStroke(border); newShape->setBackground(QSharedPointer(new KoColorBackground(QColor(255,255,255,0)))); newShapes.append(newShape); } } return newShapes; } KisShapeLayerSP GamutMaskDock::getShapeLayer() { KisNodeSP node = m_maskDocument->image()->rootLayer()->findChildByName("maskShapesLayer"); return KisShapeLayerSP(dynamic_cast(node.data())); } void GamutMaskDock::slotGamutMaskSave() { if (!m_selectedMask || !m_maskDocument) { return; } QString newTitle = m_dockerUI->maskTitleEdit->text(); if (m_selectedMask->title() != newTitle) { // title has changed, rename KoGamutMaskSP newMask = createMaskResource(m_selectedMask, newTitle); // delete old mask and select new deleteMask(); selectMask(newMask); } bool maskSaved = saveSelectedMaskResource(); if (maskSaved) { emit sigGamutMaskSet(m_selectedMask); closeMaskDocument(); } } void GamutMaskDock::slotGamutMaskCancelEdit() { if (!m_selectedMask) { return; } saveOrCancel(QMessageBox::Close); } void GamutMaskDock::slotGamutMaskPreview() { if (!m_selectedMask) { return; } m_selectedMask->setPreviewMaskShapes(getShapesFromLayer()); emit sigGamutMaskPreviewUpdate(); } void GamutMaskDock::slotGamutMaskSelected(KoGamutMaskSP mask) { if (!m_selfSelectingMask) { if (m_maskDocument) { int res = saveOrCancel(); if (res == QMessageBox::Cancel) { return; } } selectMask(mask, false); } } void GamutMaskDock::setCanvas(KoCanvasBase *canvas) { setEnabled(canvas != 0); } void GamutMaskDock::unsetCanvas() { setEnabled(false); } void GamutMaskDock::unsetResourceServer() { KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer(); rServer->removeObserver(this); } void GamutMaskDock::removingResource(KoGamutMaskSP resource) { // if deleting previously set mask, notify selectors to unset their mask if (resource == m_resourceProvider->currentGamutMask()) { emit sigGamutMaskUnset(); m_selectedMask = nullptr; } } void GamutMaskDock::resourceChanged(KoGamutMaskSP resource) { // if currently set mask has been changed, notify selectors if (resource == m_resourceProvider->currentGamutMask()) { selectMask(resource); } } void GamutMaskDock::slotGamutMaskCreateNew() { KoGamutMaskSP newMask = createMaskResource(nullptr, "new mask"); selectMask(newMask); bool editorOpened = openMaskEditor(); if (!editorOpened) { deleteMask(); } } void GamutMaskDock::slotGamutMaskDuplicate() { if (!m_selectedMask) { return; } KoGamutMaskSP newMask = createMaskResource(m_selectedMask, m_selectedMask->title()); selectMask(newMask); bool editorOpened = openMaskEditor(); if (!editorOpened) { deleteMask(); } } void GamutMaskDock::slotGamutMaskDelete() { if (!m_selectedMask) { return; } int res = getUserFeedback(i18n("Are you sure you want to delete mask '%1'?" , m_selectedMask->title())); if (res == QMessageBox::Yes) { deleteMask(); } } void GamutMaskDock::slotDocumentRemoved(QString filename) { if (!m_maskDocument) { return; } m_externalTemplateClose = true; // we do not want to run this if it is we who close the file if (!m_selfClosingTemplate) { // KisPart called, that a document will be removed // if it's ours, cancel the mask edit operation if (m_maskDocument->url().toLocalFile() == filename) { m_maskDocument->waitForSavingToComplete(); saveOrCancel(); } } m_externalTemplateClose = false; } void GamutMaskDock::slotViewChanged() { if (!m_maskDocument || !m_view) { return; } if (m_view->viewManager()->document() == m_maskDocument) { m_dockerUI->maskPropertiesBox->setEnabled(true); } else { m_dockerUI->maskPropertiesBox->setEnabled(false); } } void GamutMaskDock::slotDocumentSaved() { m_templatePrevSaved = true; } diff --git a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectEditWidget.cpp b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectEditWidget.cpp index 0b81a9c74e..e1143aaadd 100644 --- a/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectEditWidget.cpp +++ b/plugins/tools/karbonplugins/tools/filterEffectTool/FilterEffectEditWidget.cpp @@ -1,548 +1,550 @@ /* This file is part of the KDE project * Copyright (c) 2009 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "FilterEffectEditWidget.h" #include "FilterEffectResource.h" #include "FilterResourceServerProvider.h" #include "FilterInputChangeCommand.h" #include "FilterAddCommand.h" #include "FilterRemoveCommand.h" #include "FilterStackSetCommand.h" #include #include #include #include #include #include #include #include #include #include #include #include #include FilterEffectEditWidget::FilterEffectEditWidget(QWidget *parent) : QWidget(parent) , m_scene(new FilterEffectScene(this)) , m_shape(0) , m_canvas(0) , m_effects(0) { setupUi(this); presets->setDisplayMode(KoResourceSelector::TextMode); presets->setColumnCount(1); connect(presets, SIGNAL(resourceSelected(KoResourceSP )), this, SLOT(presetSelected(KoResourceSP ))); connect(presets, SIGNAL(resourceApplied(KoResourceSP )), this, SLOT(presetSelected(KoResourceSP ))); KoGenericRegistryModel *filterEffectModel = new KoGenericRegistryModel(KoFilterEffectRegistry::instance()); effectSelector->setModel(filterEffectModel); removeEffect->setIcon(koIcon("list-remove")); connect(removeEffect, SIGNAL(clicked()), this, SLOT(removeSelectedItem())); addEffect->setIcon(koIcon("list-add")); addEffect->setToolTip(i18n("Add effect to current filter stack")); connect(addEffect, SIGNAL(clicked()), this, SLOT(addSelectedEffect())); // TODO: make these buttons do something useful raiseEffect->setIcon(koIcon("arrow-up")); raiseEffect->hide(); lowerEffect->setIcon(koIcon("arrow-down")); lowerEffect->hide(); addPreset->setIcon(koIcon("list-add")); addPreset->setToolTip(i18n("Add to filter presets")); connect(addPreset, SIGNAL(clicked()), this, SLOT(addToPresets())); removePreset->setIcon(koIcon("list-remove")); removePreset->setToolTip(i18n("Remove filter preset")); connect(removePreset, SIGNAL(clicked()), this, SLOT(removeFromPresets())); view->setScene(m_scene); view->setRenderHint(QPainter::Antialiasing, true); view->setResizeAnchor(QGraphicsView::AnchorViewCenter); connect(m_scene, SIGNAL(connectionCreated(ConnectionSource,ConnectionTarget)), this, SLOT(connectionCreated(ConnectionSource,ConnectionTarget))); connect(m_scene, SIGNAL(selectionChanged()), this, SLOT(sceneSelectionChanged())); QSet inputs; inputs << ConnectionSource::SourceGraphic; inputs << ConnectionSource::SourceAlpha; inputs << ConnectionSource::BackgroundImage; inputs << ConnectionSource::BackgroundAlpha; inputs << ConnectionSource::FillPaint; inputs << ConnectionSource::StrokePaint; m_defaultSourceSelector = new KComboBox(this); Q_FOREACH (ConnectionSource::SourceType source, inputs) { m_defaultSourceSelector->addItem(ConnectionSource::typeToString(source)); } m_defaultSourceSelector->hide(); m_defaultSourceSelector->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); connect(m_defaultSourceSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(defaultSourceChanged(int))); } FilterEffectEditWidget::~FilterEffectEditWidget() { if (!m_shape) { delete m_effects; } } void FilterEffectEditWidget::editShape(KoShape *shape, KoCanvasBase *canvas) { if (!m_shape) { delete m_effects; m_effects = 0; } m_shape = shape; m_canvas = canvas; if (m_shape) { m_effects = m_shape->filterEffectStack(); } if (!m_effects) { m_effects = new KoFilterEffectStack(); } m_scene->initialize(m_effects); fitScene(); } void FilterEffectEditWidget::fitScene() { QRectF bbox = m_scene->itemsBoundingRect(); m_scene->setSceneRect(bbox); bbox.adjust(-25, -25, 25, 25); view->centerOn(bbox.center()); view->fitInView(bbox, Qt::KeepAspectRatio); } void FilterEffectEditWidget::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); fitScene(); } void FilterEffectEditWidget::showEvent(QShowEvent *event) { Q_UNUSED(event); fitScene(); } void FilterEffectEditWidget::addSelectedEffect() { KoFilterEffectRegistry *registry = KoFilterEffectRegistry::instance(); KoFilterEffectFactoryBase *factory = registry->values()[effectSelector->currentIndex()]; if (!factory) { return; } KoFilterEffect *effect = factory->createFilterEffect(); if (!effect) { return; } if (m_shape) { if (!m_shape->filterEffectStack()) { m_effects->appendFilterEffect(effect); m_canvas->addCommand(new FilterStackSetCommand(m_effects, m_shape)); } else { m_canvas->addCommand(new FilterAddCommand(effect, m_shape)); } } else { m_effects->appendFilterEffect(effect); } m_scene->initialize(m_effects); fitScene(); } void FilterEffectEditWidget::removeSelectedItem() { QList selectedItems = m_scene->selectedEffectItems(); if (!selectedItems.count()) { return; } QList changeData; QList filterEffects = m_effects->filterEffects(); int effectIndexToDelete = -1; const ConnectionSource &item = selectedItems.first(); KoFilterEffect *effect = item.effect(); if (item.type() == ConnectionSource::Effect) { int effectIndex = filterEffects.indexOf(effect); // adjust inputs of all following effects in the stack for (int i = effectIndex + 1; i < filterEffects.count(); ++i) { KoFilterEffect *nextEffect = filterEffects[i]; QList inputs = nextEffect->inputs(); int inputIndex = 0; Q_FOREACH (const QString &input, inputs) { if (input == effect->output()) { InputChangeData data(nextEffect, inputIndex, input, ""); changeData.append(data); } } // if one of the next effects has the same output name we stop if (nextEffect->output() == effect->output()) { break; } } effectIndexToDelete = effectIndex; } else { QString outputName = ConnectionSource::typeToString(item.type()); QList inputs = effect->inputs(); int inputIndex = 0; Q_FOREACH (const QString &input, inputs) { if (input == outputName) { InputChangeData data(effect, inputIndex, input, ""); changeData.append(data); } inputIndex++; } } KUndo2Command *cmd = new KUndo2Command(); if (changeData.count()) { KUndo2Command *subCmd = new FilterInputChangeCommand(changeData, m_shape, cmd); cmd->setText(subCmd->text()); } if (effectIndexToDelete >= 0) { KUndo2Command *subCmd = new FilterRemoveCommand(effectIndexToDelete, m_effects, m_shape, cmd); cmd->setText(subCmd->text()); } if (m_canvas && m_shape) { m_canvas->addCommand(cmd); } else { cmd->redo(); delete cmd; } m_scene->initialize(m_effects); fitScene(); } void FilterEffectEditWidget::connectionCreated(ConnectionSource source, ConnectionTarget target) { QList filterEffects = m_effects->filterEffects(); int targetEffectIndex = filterEffects.indexOf(target.effect()); if (targetEffectIndex < 0) { return; } QList changeData; QString sourceName; if (source.type() == ConnectionSource::Effect) { sourceName = source.effect()->output(); int sourceEffectIndex = filterEffects.indexOf(source.effect()); if (targetEffectIndex - sourceEffectIndex > 1) { // there are effects between source effect and target effect // so we have to take extra care bool renameOutput = false; if (sourceName.isEmpty()) { // output is not named so we have to rename the source output // and adjust the next effect in case it uses this output renameOutput = true; } else { // output is named but if there is an effect with the same // output name, we have to rename the source output for (int i = sourceEffectIndex + 1; i < targetEffectIndex; ++i) { KoFilterEffect *effect = filterEffects[i]; if (effect->output() == sourceName) { renameOutput = true; break; } } } if (renameOutput) { QSet uniqueOutputNames; Q_FOREACH (KoFilterEffect *effect, filterEffects) { uniqueOutputNames.insert(effect->output()); } int index = 0; QString newOutputName; do { newOutputName = QString("result%1").arg(index); } while (uniqueOutputNames.contains(newOutputName)); // rename source output source.effect()->setOutput(newOutputName); // adjust following effects for (int i = sourceEffectIndex + 1; i < targetEffectIndex; ++i) { KoFilterEffect *effect = filterEffects[i]; int inputIndex = 0; Q_FOREACH (const QString &input, effect->inputs()) { if (input.isEmpty() && (i == sourceEffectIndex + 1 || input == sourceName)) { InputChangeData data(effect, inputIndex, input, newOutputName); changeData.append(data); } inputIndex++; } if (sourceName.isEmpty() || effect->output() == sourceName) { break; } } sourceName = newOutputName; } } } else { // source is an predefined input image sourceName = ConnectionSource::typeToString(source.type()); } // finally set the input of the target if (target.inputIndex() >= target.effect()->inputs().count()) { // insert new input here target.effect()->addInput(sourceName); } else { QString oldInput = target.effect()->inputs()[target.inputIndex()]; InputChangeData data(target.effect(), target.inputIndex(), oldInput, sourceName); changeData.append(data); } if (changeData.count()) { KUndo2Command *cmd = new FilterInputChangeCommand(changeData, m_shape); if (m_canvas) { m_canvas->addCommand(cmd); } else { cmd->redo(); delete cmd; } } m_scene->initialize(m_effects); fitScene(); } void FilterEffectEditWidget::addToPresets() { if (!m_effects) { return; } bool ok = false; QString effectName = QInputDialog::getText(this, i18n("Effect name"), i18n("Please enter a name for the filter effect"), QLineEdit::Normal, QString(), &ok); if (!ok) { return; } QSharedPointer resource(FilterEffectResource::fromFilterEffectStack(m_effects)); if (!resource) { return; } resource->setName(effectName); FilterResourceServerProvider *serverProvider = FilterResourceServerProvider::instance(); KoResourceServer *server = serverProvider->filterEffectServer(); QString savePath = server->saveLocation(); int i = 1; QFileInfo fileInfo; do { fileInfo.setFile(savePath + QString("%1.svg").arg(i++, 4, 10, QChar('0'))); } while (fileInfo.exists()); resource->setFilename(fileInfo.filePath()); resource->setValid(true); } void FilterEffectEditWidget::removeFromPresets() { if (!presets->count()) { return; } + FilterResourceServerProvider *serverProvider = FilterResourceServerProvider::instance(); if (!serverProvider) { return; } + KoResourceServer *server = serverProvider->filterEffectServer(); if (!server) { return; } QSharedPointer resource = server->resources().at(presets->currentIndex()); if (!resource) { return; } - server->removeResourceAndBlacklist(resource); + server->removeResourceFromServer(resource); } void FilterEffectEditWidget::presetSelected(KoResourceSP resource) { QSharedPointer effectResource = resource.dynamicCast(); if (!effectResource) { return; } KoFilterEffectStack *filterStack = effectResource->toFilterStack(); if (!filterStack) { return; } if (m_shape) { KUndo2Command *cmd = new FilterStackSetCommand(filterStack, m_shape); if (m_canvas) { m_canvas->addCommand(cmd); } else { cmd->redo(); delete cmd; } } else { delete m_effects; } m_effects = filterStack; m_scene->initialize(m_effects); fitScene(); } void FilterEffectEditWidget::addWidgetForItem(ConnectionSource item) { // get the filter effect from the item KoFilterEffect *filterEffect = item.effect(); if (item.type() != ConnectionSource::Effect) { filterEffect = 0; } KoFilterEffect *currentEffect = m_currentItem.effect(); if (m_currentItem.type() != ConnectionSource::Effect) { currentEffect = 0; } m_defaultSourceSelector->hide(); // remove current widget if new effect is zero or effect type has changed if (!filterEffect || !currentEffect || (filterEffect->id() != currentEffect->id())) { while (configStack->count()) { configStack->removeWidget(configStack->widget(0)); } } m_currentItem = item; KoFilterEffectConfigWidgetBase *currentPanel = 0; if (!filterEffect) { if (item.type() != ConnectionSource::Effect) { configStack->insertWidget(0, m_defaultSourceSelector); m_defaultSourceSelector->blockSignals(true); m_defaultSourceSelector->setCurrentIndex(item.type() - 1); m_defaultSourceSelector->blockSignals(false); m_defaultSourceSelector->show(); } } else if (!currentEffect || currentEffect->id() != filterEffect->id()) { // when a shape is set and is differs from the previous one // get the config widget and insert it into the option widget KoFilterEffectRegistry *registry = KoFilterEffectRegistry::instance(); KoFilterEffectFactoryBase *factory = registry->value(filterEffect->id()); if (!factory) { return; } currentPanel = factory->createConfigWidget(); if (!currentPanel) { return; } configStack->insertWidget(0, currentPanel); connect(currentPanel, SIGNAL(filterChanged()), this, SLOT(filterChanged())); } currentPanel = qobject_cast(configStack->widget(0)); if (currentPanel) { currentPanel->editFilterEffect(filterEffect); } } void FilterEffectEditWidget::filterChanged() { if (m_shape) { m_shape->update(); } } void FilterEffectEditWidget::sceneSelectionChanged() { QList selectedItems = m_scene->selectedEffectItems(); if (!selectedItems.count()) { addWidgetForItem(ConnectionSource()); } else { addWidgetForItem(selectedItems.first()); } } void FilterEffectEditWidget::defaultSourceChanged(int index) { if (m_currentItem.type() == ConnectionSource::Effect) { return; } KoFilterEffect *filterEffect = m_currentItem.effect(); if (!filterEffect) { return; } QString oldInput = ConnectionSource::typeToString(m_currentItem.type()); QString newInput = m_defaultSourceSelector->itemText(index); const QString defInput = "SourceGraphic"; int effectIndex = m_effects->filterEffects().indexOf(filterEffect); InputChangeData data; int inputIndex = 0; Q_FOREACH (const QString &input, filterEffect->inputs()) { if (input == oldInput || (effectIndex == 0 && oldInput == defInput)) { data = InputChangeData(filterEffect, inputIndex, input, newInput); break; } inputIndex++; } KUndo2Command *cmd = new FilterInputChangeCommand(data, m_shape); if (m_canvas && m_shape) { m_canvas->addCommand(cmd); } else { cmd->redo(); delete cmd; } m_scene->initialize(m_effects); fitScene(); }