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"