File indexing completed on 2024-12-22 04:14:50
0001 /* 0002 * LayerBox.cc - part of Krita aka Krayon aka KimageShop 0003 * 0004 * SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org> 0005 * SPDX-FileCopyrightText: 2006 Gábor Lehel <illissius@gmail.com> 0006 * SPDX-FileCopyrightText: 2007 Thomas Zander <zander@kde.org> 0007 * SPDX-FileCopyrightText: 2007 Boudewijn Rempt <boud@valdyas.org> 0008 * SPDX-FileCopyrightText: 2011 José Luis Vergara <pentalis@gmail.com> 0009 * 0010 * SPDX-License-Identifier: GPL-2.0-or-later 0011 */ 0012 0013 #include "LayerBox.h" 0014 0015 #include <QApplication> 0016 #include <QToolButton> 0017 #include <QLayout> 0018 #include <QMouseEvent> 0019 #include <QPainter> 0020 #include <QPoint> 0021 #include <QRect> 0022 #include <QString> 0023 #include <QToolTip> 0024 #include <QWidget> 0025 #include <QComboBox> 0026 #include <QCheckBox> 0027 #include <QVBoxLayout> 0028 #include <QPixmap> 0029 #include <QBitmap> 0030 #include <QList> 0031 #include <QVector> 0032 #include <QLabel> 0033 #include <QMenu> 0034 #include <QWidgetAction> 0035 #include <QProxyStyle> 0036 #include <QStyleFactory> 0037 0038 #include <kis_debug.h> 0039 #include <klocalizedstring.h> 0040 0041 #include <kis_icon.h> 0042 #include <KoColorSpace.h> 0043 #include <KoCompositeOpRegistry.h> 0044 #include <KisDocument.h> 0045 #include <kis_time_span.h> 0046 0047 #include <kis_types.h> 0048 #include <kis_image.h> 0049 #include <kis_paint_device.h> 0050 #include <kis_layer.h> 0051 #include <kis_group_layer.h> 0052 #include <kis_mask.h> 0053 #include <kis_node.h> 0054 #include <kis_base_node.h> 0055 #include <kis_composite_ops_model.h> 0056 #include <kis_keyframe_channel.h> 0057 #include <kis_image_animation_interface.h> 0058 #include <KoProperties.h> 0059 0060 #include <kis_action.h> 0061 #include "kis_action_manager.h" 0062 #include "widgets/kis_cmb_composite.h" 0063 #include "kis_slider_spin_box.h" 0064 #include "KisViewManager.h" 0065 #include "kis_node_manager.h" 0066 #include "kis_node_model.h" 0067 #include <kis_clipboard.h> 0068 0069 #include "canvas/kis_canvas2.h" 0070 #include "kis_dummies_facade_base.h" 0071 #include "kis_shape_controller.h" 0072 #include "kis_selection_mask.h" 0073 #include "kis_config.h" 0074 #include "KisView.h" 0075 #include "krita_utils.h" 0076 #include "kis_color_label_selector_widget.h" 0077 #include "kis_signals_blocker.h" 0078 #include "kis_color_filter_combo.h" 0079 #include "kis_node_filter_proxy_model.h" 0080 #include <KisSpinBoxI18nHelper.h> 0081 0082 #include "kis_selection.h" 0083 #include "kis_processing_applicator.h" 0084 #include "commands/kis_set_global_selection_command.h" 0085 #include "KisSelectionActionsAdapter.h" 0086 #include "KisIdleTasksManager.h" 0087 0088 #include "kis_layer_utils.h" 0089 0090 #include "ui_WdgLayerBox.h" 0091 #include "NodeView.h" 0092 #include "SyncButtonAndAction.h" 0093 #include "KisMenuStyleDontCloseOnAlt.h" 0094 0095 class LayerBoxStyle : public QProxyStyle 0096 { 0097 public: 0098 LayerBoxStyle(QStyle *baseStyle = 0) : QProxyStyle(baseStyle) {} 0099 0100 void drawPrimitive(PrimitiveElement element, 0101 const QStyleOption *option, 0102 QPainter *painter, 0103 const QWidget *widget) const override 0104 { 0105 if (element == QStyle::PE_IndicatorItemViewItemDrop) 0106 { 0107 QColor color(widget->palette().color(QPalette::Highlight).lighter()); 0108 0109 if (option->rect.height() == 0) { 0110 QBrush brush(color); 0111 0112 QRect r(option->rect); 0113 r.setTop(r.top() - 2); 0114 r.setBottom(r.bottom() + 2); 0115 0116 painter->fillRect(r, brush); 0117 } else { 0118 color.setAlpha(200); 0119 QBrush brush(color); 0120 painter->fillRect(option->rect, brush); 0121 } 0122 } 0123 else 0124 { 0125 QProxyStyle::drawPrimitive(element, option, painter, widget); 0126 } 0127 } 0128 }; 0129 0130 inline void LayerBox::connectActionToButton(KisViewManager* viewManager, QAbstractButton *button, const QString &id) 0131 { 0132 if (!viewManager || !button) return; 0133 0134 KisAction *action = viewManager->actionManager()->actionByName(id); 0135 0136 if (!action) return; 0137 0138 connect(button, SIGNAL(clicked()), action, SLOT(trigger())); 0139 connect(action, SIGNAL(sigEnableSlaves(bool)), button, SLOT(setEnabled(bool))); 0140 connect(viewManager->mainWindowAsQWidget(), SIGNAL(themeChanged()), this, SLOT(slotUpdateIcons())); 0141 } 0142 0143 inline void LayerBox::addActionToMenu(QMenu *menu, const QString &id) 0144 { 0145 if (m_canvas) { 0146 menu->addAction(m_canvas->viewManager()->actionManager()->actionByName(id)); 0147 } 0148 } 0149 0150 qint32 LayerBox::convertOpacityToInt(qreal opacity) 0151 { 0152 /** 0153 * Scales opacity from the range 0...100 0154 * to the integer range 0...255 0155 */ 0156 0157 return qMin(255, int(opacity * 2.55 + 0.5)); 0158 } 0159 0160 LayerBox::LayerBox() 0161 : QDockWidget(i18n("Layers")) 0162 , m_canvas(0) 0163 , m_wdgLayerBox(new Ui_WdgLayerBox) 0164 , m_colorLabelCompressor(500, KisSignalCompressor::FIRST_INACTIVE) 0165 , m_thumbnailSizeCompressor(100, KisSignalCompressor::FIRST_INACTIVE) 0166 , m_treeIndentationCompressor(100, KisSignalCompressor::FIRST_INACTIVE) 0167 , m_infoTextOpacityCompressor(100, KisSignalCompressor::FIRST_INACTIVE) 0168 { 0169 KisConfig cfg(false); 0170 0171 QWidget* mainWidget = new QWidget(this); 0172 setWidget(mainWidget); 0173 m_opacityDelayTimer.setSingleShot(true); 0174 0175 m_wdgLayerBox->setupUi(mainWidget); 0176 0177 QStyle *newStyle = QStyleFactory::create(m_wdgLayerBox->listLayers->style()->objectName()); 0178 // proxy style steals the ownership of the style and deletes it later 0179 LayerBoxStyle *proxyStyle = new LayerBoxStyle(newStyle); 0180 0181 proxyStyle->setParent(m_wdgLayerBox->listLayers); 0182 m_wdgLayerBox->listLayers->setStyle(proxyStyle); 0183 0184 connect(m_wdgLayerBox->listLayers, 0185 SIGNAL(contextMenuRequested(QPoint,QModelIndex)), 0186 this, SLOT(slotContextMenuRequested(QPoint,QModelIndex))); 0187 connect(m_wdgLayerBox->listLayers, 0188 SIGNAL(collapsed(QModelIndex)), SLOT(slotCollapsed(QModelIndex))); 0189 connect(m_wdgLayerBox->listLayers, 0190 SIGNAL(expanded(QModelIndex)), SLOT(slotExpanded(QModelIndex))); 0191 connect(m_wdgLayerBox->listLayers, 0192 SIGNAL(selectionChanged(QModelIndexList)), SLOT(selectionChanged(QModelIndexList))); 0193 0194 slotUpdateIcons(); 0195 0196 m_wdgLayerBox->bnAdd->setIconSize(QSize(22, 22)); 0197 m_wdgLayerBox->bnDelete->setIconSize(QSize(22, 22)); 0198 m_wdgLayerBox->bnRaise->setIconSize(QSize(22, 22)); 0199 m_wdgLayerBox->bnLower->setIconSize(QSize(22, 22)); 0200 m_wdgLayerBox->bnProperties->setIconSize(QSize(22, 22)); 0201 m_wdgLayerBox->bnDuplicate->setIconSize(QSize(22, 22)); 0202 0203 m_wdgLayerBox->bnLower->setEnabled(false); 0204 m_wdgLayerBox->bnRaise->setEnabled(false); 0205 0206 m_wdgLayerBox->doubleOpacity->setRange(0, 100, 0); 0207 if (cfg.sliderLabels()) { 0208 m_wdgLayerBox->opacityLabel->hide(); 0209 KisSpinBoxI18nHelper::setText(m_wdgLayerBox->doubleOpacity, 0210 i18nc("{n} is the number value, % is the percent sign", "Opacity: {n}%")); 0211 } else { 0212 KisSpinBoxI18nHelper::setText(m_wdgLayerBox->doubleOpacity, 0213 i18nc("{n} is the number value, % is the percent sign", "{n}%")); 0214 } 0215 0216 connect(m_wdgLayerBox->doubleOpacity, SIGNAL(valueChanged(qreal)), SLOT(slotOpacitySliderMoved(qreal))); 0217 connect(&m_opacityDelayTimer, SIGNAL(timeout()), SLOT(slotOpacityChanged())); 0218 0219 connect(m_wdgLayerBox->cmbComposite, SIGNAL(activated(int)), SLOT(slotCompositeOpChanged(int))); 0220 0221 m_newLayerMenu = new QMenu(this); 0222 m_wdgLayerBox->bnAdd->setMenu(m_newLayerMenu); 0223 m_wdgLayerBox->bnAdd->setPopupMode(QToolButton::MenuButtonPopup); 0224 0225 m_opLayerMenu = new QMenu(this); 0226 m_wdgLayerBox->bnProperties->setMenu(m_opLayerMenu); 0227 m_wdgLayerBox->bnProperties->setPopupMode(QToolButton::MenuButtonPopup); 0228 0229 m_nodeModel = new KisNodeModel(this, 2); 0230 m_filteringModel = new KisNodeFilterProxyModel(this); 0231 m_filteringModel->setNodeModel(m_nodeModel); 0232 0233 /** 0234 * Connect model updateUI() to enable/disable controls. 0235 * Note: nodeActivated() is connected separately in setImage(), because 0236 * it needs particular order of calls: first the connection to the 0237 * node manager should be called, then updateUI() 0238 */ 0239 connect(m_nodeModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(updateUI())); 0240 connect(m_nodeModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(updateUI())); 0241 connect(m_nodeModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), SLOT(updateUI())); 0242 connect(m_nodeModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), SLOT(updateUI())); 0243 connect(m_nodeModel, SIGNAL(modelReset()), SLOT(slotModelReset())); 0244 0245 connect(m_nodeModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(slotForgetAboutSavedNodeBeforeEditSelectionMode())); 0246 connect(m_nodeModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(slotForgetAboutSavedNodeBeforeEditSelectionMode())); 0247 connect(m_nodeModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), SLOT(slotForgetAboutSavedNodeBeforeEditSelectionMode())); 0248 connect(m_nodeModel, SIGNAL(modelReset()), SLOT(slotForgetAboutSavedNodeBeforeEditSelectionMode())); 0249 0250 // we should update expanded state of the nodes on adding the nodes 0251 connect(m_nodeModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(slotNodeCollapsedChanged())); 0252 connect(m_nodeModel, SIGNAL(modelReset()), SLOT(slotNodeCollapsedChanged())); 0253 0254 m_showGlobalSelectionMask = new KisAction(i18n("&Show Global Selection Mask"), this); 0255 m_showGlobalSelectionMask->setObjectName("show-global-selection-mask"); 0256 m_showGlobalSelectionMask->setActivationFlags(KisAction::ACTIVE_IMAGE); 0257 m_showGlobalSelectionMask->setToolTip(i18nc("@info:tooltip", "Shows global selection as a usual selection mask in <b>Layers</b> docker")); 0258 m_showGlobalSelectionMask->setCheckable(true); 0259 connect(m_showGlobalSelectionMask, SIGNAL(triggered(bool)), SLOT(slotEditGlobalSelection(bool))); 0260 0261 m_showGlobalSelectionMask->setChecked(cfg.showGlobalSelection()); 0262 0263 m_colorSelector = new KisColorLabelSelectorWidgetMenuWrapper(this); 0264 MouseClickIgnore* mouseEater = new MouseClickIgnore(this); 0265 m_colorSelector->installEventFilter(mouseEater); 0266 connect(m_colorSelector->colorLabelSelector(), SIGNAL(currentIndexChanged(int)), SLOT(slotColorLabelChanged(int))); 0267 m_colorSelectorAction = new QWidgetAction(this); 0268 m_colorSelectorAction->setDefaultWidget(m_colorSelector); 0269 0270 connect(m_nodeModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), 0271 &m_colorLabelCompressor, SLOT(start())); 0272 0273 m_wdgLayerBox->listLayers->setModel(m_filteringModel); 0274 // this connection should be done *after* the setModel() call to 0275 // happen later than the internal selection model 0276 connect(m_filteringModel.data(), &KisNodeFilterProxyModel::sigBeforeBeginRemoveRows, 0277 this, &LayerBox::slotAdjustCurrentBeforeRemoveRows); 0278 0279 0280 //LayerFilter Menu 0281 QMenu *layerFilterMenu = new QMenu(this); 0282 m_wdgLayerBox->bnLayerFilters->setMenu(layerFilterMenu); 0283 m_wdgLayerBox->bnLayerFilters->setPopupMode(QToolButton::InstantPopup); 0284 0285 const QIcon filterIcon = KisIconUtils::loadIcon("view-filter"); 0286 m_wdgLayerBox->bnLayerFilters->setIcon(filterIcon); 0287 m_wdgLayerBox->bnLayerFilters->setAutoRaise(true); 0288 QPixmap filterEnabledPixmap = filterIcon.pixmap(64,64); 0289 const QBitmap filterEnabledBitmask = filterEnabledPixmap.mask(); 0290 filterEnabledPixmap.fill(palette().color(QPalette::Highlight)); 0291 filterEnabledPixmap.setMask(filterEnabledBitmask); 0292 const QIcon filterEnabledIcon = QIcon(filterEnabledPixmap); 0293 0294 layerFilterWidget = new KisLayerFilterWidget(this); 0295 connect(layerFilterWidget, SIGNAL(filteringOptionsChanged()), this, SLOT(updateLayerFiltering())); 0296 connect(layerFilterWidget, &KisLayerFilterWidget::filteringOptionsChanged, [this, filterIcon, filterEnabledIcon](){ 0297 if(layerFilterWidget->isCurrentlyFiltering()) { 0298 m_wdgLayerBox->bnLayerFilters->setIcon(filterEnabledIcon); 0299 } else { 0300 m_wdgLayerBox->bnLayerFilters->setIcon(filterIcon); 0301 } 0302 0303 m_wdgLayerBox->bnLayerFilters->setSelectedColors(QList<int>::fromSet(layerFilterWidget->getActiveColors())); 0304 m_wdgLayerBox->bnLayerFilters->setTextFilter(layerFilterWidget->hasTextFilter()); 0305 }); 0306 0307 QWidgetAction *layerFilterMenuAction = new QWidgetAction(this); 0308 layerFilterMenuAction->setDefaultWidget(layerFilterWidget); 0309 layerFilterMenu->addAction(layerFilterMenuAction); 0310 0311 0312 KisMenuStyleDontCloseOnAlt *menuStyle = new KisMenuStyleDontCloseOnAlt(layerFilterMenu->style()); 0313 menuStyle->setParent(layerFilterMenu); 0314 layerFilterMenu->setStyle(menuStyle); 0315 0316 setEnabled(false); 0317 0318 connect(&m_colorLabelCompressor, SIGNAL(timeout()), SLOT(updateAvailableLabels())); 0319 0320 0321 // set up the configure menu for changing thumbnail size 0322 QMenu* configureMenu = new QMenu(this); 0323 configureMenu->setContentsMargins(6, 6, 6, 6); 0324 configureMenu->addSection(i18n("Thumbnail Size")); 0325 0326 m_wdgLayerBox->configureLayerDockerToolbar->setMenu(configureMenu); 0327 m_wdgLayerBox->configureLayerDockerToolbar->setIconSize(QSize(16, 16)); 0328 m_wdgLayerBox->configureLayerDockerToolbar->setPopupMode(QToolButton::InstantPopup); 0329 m_wdgLayerBox->configureLayerDockerToolbar->setAutoRaise(true); 0330 0331 0332 // add horizontal slider 0333 thumbnailSizeSlider = new QSlider(this); 0334 thumbnailSizeSlider->setOrientation(Qt::Horizontal); 0335 thumbnailSizeSlider->setRange(20, 80); 0336 0337 thumbnailSizeSlider->setValue(cfg.layerThumbnailSize(false)); // grab this from the kritarc 0338 0339 thumbnailSizeSlider->setMinimumHeight(20); 0340 thumbnailSizeSlider->setMinimumWidth(40); 0341 thumbnailSizeSlider->setTickInterval(5); 0342 m_nodeModel->setPreferredThumnalSize(cfg.layerThumbnailSize()); 0343 0344 QWidgetAction *sliderAction= new QWidgetAction(this); 0345 sliderAction->setDefaultWidget(thumbnailSizeSlider); 0346 configureMenu->addAction(sliderAction); 0347 0348 0349 connect(thumbnailSizeSlider, SIGNAL(valueChanged(int)), &m_thumbnailSizeCompressor, SLOT(start())); 0350 connect(&m_thumbnailSizeCompressor, SIGNAL(timeout()), SLOT(slotUpdateThumbnailIconSize())); 0351 0352 configureMenu->addSection(i18nc("@item:inmenu Layers Docker settings, slider", "Tree Indentation")); 0353 0354 // add horizontal slider 0355 indentationSlider = new QSlider(Qt::Horizontal, this); 0356 indentationSlider->setRange(20, 100); 0357 indentationSlider->setMinimumSize(40, 20); 0358 indentationSlider->setSingleStep(5); 0359 indentationSlider->setPageStep(20); 0360 indentationSlider->setValue(cfg.layerTreeIndentation()); 0361 0362 0363 sliderAction= new QWidgetAction(this); 0364 sliderAction->setDefaultWidget(indentationSlider); 0365 configureMenu->addAction(sliderAction); 0366 0367 // NOTE: if KisConfig would just compress its file sync events, we wouldn't need 0368 // this extra compressor that juggles between slow UI and disk thrashing 0369 connect(indentationSlider, SIGNAL(valueChanged(int)), &m_treeIndentationCompressor, SLOT(start())); 0370 connect(&m_treeIndentationCompressor, SIGNAL(timeout()), SLOT(slotUpdateTreeIndentation())); 0371 0372 0373 // Layer info-text settings: 0374 // blending info-text style combobox 0375 configureMenu->addSection(i18nc("@item:inmenu Layers Docker settings, combobox", "Blending Info Style")); 0376 infoTextCombobox = new QComboBox(this); 0377 infoTextCombobox->setToolTip(i18nc("@item:tooltip", "None: Show nothing.\n" 0378 "Simple: Show changed opacities or blending modes.\n" 0379 "Balanced: Show both opacity and blending mode if either are changed.\n" 0380 "Detailed: Show both opacity and blending mode even if unchanged.")); 0381 infoTextCombobox->insertItems(0, QStringList ({ 0382 i18nc("@item:inlistbox Layer Docker blending info style", "None"), 0383 i18nc("@item:inlistbox Layer Docker blending info style", "Simple"), 0384 i18nc("@item:inlistbox Layer Docker blending info style", "Balanced"), 0385 i18nc("@item:inlistbox Layer Docker blending info style", "Detailed"), 0386 })); 0387 infoTextCombobox->setCurrentIndex((int)cfg.layerInfoTextStyle()); 0388 0389 QWidgetAction *cmbboxAction = new QWidgetAction(this); 0390 cmbboxAction->setDefaultWidget(infoTextCombobox); 0391 configureMenu->addAction(cmbboxAction); 0392 connect(infoTextCombobox, SIGNAL(currentIndexChanged(int)), SLOT(slotUpdateLayerInfoTextStyle())); 0393 0394 // info-text opacity slider 0395 infoTextOpacitySlider = new KisSliderSpinBox(this); 0396 KisSpinBoxI18nHelper::setText(infoTextOpacitySlider, 0397 i18nc("{n} is the number value, % is the percent sign", "Opacity: {n}%")); 0398 infoTextOpacitySlider->setToolTip(i18nc("@item:tooltip", "Blending info text opacity")); 0399 // 55% is the opacity of nonvisible layer text 0400 infoTextOpacitySlider->setRange(55, 100); 0401 infoTextOpacitySlider->setMinimumSize(40, 20); 0402 infoTextOpacitySlider->setSingleStep(5); 0403 infoTextOpacitySlider->setPageStep(15); 0404 infoTextOpacitySlider->setValue(cfg.layerInfoTextOpacity()); 0405 if (infoTextCombobox->currentIndex() == 0) { 0406 infoTextOpacitySlider->setDisabled(true); 0407 } 0408 0409 sliderAction= new QWidgetAction(this); 0410 sliderAction->setDefaultWidget(infoTextOpacitySlider); 0411 configureMenu->addAction(sliderAction); 0412 connect(infoTextOpacitySlider, SIGNAL(valueChanged(int)), &m_infoTextOpacityCompressor, SLOT(start())); 0413 connect(&m_infoTextOpacityCompressor, SIGNAL(timeout()), SLOT(slotUpdateLayerInfoTextOpacity())); 0414 0415 // info-text inline checkbox 0416 infoTextInlineChkbox = new QCheckBox(i18nc("@item:inmenu Layers Docker settings, checkbox", "Inline"), this); 0417 infoTextInlineChkbox->setChecked(cfg.useInlineLayerInfoText()); 0418 infoTextInlineChkbox->setToolTip(i18nc("@item:tooltip", "If enabled, show blending info beside layer names.\n" 0419 "If disabled, show below layer names (when enough space).")); 0420 if (infoTextCombobox->currentIndex() == 0) { 0421 infoTextInlineChkbox->setDisabled(true); 0422 } 0423 0424 QWidgetAction *chkboxAction = new QWidgetAction(this); 0425 chkboxAction->setDefaultWidget(infoTextInlineChkbox); 0426 configureMenu->addAction(chkboxAction); 0427 connect(infoTextInlineChkbox, SIGNAL(stateChanged(int)), SLOT(slotUpdateUseInlineLayerInfoText())); 0428 0429 layerSelectionCheckBox = new QCheckBox( 0430 i18nc("@item:inmenu Layers Docker settings, checkbox", "Checkbox for Selecting Layers"), this); 0431 layerSelectionCheckBox->setToolTip(i18nc("@item:tooltip", "Show checkbox to select/unselect layers.")); 0432 layerSelectionCheckBox->setChecked(cfg.useLayerSelectionCheckbox()); 0433 0434 QWidgetAction *layerSelectionAction = new QWidgetAction(this); 0435 layerSelectionAction->setDefaultWidget(layerSelectionCheckBox); 0436 configureMenu->addAction(layerSelectionAction); 0437 connect(layerSelectionCheckBox, SIGNAL(stateChanged(int)), SLOT(slotUpdateUseLayerSelectionCheckbox())); 0438 } 0439 0440 LayerBox::~LayerBox() 0441 { 0442 delete m_wdgLayerBox; 0443 } 0444 0445 0446 void expandNodesRecursively(KisNodeSP root, QPointer<KisNodeFilterProxyModel> filteringModel, NodeView *nodeView) 0447 { 0448 if (!root) return; 0449 if (filteringModel.isNull()) return; 0450 if (!nodeView) return; 0451 0452 nodeView->blockSignals(true); 0453 0454 KisNodeSP node = root->firstChild(); 0455 while (node) { 0456 QModelIndex idx = filteringModel->indexFromNode(node); 0457 if (idx.isValid()) { 0458 nodeView->setExpanded(idx, !node->collapsed()); 0459 } 0460 if (!node->collapsed() && node->childCount() > 0) { 0461 expandNodesRecursively(node, filteringModel, nodeView); 0462 } 0463 node = node->nextSibling(); 0464 } 0465 nodeView->blockSignals(false); 0466 } 0467 0468 void LayerBox::slotAddLayerBnClicked() 0469 { 0470 if (m_canvas) { 0471 KisNodeList nodes = m_nodeManager->selectedNodes(); 0472 0473 if (nodes.size() == 1) { 0474 KisAction *action = m_canvas->viewManager()->actionManager()->actionByName("add_new_paint_layer"); 0475 action->trigger(); 0476 } else { 0477 KisAction *action = m_canvas->viewManager()->actionManager()->actionByName("create_quick_group"); 0478 action->trigger(); 0479 } 0480 } 0481 } 0482 0483 void LayerBox::setViewManager(KisViewManager* kisview) 0484 { 0485 m_nodeManager = kisview->nodeManager(); 0486 0487 if (m_nodeManager) { 0488 connect(m_nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), SLOT(slotForgetAboutSavedNodeBeforeEditSelectionMode())); 0489 } 0490 0491 kisview->actionManager()-> 0492 addAction(m_showGlobalSelectionMask->objectName(), 0493 m_showGlobalSelectionMask); 0494 0495 connect(m_wdgLayerBox->bnAdd, SIGNAL(clicked()), this, SLOT(slotAddLayerBnClicked())); 0496 0497 connectActionToButton(kisview, m_wdgLayerBox->bnDuplicate, "duplicatelayer"); 0498 0499 KisActionManager *actionManager = kisview->actionManager(); 0500 0501 KisAction *action = actionManager->createAction("RenameCurrentLayer"); 0502 Q_ASSERT(action); 0503 connect(action, SIGNAL(triggered()), this, SLOT(slotRenameCurrentNode())); 0504 0505 m_propertiesAction = actionManager->createAction("layer_properties"); 0506 Q_ASSERT(m_propertiesAction); 0507 new SyncButtonAndAction(m_propertiesAction, m_wdgLayerBox->bnProperties, this); 0508 connect(m_propertiesAction, SIGNAL(triggered()), this, SLOT(slotPropertiesClicked())); 0509 0510 connect(m_opLayerMenu, SIGNAL(aboutToShow()), this, SLOT(slotLayerOpMenuOpened())); 0511 0512 // It's necessary to clear the layer operations menu when it closes, else 0513 // the color selector can't be shared with the right-click context menu 0514 connect(m_opLayerMenu, SIGNAL(aboutToHide()), this, SLOT(slotLayerOpMenuClosed())); 0515 0516 m_removeAction = actionManager->createAction("remove_layer"); 0517 Q_ASSERT(m_removeAction); 0518 new SyncButtonAndAction(m_removeAction, m_wdgLayerBox->bnDelete, this); 0519 connect(m_removeAction, SIGNAL(triggered()), this, SLOT(slotRmClicked())); 0520 0521 action = actionManager->createAction("move_layer_up"); 0522 Q_ASSERT(action); 0523 new SyncButtonAndAction(action, m_wdgLayerBox->bnRaise, this); 0524 connect(action, SIGNAL(triggered()), this, SLOT(slotRaiseClicked())); 0525 0526 action = actionManager->createAction("move_layer_down"); 0527 Q_ASSERT(action); 0528 new SyncButtonAndAction(action, m_wdgLayerBox->bnLower, this); 0529 connect(action, SIGNAL(triggered()), this, SLOT(slotLowerClicked())); 0530 0531 m_changeCloneSourceAction = actionManager->createAction("set-copy-from"); 0532 Q_ASSERT(m_changeCloneSourceAction); 0533 connect(m_changeCloneSourceAction, &KisAction::triggered, 0534 this, &LayerBox::slotChangeCloneSourceClicked); 0535 0536 m_layerToggleSolo = actionManager->createAction("toggle_layer_soloing"); 0537 connect(m_layerToggleSolo, SIGNAL(triggered(bool)), this, SLOT(toggleActiveLayerSolo())); 0538 } 0539 0540 void LayerBox::setCanvas(KoCanvasBase *canvas) 0541 { 0542 if (m_canvas == canvas) 0543 return; 0544 0545 setEnabled(canvas != 0); 0546 0547 if (m_canvas) { 0548 m_canvas->disconnectCanvasObserver(this); 0549 m_nodeModel->setIdleTaskManager(0); 0550 m_nodeModel->setDummiesFacade(0, 0, 0, 0, 0); 0551 m_selectionActionsAdapter.reset(); 0552 0553 if (m_image) { 0554 KisImageAnimationInterface *animation = m_image->animationInterface(); 0555 animation->disconnect(this); 0556 } 0557 0558 disconnect(m_image, 0, this, 0); 0559 disconnect(m_nodeManager, 0, this, 0); 0560 disconnect(m_nodeModel, 0, m_nodeManager, 0); 0561 m_nodeManager->slotSetSelectedNodes(KisNodeList()); 0562 } 0563 0564 m_canvas = dynamic_cast<KisCanvas2*>(canvas); 0565 0566 if (m_canvas) { 0567 m_image = m_canvas->image(); 0568 emit imageChanged(); 0569 0570 KisDocument* doc = static_cast<KisDocument*>(m_canvas->imageView()->document()); 0571 KisShapeController *kritaShapeController = 0572 dynamic_cast<KisShapeController*>(doc->shapeController()); 0573 KisDummiesFacadeBase *kritaDummiesFacade = 0574 static_cast<KisDummiesFacadeBase*>(kritaShapeController); 0575 0576 0577 m_selectionActionsAdapter.reset(new KisSelectionActionsAdapter(m_canvas->viewManager()->selectionManager())); 0578 m_nodeModel->setDummiesFacade(kritaDummiesFacade, 0579 m_image, 0580 kritaShapeController, 0581 m_selectionActionsAdapter.data(), 0582 m_nodeManager); 0583 0584 if (isVisible()) { 0585 m_nodeModel->setIdleTaskManager(m_canvas->viewManager()->idleTasksManager()); 0586 } 0587 0588 connect(m_image, SIGNAL(sigAboutToBeDeleted()), SLOT(notifyImageDeleted())); 0589 connect(m_image, SIGNAL(sigNodeCollapsedChanged()), SLOT(slotNodeCollapsedChanged())); 0590 0591 // cold start 0592 if (m_nodeManager) { 0593 setCurrentNode(m_nodeManager->activeNode()); 0594 // Connection KisNodeManager -> LayerBox 0595 connect(m_nodeManager, SIGNAL(sigUiNeedChangeActiveNode(KisNodeSP)), 0596 this, SLOT(setCurrentNode(KisNodeSP))); 0597 0598 connect(m_nodeManager, 0599 SIGNAL(sigUiNeedChangeSelectedNodes(QList<KisNodeSP>)), 0600 SLOT(slotNodeManagerChangedSelection(QList<KisNodeSP>))); 0601 } 0602 else { 0603 setCurrentNode(m_canvas->imageView()->currentNode()); 0604 } 0605 0606 // Connection LayerBox -> KisNodeManager (isolate layer) 0607 connect(m_nodeModel, SIGNAL(toggleIsolateActiveNode()), 0608 m_nodeManager, SLOT(toggleIsolateActiveNode())); 0609 0610 KisImageAnimationInterface *animation = m_image->animationInterface(); 0611 connect(animation, &KisImageAnimationInterface::sigUiTimeChanged, this, &LayerBox::slotImageTimeChanged); 0612 0613 expandNodesRecursively(m_image->rootLayer(), m_filteringModel, m_wdgLayerBox->listLayers); 0614 m_wdgLayerBox->listLayers->scrollTo(m_wdgLayerBox->listLayers->currentIndex()); 0615 updateAvailableLabels(); 0616 0617 addActionToMenu(m_newLayerMenu, "add_new_paint_layer"); 0618 addActionToMenu(m_newLayerMenu, "add_new_group_layer"); 0619 addActionToMenu(m_newLayerMenu, "add_new_clone_layer"); 0620 addActionToMenu(m_newLayerMenu, "add_new_shape_layer"); 0621 addActionToMenu(m_newLayerMenu, "add_new_adjustment_layer"); 0622 addActionToMenu(m_newLayerMenu, "add_new_fill_layer"); 0623 addActionToMenu(m_newLayerMenu, "add_new_file_layer"); 0624 m_newLayerMenu->addSeparator(); 0625 addActionToMenu(m_newLayerMenu, "add_new_transparency_mask"); 0626 addActionToMenu(m_newLayerMenu, "add_new_filter_mask"); 0627 addActionToMenu(m_newLayerMenu, "add_new_colorize_mask"); 0628 addActionToMenu(m_newLayerMenu, "add_new_transform_mask"); 0629 addActionToMenu(m_newLayerMenu, "add_new_selection_mask"); 0630 0631 } 0632 0633 } 0634 0635 0636 void LayerBox::unsetCanvas() 0637 { 0638 setEnabled(false); 0639 if (m_canvas) { 0640 m_newLayerMenu->clear(); 0641 } 0642 0643 m_filteringModel->unsetDummiesFacade(); 0644 disconnect(m_image, 0, this, 0); 0645 disconnect(m_nodeManager, 0, this, 0); 0646 disconnect(m_nodeModel, 0, m_nodeManager, 0); 0647 m_nodeManager->slotSetSelectedNodes(KisNodeList()); 0648 0649 m_canvas = 0; 0650 } 0651 0652 void LayerBox::showEvent(QShowEvent *event) 0653 { 0654 QDockWidget::showEvent(event); 0655 0656 if (m_canvas) { 0657 m_nodeModel->setIdleTaskManager(m_canvas->viewManager()->idleTasksManager()); 0658 } 0659 } 0660 0661 void LayerBox::hideEvent(QHideEvent *event) 0662 { 0663 QDockWidget::hideEvent(event); 0664 m_nodeModel->setIdleTaskManager(0); 0665 } 0666 0667 void LayerBox::notifyImageDeleted() 0668 { 0669 setCanvas(0); 0670 } 0671 0672 void LayerBox::updateUI() 0673 { 0674 if (!m_canvas) return; 0675 if (!m_nodeManager) return; 0676 0677 KisNodeSP activeNode = m_nodeManager->activeNode(); 0678 0679 if (activeNode != m_activeNode) { 0680 m_activeNodeConnections.clear(); 0681 m_activeNode = activeNode; 0682 0683 if (activeNode) { 0684 KisPaintDeviceSP parentLayerDevice = activeNode->parent() ? activeNode->parent()->original() : 0; 0685 if (parentLayerDevice) { 0686 // update blending modes availability 0687 m_activeNodeConnections.addConnection( 0688 parentLayerDevice, SIGNAL(colorSpaceChanged(const KoColorSpace*)), 0689 this, SLOT(updateUI())); 0690 } 0691 0692 m_activeNodeConnections.addConnection( 0693 activeNode, SIGNAL(opacityChanged(quint8)), 0694 this, SLOT(slotUpdateOpacitySlider(quint8))); 0695 } 0696 } 0697 0698 m_wdgLayerBox->bnRaise->setEnabled(activeNode && activeNode->isEditable(false) && (activeNode->nextSibling() 0699 || (activeNode->parent() && activeNode->parent() != m_image->root()))); 0700 m_wdgLayerBox->bnLower->setEnabled(activeNode && activeNode->isEditable(false) && (activeNode->prevSibling() 0701 || (activeNode->parent() && activeNode->parent() != m_image->root()))); 0702 0703 m_wdgLayerBox->doubleOpacity->setEnabled(activeNode && activeNode->isEditable(false)); 0704 0705 m_wdgLayerBox->cmbComposite->setEnabled(activeNode && activeNode->isEditable(false)); 0706 0707 if (activeNode) { 0708 if (activeNode->inherits("KisColorizeMask") || activeNode->inherits("KisLayer")) { 0709 0710 m_wdgLayerBox->doubleOpacity->setEnabled(true); 0711 0712 if (!m_wdgLayerBox->doubleOpacity->isDragging()) { 0713 slotSetOpacity(activeNode->opacity() * 100.0 / 255); 0714 } 0715 0716 const KoCompositeOp* compositeOp = activeNode->compositeOp(); 0717 if (compositeOp) { 0718 /// the composite op works in the color space of the parent layer, 0719 /// not the active one. 0720 m_wdgLayerBox->cmbComposite->validate(compositeOp->colorSpace()); 0721 slotSetCompositeOp(compositeOp); 0722 } else { 0723 m_wdgLayerBox->cmbComposite->setEnabled(false); 0724 } 0725 0726 const KisGroupLayer *group = qobject_cast<const KisGroupLayer*>(activeNode.data()); 0727 bool compositeSelectionActive = !(group && group->passThroughMode()); 0728 0729 m_wdgLayerBox->cmbComposite->setEnabled(compositeSelectionActive); 0730 } else if (activeNode->inherits("KisMask")) { 0731 m_wdgLayerBox->cmbComposite->setEnabled(false); 0732 m_wdgLayerBox->doubleOpacity->setEnabled(false); 0733 } 0734 } 0735 } 0736 0737 0738 /** 0739 * This method is called *only* when non-GUI code requested the 0740 * change of the current node 0741 */ 0742 void LayerBox::setCurrentNode(KisNodeSP node) 0743 { 0744 m_filteringModel->setActiveNode(node); 0745 0746 QModelIndex index = node ? m_filteringModel->indexFromNode(node) : QModelIndex(); 0747 m_filteringModel->setData(index, true, KisNodeModel::ActiveRole); 0748 updateUI(); 0749 } 0750 0751 void LayerBox::slotModelReset() 0752 { 0753 if(m_nodeModel->hasDummiesFacade()) { 0754 QItemSelection selection; 0755 Q_FOREACH (const KisNodeSP node, m_nodeManager->selectedNodes()) { 0756 const QModelIndex &idx = m_filteringModel->indexFromNode(node); 0757 if(idx.isValid()){ 0758 QItemSelectionRange selectionRange(idx); 0759 selection << selectionRange; 0760 } 0761 } 0762 0763 m_wdgLayerBox->listLayers->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect); 0764 } 0765 0766 updateUI(); 0767 } 0768 0769 void LayerBox::slotSetCompositeOp(const KoCompositeOp* compositeOp) 0770 { 0771 KoID opId = KoCompositeOpRegistry::instance().getKoID(compositeOp->id()); 0772 0773 m_wdgLayerBox->cmbComposite->blockSignals(true); 0774 m_wdgLayerBox->cmbComposite->selectCompositeOp(opId); 0775 m_wdgLayerBox->cmbComposite->blockSignals(false); 0776 } 0777 0778 // range: 0-100 0779 void LayerBox::slotSetOpacity(double opacity) 0780 { 0781 Q_ASSERT(opacity >= 0 && opacity <= 100); 0782 m_wdgLayerBox->doubleOpacity->blockSignals(true); 0783 m_wdgLayerBox->doubleOpacity->setValue(opacity); 0784 m_wdgLayerBox->doubleOpacity->blockSignals(false); 0785 } 0786 0787 void LayerBox::slotUpdateOpacitySlider(quint8 value) { 0788 double percentage = value * 100.0 / 255.0; 0789 0790 m_wdgLayerBox->doubleOpacity->blockSignals(true); 0791 m_wdgLayerBox->doubleOpacity->setValue(percentage); 0792 m_wdgLayerBox->doubleOpacity->blockSignals(false); 0793 } 0794 0795 void LayerBox::slotContextMenuRequested(const QPoint &pos, const QModelIndex &index) 0796 { 0797 KisNodeList nodes = m_nodeManager->selectedNodes(); 0798 KisNodeSP activeNode = m_nodeManager->activeNode(); 0799 if (nodes.isEmpty() || !activeNode) return; 0800 0801 if (m_canvas) { 0802 QMenu menu; 0803 updateLayerOpMenu(index, menu); 0804 menu.exec(pos); 0805 } 0806 } 0807 0808 void LayerBox::slotRmClicked() 0809 { 0810 if (!m_canvas) return; 0811 m_nodeManager->removeNode(); 0812 } 0813 0814 void LayerBox::slotRaiseClicked() 0815 { 0816 if (!m_canvas) return; 0817 m_nodeManager->raiseNode(); 0818 } 0819 0820 void LayerBox::slotLowerClicked() 0821 { 0822 if (!m_canvas) return; 0823 m_nodeManager->lowerNode(); 0824 } 0825 0826 void LayerBox::slotPropertiesClicked() 0827 { 0828 if (!m_canvas) return; 0829 if (KisNodeSP active = m_nodeManager->activeNode()) { 0830 m_nodeManager->nodeProperties(active); 0831 } 0832 } 0833 0834 void LayerBox::slotLayerOpMenuOpened() 0835 { 0836 if (!m_canvas) return; 0837 updateLayerOpMenu(m_wdgLayerBox->listLayers->currentIndex(), *m_opLayerMenu); 0838 } 0839 0840 void LayerBox::slotLayerOpMenuClosed() 0841 { 0842 if (!m_canvas) return; 0843 m_opLayerMenu->clear(); 0844 } 0845 0846 void LayerBox::slotChangeCloneSourceClicked() 0847 { 0848 if (!m_canvas) return; 0849 m_nodeManager->changeCloneSource(); 0850 } 0851 0852 void LayerBox::slotCompositeOpChanged(int index) 0853 { 0854 Q_UNUSED(index); 0855 if (!m_canvas) return; 0856 0857 QString compositeOp = m_wdgLayerBox->cmbComposite->selectedCompositeOp().id(); 0858 m_nodeManager->nodeCompositeOpChanged(m_nodeManager->activeColorSpace()->compositeOp(compositeOp)); 0859 } 0860 0861 void LayerBox::slotOpacityChanged() 0862 { 0863 if (!m_canvas) return; 0864 m_blockOpacityUpdate = true; 0865 m_nodeManager->setNodeOpacity(m_changedOpacityNode, convertOpacityToInt(m_newOpacity)); 0866 m_blockOpacityUpdate = false; 0867 } 0868 0869 void LayerBox::slotOpacitySliderMoved(qreal opacity) 0870 { 0871 m_newOpacity = opacity; 0872 m_changedOpacityNode = m_activeNode; 0873 m_opacityDelayTimer.start(200); 0874 } 0875 0876 void LayerBox::slotCollapsed(const QModelIndex &index) 0877 { 0878 KisNodeSP node = m_filteringModel->nodeFromIndex(index); 0879 if (node) { 0880 node->setCollapsed(true); 0881 } 0882 } 0883 0884 void LayerBox::slotExpanded(const QModelIndex &index) 0885 { 0886 KisNodeSP node = m_filteringModel->nodeFromIndex(index); 0887 if (node) { 0888 node->setCollapsed(false); 0889 } 0890 } 0891 0892 void LayerBox::slotSelectOpaque() 0893 { 0894 if (!m_canvas) return; 0895 QAction *action = m_canvas->viewManager()->actionManager()->actionByName("selectopaque"); 0896 if (action) { 0897 action->trigger(); 0898 } 0899 } 0900 0901 void LayerBox::slotNodeCollapsedChanged() 0902 { 0903 if (m_nodeModel->hasDummiesFacade()) { 0904 expandNodesRecursively(m_image->rootLayer(), m_filteringModel, m_wdgLayerBox->listLayers); 0905 } 0906 } 0907 0908 inline bool isSelectionMask(KisNodeSP node) 0909 { 0910 return dynamic_cast<KisSelectionMask*>(node.data()); 0911 } 0912 0913 KisNodeSP LayerBox::findNonHidableNode(KisNodeSP startNode) 0914 { 0915 if (KisNodeManager::isNodeHidden(startNode, true) && 0916 startNode->parent() && 0917 !startNode->parent()->parent()) { 0918 0919 0920 KisNodeSP node = startNode->prevSibling(); 0921 while (node && KisNodeManager::isNodeHidden(node, true)) { 0922 node = node->prevSibling(); 0923 } 0924 0925 if (!node) { 0926 node = startNode->nextSibling(); 0927 while (node && KisNodeManager::isNodeHidden(node, true)) { 0928 node = node->nextSibling(); 0929 } 0930 } 0931 0932 if (!node) { 0933 node = m_image->root()->lastChild(); 0934 while (node && KisNodeManager::isNodeHidden(node, true)) { 0935 node = node->prevSibling(); 0936 } 0937 } 0938 0939 KIS_ASSERT_RECOVER_NOOP(node && "cannot activate any node!"); 0940 startNode = node; 0941 } 0942 0943 return startNode; 0944 } 0945 0946 void LayerBox::slotEditGlobalSelection(bool showSelections) 0947 { 0948 KisNodeSP lastActiveNode = m_nodeManager->activeNode(); 0949 KisNodeSP activateNode = lastActiveNode; 0950 KisSelectionMaskSP globalSelectionMask; 0951 0952 if (!showSelections) { 0953 activateNode = 0954 m_savedNodeBeforeEditSelectionMode ? 0955 KisNodeSP(m_savedNodeBeforeEditSelectionMode) : 0956 findNonHidableNode(activateNode); 0957 } 0958 0959 m_nodeModel->setShowGlobalSelection(showSelections); 0960 0961 globalSelectionMask = m_image->rootLayer()->selectionMask(); 0962 0963 // try to find deactivated, but visible masks 0964 if (!globalSelectionMask) { 0965 KoProperties properties; 0966 properties.setProperty("visible", true); 0967 QList<KisNodeSP> masks = m_image->rootLayer()->childNodes(QStringList("KisSelectionMask"), properties); 0968 if (!masks.isEmpty()) { 0969 globalSelectionMask = dynamic_cast<KisSelectionMask*>(masks.first().data()); 0970 } 0971 } 0972 0973 // try to find at least any selection mask 0974 if (!globalSelectionMask) { 0975 KoProperties properties; 0976 QList<KisNodeSP> masks = m_image->rootLayer()->childNodes(QStringList("KisSelectionMask"), properties); 0977 if (!masks.isEmpty()) { 0978 globalSelectionMask = dynamic_cast<KisSelectionMask*>(masks.first().data()); 0979 } 0980 } 0981 0982 if (globalSelectionMask) { 0983 if (showSelections) { 0984 activateNode = globalSelectionMask; 0985 } 0986 } 0987 0988 if (activateNode != lastActiveNode) { 0989 m_nodeManager->slotNonUiActivatedNode(activateNode); 0990 } else if (lastActiveNode) { 0991 setCurrentNode(lastActiveNode); 0992 } 0993 0994 if (showSelections && !globalSelectionMask) { 0995 KisProcessingApplicator applicator(m_image, 0, 0996 KisProcessingApplicator::NONE, 0997 KisImageSignalVector(), 0998 kundo2_i18n("Quick Selection Mask")); 0999 1000 applicator.applyCommand( 1001 new KisLayerUtils::KeepNodesSelectedCommand( 1002 m_nodeManager->selectedNodes(), KisNodeList(), 1003 lastActiveNode, 0, m_image, false), 1004 KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); 1005 applicator.applyCommand(new KisSetEmptyGlobalSelectionCommand(m_image), 1006 KisStrokeJobData::SEQUENTIAL, 1007 KisStrokeJobData::EXCLUSIVE); 1008 applicator.applyCommand(new KisLayerUtils::SelectGlobalSelectionMask(m_image), 1009 KisStrokeJobData::SEQUENTIAL, 1010 KisStrokeJobData::EXCLUSIVE); 1011 1012 applicator.end(); 1013 } else if (!showSelections && 1014 globalSelectionMask && 1015 globalSelectionMask->selection()->selectedRect().isEmpty()) { 1016 1017 KisProcessingApplicator applicator(m_image, 0, 1018 KisProcessingApplicator::NONE, 1019 KisImageSignalVector(), 1020 kundo2_i18n("Cancel Quick Selection Mask")); 1021 applicator.applyCommand(new KisSetGlobalSelectionCommand(m_image, 0), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); 1022 applicator.end(); 1023 } 1024 1025 if (showSelections) { 1026 m_savedNodeBeforeEditSelectionMode = lastActiveNode; 1027 } 1028 } 1029 1030 void LayerBox::selectionChanged(const QModelIndexList &selection) 1031 { 1032 if (!m_nodeManager) return; 1033 1034 /** 1035 * When the user clears the extended selection by clicking on the 1036 * empty area of the docker, the selection should be reset on to 1037 * the active layer, which might be even unselected(!). 1038 */ 1039 if (selection.isEmpty() && m_nodeManager->activeNode()) { 1040 QModelIndex selectedIndex = 1041 m_filteringModel->indexFromNode(m_nodeManager->activeNode()); 1042 1043 m_wdgLayerBox->listLayers->selectionModel()-> 1044 setCurrentIndex(selectedIndex, QItemSelectionModel::ClearAndSelect); 1045 return; 1046 } 1047 1048 QList<KisNodeSP> selectedNodes; 1049 Q_FOREACH (const QModelIndex &idx, selection) { 1050 // Precaution because node manager doesn't like duplicates in that list. 1051 // NodeView Selection behavior is SelectRows, although currently only column 0 allows selections. 1052 if (idx.column() != 0) { 1053 continue; 1054 } 1055 selectedNodes << m_filteringModel->nodeFromIndex(idx); 1056 } 1057 1058 m_nodeManager->slotSetSelectedNodes(selectedNodes); 1059 updateUI(); 1060 } 1061 1062 void LayerBox::slotAdjustCurrentBeforeRemoveRows(const QModelIndex &parent, int start, int end) 1063 { 1064 /** 1065 * Qt has changed its behavior when deleting an item. Previously 1066 * the selection priority was on the next item in the list, and 1067 * now it has changed to the previous item. Here we just adjust 1068 * the selected item after the node removal. 1069 * 1070 * This method is called right before the Qt's beginRemoveRows() 1071 * is called, that is we make sure that Qt will never have to 1072 * adjust the position of the removed cursor. 1073 * 1074 * See bug: https://bugs.kde.org/show_bug.cgi?id=345601 1075 */ 1076 1077 QModelIndex currentIndex = m_wdgLayerBox->listLayers->currentIndex(); 1078 QAbstractItemModel *model = m_filteringModel; 1079 1080 if (currentIndex.isValid() && parent == currentIndex.parent() 1081 && currentIndex.row() >= start && currentIndex.row() <= end) { 1082 QModelIndex old = currentIndex; 1083 1084 if (model && end < model->rowCount(parent) - 1) // there are rows left below the change 1085 currentIndex = model->index(end + 1, old.column(), parent); 1086 else if (model && start > 0) // there are rows left above the change 1087 currentIndex = model->index(start - 1, old.column(), parent); 1088 else // there are no rows left in the table 1089 currentIndex = QModelIndex(); 1090 1091 if (currentIndex.isValid() && currentIndex != old) { 1092 m_wdgLayerBox->listLayers->setCurrentIndex(currentIndex); 1093 } 1094 } 1095 } 1096 1097 void LayerBox::slotNodeManagerChangedSelection(const KisNodeList &nodes) 1098 { 1099 if (!m_nodeManager) return; 1100 1101 QModelIndexList newSelection; 1102 Q_FOREACH(KisNodeSP node, nodes) { 1103 newSelection << m_filteringModel->indexFromNode(node); 1104 } 1105 1106 QItemSelectionModel *model = m_wdgLayerBox->listLayers->selectionModel(); 1107 1108 if (KritaUtils::compareListsUnordered(newSelection, model->selectedRows())) { 1109 return; 1110 } 1111 1112 QItemSelection selection; 1113 Q_FOREACH(const QModelIndex &idx, newSelection) { 1114 selection.select(idx, idx); 1115 } 1116 1117 model->select(selection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); 1118 } 1119 1120 void LayerBox::slotRenameCurrentNode() 1121 { 1122 m_wdgLayerBox->listLayers->edit(m_wdgLayerBox->listLayers->currentIndex()); 1123 } 1124 1125 void LayerBox::slotColorLabelChanged(int label) 1126 { 1127 KisNodeList selectedNodes = m_nodeManager->selectedNodes(); 1128 1129 Q_FOREACH(KisNodeSP selectedNode, selectedNodes) { 1130 //Always apply label to selected nodes.. 1131 selectedNode->setColorLabelIndex(label); 1132 1133 //Apply label only to unlabelled children.. 1134 KisNodeList children = selectedNode->childNodes(QStringList(), KoProperties()); 1135 1136 auto applyLabelFunc = 1137 [label](KisNodeSP child) { 1138 if (child->colorLabelIndex() == 0) { 1139 child->setColorLabelIndex(label); 1140 } 1141 }; 1142 1143 Q_FOREACH(KisNodeSP child, children) { 1144 KisLayerUtils::recursiveApplyNodes(child, applyLabelFunc); 1145 } 1146 } 1147 } 1148 1149 void LayerBox::updateAvailableLabels() 1150 { 1151 if (!m_image) return; 1152 layerFilterWidget->updateColorLabels(m_image->root()); 1153 } 1154 1155 void LayerBox::updateLayerFiltering() 1156 { 1157 m_filteringModel->setAcceptedLabels(layerFilterWidget->getActiveColors()); 1158 m_filteringModel->setTextFilter(layerFilterWidget->getTextFilter()); 1159 } 1160 1161 void LayerBox::slotImageTimeChanged(int time) 1162 { 1163 Q_UNUSED(time); 1164 updateUI(); 1165 } 1166 1167 void LayerBox::updateLayerOpMenu(const QModelIndex &index, QMenu &menu) { 1168 1169 KisNodeList nodes = m_nodeManager->selectedNodes(); 1170 KisNodeSP activeNode = m_nodeManager->activeNode(); 1171 const bool singleNode = nodes.size() == 1; 1172 1173 if (index.isValid()) { 1174 menu.addAction(m_propertiesAction); 1175 1176 KisLayerSP singleLayer = dynamic_cast<KisLayer*>(activeNode.data()); 1177 1178 if (singleLayer) { 1179 addActionToMenu(&menu, "layer_style"); 1180 1181 if (singleLayer->layerStyle()) { 1182 addActionToMenu(&menu, "copy_layer_style"); 1183 } 1184 1185 if (KisClipboard::instance()->hasLayerStyles()) { 1186 addActionToMenu(&menu, "paste_layer_style"); 1187 } 1188 } 1189 1190 Q_FOREACH(KisNodeSP node, nodes) { 1191 if (node && node->inherits("KisCloneLayer")) { 1192 menu.addAction(m_changeCloneSourceAction); 1193 break; 1194 } 1195 } 1196 1197 { 1198 KisSignalsBlocker b(m_colorSelector->colorLabelSelector()); 1199 m_colorSelector->colorLabelSelector()->setCurrentIndex(singleNode ? activeNode->colorLabelIndex() : -1); 1200 } 1201 1202 menu.addAction(m_colorSelectorAction); 1203 1204 menu.addSeparator(); 1205 1206 addActionToMenu(&menu, "cut_layer_clipboard"); 1207 addActionToMenu(&menu, "copy_layer_clipboard"); 1208 addActionToMenu(&menu, "paste_layer_from_clipboard"); 1209 menu.addAction(m_removeAction); 1210 addActionToMenu(&menu, "duplicatelayer"); 1211 addActionToMenu(&menu, "merge_layer"); 1212 addActionToMenu(&menu, "new_from_visible"); 1213 1214 if (singleNode) { 1215 addActionToMenu(&menu, "flatten_image"); 1216 addActionToMenu(&menu, "flatten_layer"); 1217 } 1218 1219 menu.addSeparator(); 1220 QMenu *selectMenu = menu.addMenu(i18n("&Select")); 1221 addActionToMenu(selectMenu, "select_all_layers"); 1222 addActionToMenu(selectMenu, "select_visible_layers"); 1223 addActionToMenu(selectMenu, "select_invisible_layers"); 1224 addActionToMenu(selectMenu, "select_locked_layers"); 1225 addActionToMenu(selectMenu, "select_unlocked_layers"); 1226 QMenu *groupMenu = menu.addMenu(i18nc("A group of layers", "&Group")); 1227 addActionToMenu(groupMenu, "create_quick_group"); 1228 addActionToMenu(groupMenu, "create_quick_clipping_group"); 1229 addActionToMenu(groupMenu, "quick_ungroup"); 1230 QMenu *locksMenu = menu.addMenu(i18n("&Toggle Locks && Visibility")); 1231 addActionToMenu(locksMenu, "toggle_layer_visibility"); 1232 addActionToMenu(locksMenu, "toggle_layer_lock"); 1233 addActionToMenu(locksMenu, "toggle_layer_inherit_alpha"); 1234 addActionToMenu(locksMenu, "toggle_layer_alpha_lock"); 1235 1236 if (singleNode) { 1237 QMenu *addLayerMenu = menu.addMenu(i18n("&Add")); 1238 addActionToMenu(addLayerMenu, "add_new_transparency_mask"); 1239 addActionToMenu(addLayerMenu, "add_new_filter_mask"); 1240 addActionToMenu(addLayerMenu, "add_new_colorize_mask"); 1241 addActionToMenu(addLayerMenu, "add_new_transform_mask"); 1242 addActionToMenu(addLayerMenu, "add_new_selection_mask"); 1243 addLayerMenu->addSeparator(); 1244 addActionToMenu(addLayerMenu, "add_new_clone_layer"); 1245 1246 QMenu *convertToMenu = menu.addMenu(i18n("&Convert")); 1247 addActionToMenu(convertToMenu, "convert_to_paint_layer"); 1248 addActionToMenu(convertToMenu, "convert_to_transparency_mask"); 1249 addActionToMenu(convertToMenu, "convert_to_filter_mask"); 1250 addActionToMenu(convertToMenu, "convert_to_selection_mask"); 1251 addActionToMenu(convertToMenu, "convert_to_file_layer"); 1252 addActionToMenu(convertToMenu, "convert_to_animated"); 1253 addActionToMenu(convertToMenu, "layercolorspaceconversion"); 1254 1255 QMenu *splitAlphaMenu = menu.addMenu(i18n("S&plit Alpha")); 1256 addActionToMenu(splitAlphaMenu, "split_alpha_into_mask"); 1257 addActionToMenu(splitAlphaMenu, "split_alpha_write"); 1258 addActionToMenu(splitAlphaMenu, "split_alpha_save_merged"); 1259 } else { 1260 QMenu *addLayerMenu = menu.addMenu(i18n("&Add")); 1261 addActionToMenu(addLayerMenu, "add_new_clone_layer"); 1262 } 1263 1264 menu.addSeparator(); 1265 1266 addActionToMenu(&menu, "pin_to_timeline"); 1267 1268 if (singleNode) { 1269 KisNodeSP node = m_filteringModel->nodeFromIndex(index); 1270 if (node && !node->inherits("KisTransformMask")) { 1271 addActionToMenu(&menu, "isolate_active_layer"); 1272 addActionToMenu(&menu, "isolate_active_group"); 1273 } 1274 1275 addActionToMenu(&menu, "selectopaque"); 1276 1277 } 1278 } 1279 } 1280 1281 void LayerBox::slotForgetAboutSavedNodeBeforeEditSelectionMode() 1282 { 1283 m_savedNodeBeforeEditSelectionMode = 0; 1284 } 1285 1286 void LayerBox::slotUpdateIcons() { 1287 m_wdgLayerBox->bnAdd->setIcon(KisIconUtils::loadIcon("addlayer")); 1288 m_wdgLayerBox->bnRaise->setIcon(KisIconUtils::loadIcon("arrowup")); 1289 m_wdgLayerBox->bnDelete->setIcon(KisIconUtils::loadIcon("deletelayer")); 1290 m_wdgLayerBox->bnLower->setIcon(KisIconUtils::loadIcon("arrowdown")); 1291 m_wdgLayerBox->bnProperties->setIcon(KisIconUtils::loadIcon("properties")); 1292 m_wdgLayerBox->bnDuplicate->setIcon(KisIconUtils::loadIcon("duplicatelayer")); 1293 m_wdgLayerBox->configureLayerDockerToolbar->setIcon(KisIconUtils::loadIcon("view-choose")); 1294 1295 // call child function about needing to update icons 1296 m_wdgLayerBox->listLayers->slotUpdateIcons(); 1297 } 1298 1299 void LayerBox::toggleActiveLayerSolo() { 1300 NodeView* view = m_wdgLayerBox->listLayers; 1301 if (!view) 1302 return; 1303 1304 KisNodeSP node = m_nodeManager->activeNode(); 1305 if (!node) 1306 return; 1307 1308 QModelIndex index = m_filteringModel->indexFromNode(node); 1309 if (!index.isValid()) 1310 return; 1311 1312 view->toggleSolo(index); 1313 } 1314 1315 void LayerBox::slotUpdateThumbnailIconSize() 1316 { 1317 KisConfig cfg(false); 1318 cfg.setLayerThumbnailSize(thumbnailSizeSlider->value()); 1319 1320 m_nodeModel->setPreferredThumnalSize(thumbnailSizeSlider->value()); 1321 m_wdgLayerBox->listLayers->slotConfigurationChanged(); 1322 } 1323 1324 void LayerBox::slotUpdateTreeIndentation() 1325 { 1326 KisConfig cfg(false); 1327 if (indentationSlider->value() == cfg.layerTreeIndentation()) { 1328 return; 1329 } 1330 cfg.setLayerTreeIndentation(indentationSlider->value()); 1331 m_wdgLayerBox->listLayers->slotConfigurationChanged(); 1332 } 1333 1334 void LayerBox::slotUpdateLayerInfoTextStyle() 1335 { 1336 KisConfig cfg(false); 1337 if (infoTextCombobox->currentIndex() == cfg.layerInfoTextStyle()) { 1338 return; 1339 } 1340 cfg.setLayerInfoTextStyle((KisConfig::LayerInfoTextStyle)infoTextCombobox->currentIndex()); 1341 m_wdgLayerBox->listLayers->slotConfigurationChanged(); 1342 m_wdgLayerBox->listLayers->viewport()->update(); 1343 if (infoTextCombobox->currentIndex() == 0) { 1344 infoTextOpacitySlider->setDisabled(true); 1345 infoTextInlineChkbox->setDisabled(true); 1346 } 1347 else { 1348 infoTextOpacitySlider->setDisabled(false); 1349 infoTextInlineChkbox->setDisabled(false); 1350 } 1351 } 1352 1353 void LayerBox::slotUpdateLayerInfoTextOpacity() 1354 { 1355 KisConfig cfg(false); 1356 if (infoTextOpacitySlider->value() == cfg.layerInfoTextOpacity()) { 1357 return; 1358 } 1359 cfg.setLayerInfoTextOpacity(infoTextOpacitySlider->value()); 1360 m_wdgLayerBox->listLayers->slotConfigurationChanged(); 1361 m_wdgLayerBox->listLayers->viewport()->update(); 1362 } 1363 1364 void LayerBox::slotUpdateUseInlineLayerInfoText() 1365 { 1366 KisConfig cfg(false); 1367 if (infoTextInlineChkbox->isChecked() == cfg.useInlineLayerInfoText()) { 1368 return; 1369 } 1370 cfg.setUseInlineLayerInfoText(infoTextInlineChkbox->isChecked()); 1371 m_wdgLayerBox->listLayers->slotConfigurationChanged(); 1372 m_wdgLayerBox->listLayers->viewport()->update(); 1373 } 1374 1375 1376 void LayerBox::slotUpdateUseLayerSelectionCheckbox() 1377 { 1378 KisConfig cfg(false); 1379 if (layerSelectionCheckBox->isChecked() == cfg.useLayerSelectionCheckbox()) { 1380 return; 1381 } 1382 cfg.setUseLayerSelectionCheckbox(layerSelectionCheckBox->isChecked()); 1383 m_wdgLayerBox->listLayers->slotConfigurationChanged(); 1384 m_wdgLayerBox->listLayers->viewport()->update(); 1385 } 1386 1387 #include "moc_LayerBox.cpp"