File indexing completed on 2025-12-07 04:19:08
0001 /* 0002 SPDX-FileCopyrightText: 2012 Dan Leinir Turthra Jensen <admin@leinir.dk> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 0008 #include "CompositeOpModel.h" 0009 #include <kis_composite_ops_model.h> 0010 #include <KisViewManager.h> 0011 #include <kis_canvas_resource_provider.h> 0012 #include <kis_tool.h> 0013 #include <kis_canvas2.h> 0014 #include <input/kis_input_manager.h> 0015 #include <kis_node_manager.h> 0016 #include <kis_node.h> 0017 #include <kis_layer.h> 0018 #include <brushengine/kis_paintop_preset.h> 0019 #include <brushengine/kis_paintop_settings.h> 0020 #include <brushengine/kis_paintop_registry.h> 0021 #include <brushengine/kis_paintop_config_widget.h> 0022 #include <KoCompositeOpRegistry.h> 0023 #include <KoColorSpace.h> 0024 #include <KoToolManager.h> 0025 #include <KisGlobalResourcesInterface.h> 0026 0027 class CompositeOpModel::Private 0028 { 0029 public: 0030 Private(CompositeOpModel* qq) 0031 : q(qq) 0032 , model(new KisCompositeOpListModel()) 0033 , view(0) 0034 , eraserMode(0) 0035 , opacity(0) 0036 , opacityEnabled(false) 0037 , flow(0) 0038 , flowEnabled(false) 0039 , size(0) 0040 , sizeEnabled(false) 0041 , presetsEnabled(true) 0042 {}; 0043 0044 CompositeOpModel* q; 0045 KisCompositeOpListModel* model; 0046 KisViewManager* view; 0047 QString currentCompositeOpID; 0048 QString prevCompositeOpID; 0049 bool eraserMode; 0050 QMap<KisPaintOpPreset*, KisPaintOpConfigWidget*> settingsWidgets; 0051 0052 qreal opacity; 0053 bool opacityEnabled; 0054 qreal flow; 0055 bool flowEnabled; 0056 qreal size; 0057 bool sizeEnabled; 0058 bool presetsEnabled; 0059 KisPaintOpPresetSP currentPreset; 0060 0061 void updateCompositeOp(QString compositeOpID) 0062 { 0063 if (!view) 0064 return; 0065 0066 KisNodeSP node = view->canvasResourceProvider()->currentNode(); 0067 0068 if (node && node->paintDevice()) 0069 { 0070 if (!node->paintDevice()->colorSpace()->hasCompositeOp(compositeOpID)) 0071 compositeOpID = KoCompositeOpRegistry::instance().getDefaultCompositeOp().id(); 0072 0073 if (compositeOpID != currentCompositeOpID) 0074 { 0075 q->setEraserMode(compositeOpID == COMPOSITE_ERASE); 0076 currentPreset->settings()->setProperty("CompositeOp", compositeOpID); 0077 //m_optionWidget->setConfiguration(m_activePreset->settings().data()); 0078 view->canvasResourceProvider()->setCurrentCompositeOp(compositeOpID); 0079 prevCompositeOpID = currentCompositeOpID; 0080 currentCompositeOpID = compositeOpID; 0081 } 0082 } 0083 emit q->currentCompositeOpIDChanged(); 0084 } 0085 0086 void ofsChanged() 0087 { 0088 if (presetsEnabled && !currentPreset.isNull() && !currentPreset->settings().isNull()) 0089 { 0090 // IMPORTANT: set the PaintOp size before setting the other properties 0091 // it wont work the other way 0092 //qreal sizeDiff = size - currentPreset->settings()->paintOpSize(); 0093 //currentPreset->settings()->changePaintOpSize(sizeDiff, 0); 0094 0095 if (currentPreset->settings()->hasProperty("OpacityValue")) 0096 currentPreset->settings()->setProperty("OpacityValue", opacity); 0097 0098 if (currentPreset->settings()->hasProperty("FlowValue")) 0099 currentPreset->settings()->setProperty("FlowValue", flow); 0100 0101 //m_optionWidget->setConfiguration(d->currentPreset->settings().data()); 0102 } 0103 if (view) 0104 { 0105 view->canvasResourceProvider()->setOpacity(opacity); 0106 } 0107 } 0108 }; 0109 0110 CompositeOpModel::CompositeOpModel(QObject* parent) 0111 : QAbstractListModel(parent) 0112 , d(new Private(this)) 0113 { 0114 connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*)), 0115 this, SLOT(slotToolChanged(KoCanvasController*))); 0116 0117 } 0118 0119 CompositeOpModel::~CompositeOpModel() 0120 { 0121 delete d; 0122 } 0123 0124 QHash<int, QByteArray> CompositeOpModel::roleNames() const 0125 { 0126 QHash<int,QByteArray> roles; 0127 roles[TextRole] = "text"; 0128 roles[IsCategoryRole] = "isCategory"; 0129 return roles; 0130 } 0131 0132 QVariant CompositeOpModel::data(const QModelIndex& index, int role) const 0133 { 0134 QVariant data; 0135 if (index.isValid()) 0136 { 0137 QModelIndex otherIndex = d->model->index(index.row(), index.column(), QModelIndex()); 0138 switch(role) 0139 { 0140 case TextRole: 0141 data = d->model->data(otherIndex, Qt::DisplayRole); 0142 break; 0143 case IsCategoryRole: 0144 data = d->model->data(otherIndex, __CategorizedListModelBase::IsHeaderRole); 0145 break; 0146 default: 0147 break; 0148 } 0149 } 0150 return data; 0151 } 0152 0153 int CompositeOpModel::rowCount(const QModelIndex& parent) const 0154 { 0155 if (parent.isValid()) 0156 return 0; 0157 return d->model->rowCount(QModelIndex()); 0158 } 0159 0160 void CompositeOpModel::activateItem(int index) 0161 { 0162 if (index > -1 && index < d->model->rowCount(QModelIndex())) 0163 { 0164 KoID compositeOp; 0165 if (d->model->entryAt(compositeOp, d->model->index(index))) 0166 d->updateCompositeOp(compositeOp.id()); 0167 } 0168 } 0169 0170 QObject* CompositeOpModel::view() const 0171 { 0172 return d->view; 0173 } 0174 0175 void CompositeOpModel::setView(QObject* newView) 0176 { 0177 if (d->view) 0178 { 0179 d->view->canvasBase()->disconnect(this); 0180 d->view->canvasBase()->globalInputManager()->disconnect(this); 0181 d->view->nodeManager()->disconnect(this); 0182 } 0183 d->view = qobject_cast<KisViewManager*>( newView ); 0184 if (d->view) 0185 { 0186 if (d->view->canvasBase() && d->view->canvasBase()->resourceManager()) { 0187 connect(d->view->canvasBase()->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), 0188 this, SLOT(resourceChanged(int,QVariant))); 0189 } 0190 // if (d->view->nodeManager()) { 0191 // connect(d->view->nodeManager(), SIGNAL(sigLayerActivated(KisLayerSP)), 0192 // this, SLOT(currentNodeChanged(KisLayerSP))); 0193 // } 0194 slotToolChanged(0); 0195 } 0196 emit viewChanged(); 0197 } 0198 0199 bool CompositeOpModel::eraserMode() const 0200 { 0201 return d->eraserMode; 0202 } 0203 0204 void CompositeOpModel::setEraserMode(bool newEraserMode) 0205 { 0206 if (d->eraserMode != newEraserMode) 0207 { 0208 d->eraserMode = newEraserMode; 0209 if (d->eraserMode) 0210 d->updateCompositeOp(COMPOSITE_ERASE); 0211 else 0212 d->updateCompositeOp(d->prevCompositeOpID); 0213 emit eraserModeChanged(); 0214 } 0215 } 0216 0217 qreal CompositeOpModel::flow() const 0218 { 0219 return d->flow; 0220 } 0221 0222 void CompositeOpModel::setFlow(qreal newFlow) 0223 { 0224 if (d->flow != newFlow) 0225 { 0226 d->flow = newFlow; 0227 d->ofsChanged(); 0228 emit flowChanged(); 0229 } 0230 } 0231 0232 bool CompositeOpModel::flowEnabled() const 0233 { 0234 return d->flowEnabled; 0235 } 0236 0237 void CompositeOpModel::setFlowEnabled(bool newFlowEnabled) 0238 { 0239 d->flowEnabled = newFlowEnabled; 0240 emit flowEnabledChanged(); 0241 } 0242 0243 qreal CompositeOpModel::opacity() const 0244 { 0245 return d->opacity; 0246 } 0247 0248 void CompositeOpModel::setOpacity(qreal newOpacity) 0249 { 0250 if (d->opacity != newOpacity) 0251 { 0252 d->opacity = newOpacity; 0253 d->ofsChanged(); 0254 emit opacityChanged(); 0255 } 0256 } 0257 0258 bool CompositeOpModel::opacityEnabled() const 0259 { 0260 return d->opacityEnabled; 0261 } 0262 0263 void CompositeOpModel::setOpacityEnabled(bool newOpacityEnabled) 0264 { 0265 d->opacityEnabled = newOpacityEnabled; 0266 emit opacityEnabledChanged(); 0267 } 0268 0269 qreal CompositeOpModel::size() const 0270 { 0271 return d->size; 0272 } 0273 0274 void CompositeOpModel::setSize(qreal newSize) 0275 { 0276 if (d->size != newSize) 0277 { 0278 d->size = newSize; 0279 d->ofsChanged(); 0280 emit sizeChanged(); 0281 } 0282 } 0283 0284 bool CompositeOpModel::sizeEnabled() const 0285 { 0286 return d->sizeEnabled; 0287 } 0288 0289 void CompositeOpModel::setSizeEnabled(bool newSizeEnabled) 0290 { 0291 d->sizeEnabled = newSizeEnabled; 0292 emit sizeEnabledChanged(); 0293 } 0294 0295 void CompositeOpModel::changePaintopValue(QString propertyName, QVariant value) 0296 { 0297 if (propertyName == "size" && value.toReal() != d->size) 0298 setSize(value.toReal()); 0299 else if (propertyName == "opacity" && value.toReal() != d->opacity) 0300 setOpacity(value.toReal()); 0301 else if (propertyName == "flow" && value.toReal() != d->flow) 0302 setFlow(value.toReal()); 0303 } 0304 0305 bool CompositeOpModel::mirrorHorizontally() const 0306 { 0307 if (d->view) 0308 return d->view->canvasResourceProvider()->mirrorHorizontal(); 0309 return false; 0310 } 0311 0312 void CompositeOpModel::setMirrorHorizontally(bool newMirrorHorizontally) 0313 { 0314 if (d->view && d->view->canvasResourceProvider()->mirrorHorizontal() != newMirrorHorizontally) 0315 { 0316 d->view->canvasResourceProvider()->setMirrorHorizontal(newMirrorHorizontally); 0317 emit mirrorHorizontallyChanged(); 0318 } 0319 } 0320 0321 bool CompositeOpModel::mirrorVertically() const 0322 { 0323 if (d->view) 0324 return d->view->canvasResourceProvider()->mirrorVertical(); 0325 return false; 0326 } 0327 0328 void CompositeOpModel::setMirrorVertically(bool newMirrorVertically) 0329 { 0330 if (d->view && d->view->canvasResourceProvider()->mirrorVertical() != newMirrorVertically) 0331 { 0332 d->view->canvasResourceProvider()->setMirrorVertical(newMirrorVertically); 0333 emit mirrorVerticallyChanged(); 0334 } 0335 } 0336 0337 void CompositeOpModel::slotToolChanged(KoCanvasController* canvas) 0338 { 0339 Q_UNUSED(canvas); 0340 0341 if (!d->view) return; 0342 if (!d->view->canvasBase()) return; 0343 0344 QString id = KoToolManager::instance()->activeToolId(); 0345 KisTool* tool = dynamic_cast<KisTool*>(KoToolManager::instance()->toolById(d->view->canvasBase(), id)); 0346 0347 if (tool) { 0348 int flags = tool->flags(); 0349 0350 if (flags & KisTool::FLAG_USES_CUSTOM_COMPOSITEOP) { 0351 //setWidgetState(ENABLE_COMPOSITEOP|ENABLE_OPACITY); 0352 d->opacityEnabled = true; 0353 } 0354 else { 0355 //setWidgetState(DISABLE_COMPOSITEOP|DISABLE_OPACITY); 0356 d->opacityEnabled = false; 0357 } 0358 0359 if (flags & KisTool::FLAG_USES_CUSTOM_PRESET) { 0360 d->flowEnabled = true; 0361 d->sizeEnabled = true; 0362 d->presetsEnabled = true; 0363 } 0364 else { 0365 d->flowEnabled = false; 0366 d->sizeEnabled = false; 0367 d->presetsEnabled = false; 0368 } 0369 } 0370 else { 0371 d->opacityEnabled = false; 0372 d->flowEnabled = false; 0373 d->sizeEnabled = false; 0374 } 0375 emit opacityEnabledChanged(); 0376 emit flowEnabledChanged(); 0377 emit sizeEnabledChanged(); 0378 } 0379 0380 void CompositeOpModel::resourceChanged(int key, const QVariant& /*v*/) 0381 { 0382 if (d->view && d->view->canvasBase() && d->view->canvasBase()->resourceManager() && d->view->canvasResourceProvider()) { 0383 0384 if (key == KoCanvasResource::MirrorHorizontal) { 0385 emit mirrorHorizontallyChanged(); 0386 return; 0387 } 0388 else if(key == KoCanvasResource::MirrorVertical) { 0389 emit mirrorVerticallyChanged(); 0390 return; 0391 } 0392 0393 KisPaintOpPresetSP preset = d->view->canvasBase()->resourceManager()->resource(KoCanvasResource::CurrentPaintOpPreset).value<KisPaintOpPresetSP>(); 0394 0395 if (preset && d->currentPreset.data() != preset.data()) { 0396 d->currentPreset = preset; 0397 if (!d->settingsWidgets.contains(preset.data())) { 0398 d->settingsWidgets[preset.data()] = KisPaintOpRegistry::instance()->get(preset->paintOp().id())->createConfigWidget(0, 0399 KisGlobalResourcesInterface::instance(), 0400 d->view->canvasResourceProvider()->resourceManager()->canvasResourcesInterface()); 0401 d->settingsWidgets[preset.data()]->setImage(d->view->image()); 0402 d->settingsWidgets[preset.data()]->setConfiguration(preset->settings()); 0403 } 0404 0405 d->size = preset->settings()->paintOpSize(); 0406 emit sizeChanged(); 0407 0408 if (preset->settings()->hasProperty("OpacityValue")) { 0409 d->opacityEnabled = true; 0410 d->opacity = preset->settings()->getProperty("OpacityValue").toReal(); 0411 } 0412 else { 0413 d->opacityEnabled = false; 0414 d->opacity = 1; 0415 } 0416 0417 d->view->canvasResourceProvider()->setOpacity(d->opacity); 0418 emit opacityChanged(); 0419 emit opacityEnabledChanged(); 0420 0421 if (preset->settings()->hasProperty("FlowValue")) { 0422 d->flowEnabled = true; 0423 d->flow = preset->settings()->getProperty("FlowValue").toReal(); 0424 } 0425 else { 0426 d->flowEnabled = false; 0427 d->flow = 1; 0428 } 0429 emit flowChanged(); 0430 emit flowEnabledChanged(); 0431 0432 QString compositeOp = preset->settings()->getString("CompositeOp"); 0433 0434 // This is a little odd, but the logic here is that the opposite of an eraser is a normal composite op (so we just select over, aka normal) 0435 // This means that you can switch your eraser over to being a painting tool by turning off the eraser again. 0436 if (compositeOp == COMPOSITE_ERASE) { 0437 d->currentCompositeOpID = COMPOSITE_OVER; 0438 d->eraserMode = true; 0439 } 0440 else { 0441 d->eraserMode = false; 0442 } 0443 emit eraserModeChanged(); 0444 d->updateCompositeOp(compositeOp); 0445 } 0446 } 0447 } 0448 0449 void CompositeOpModel::currentNodeChanged(KisLayerSP newNode) 0450 { 0451 Q_UNUSED(newNode); 0452 if (d->eraserMode) { 0453 d->eraserMode = false; 0454 d->updateCompositeOp(d->prevCompositeOpID); 0455 emit eraserModeChanged(); 0456 } 0457 } 0458 0459 int CompositeOpModel::indexOf(QString compositeOpId) 0460 { 0461 return d->model->indexOf(KoID(compositeOpId)).row(); 0462 } 0463 0464 QString CompositeOpModel::currentCompositeOpID() const 0465 { 0466 return d->currentCompositeOpID; 0467 } 0468