File indexing completed on 2024-12-22 04:12:42

0001 /* This file is part of the KDE project
0002  * SPDX-FileCopyrightText: 2012 Arjen Hiemstra <ahiemstra@heimr.nl>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_select_layer_action.h"
0008 
0009 #include <kis_debug.h>
0010 #include <QMouseEvent>
0011 #include <QApplication>
0012 #include <QMenu>
0013 
0014 #include <klocalizedstring.h>
0015 
0016 #include <kis_canvas2.h>
0017 #include <kis_image.h>
0018 #include <KisViewManager.h>
0019 #include <kis_node_manager.h>
0020 #include <kis_cursor.h>
0021 
0022 #include "kis_input_manager.h"
0023 #include "kis_tool_utils.h"
0024 #include <kis_group_layer.h>
0025 
0026 #include <kis_assert.h>
0027 
0028 class KisSelectLayerAction::Private
0029 {
0030 public:
0031     KisSelectLayerAction *q {nullptr};
0032     int shortcut {makeShortcut(LayerSelectionMode_TopLayer, SelectionOverrideMode_Replace)};
0033 
0034     Private(KisSelectLayerAction *q)
0035         : q(q)
0036     {}
0037 
0038     static int makeShortcut(LayerSelectionMode layerSelectionMode, SelectionOverrideMode selectionOverrideMode)
0039     {
0040         // Store the layer selection mode on the second byte and the selection override mode on the first one
0041         return (layerSelectionMode << 8) | selectionOverrideMode;
0042     }
0043 
0044     static int layerSelectionMode(int shortcut)
0045     {
0046         // Get the layer selection mode from the second byte
0047         return (shortcut >> 8) & 0xFF;
0048     }
0049 
0050     static int selectionOverrideMode(int shortcut)
0051     {
0052         // Get the selection override mode from the first byte
0053         return shortcut & 0xFF;
0054     }
0055 
0056     void selectNodes(const KisNodeList &nodesToSelect, int selectionOverrideMode, bool includeGroups) const
0057     {
0058         KisNodeManager *nodeManager = q->inputManager()->canvas()->viewManager()->nodeManager();
0059         KisNodeSP activeNode = nodeManager->activeNode();
0060         KisNodeList finalSelectedNodes;
0061 
0062         // Make the final list of nodes to select, excluding the group layers,
0063         // if needed
0064         if (includeGroups) {
0065             finalSelectedNodes = nodesToSelect;
0066         } else {
0067             Q_FOREACH(KisNodeSP node, nodesToSelect) {
0068                 if (!dynamic_cast<KisGroupLayer*>(node.data())) {
0069                     finalSelectedNodes.append(node);
0070                 }
0071             }
0072         }
0073 
0074         // Expand the group layers that contain newly selected nodes
0075         Q_FOREACH(KisNodeSP node, finalSelectedNodes) {
0076             KisNodeSP tmpNode = node->parent();
0077             while (tmpNode) {
0078                 if (dynamic_cast<KisGroupLayer*>(tmpNode.data())) {
0079                     tmpNode->setCollapsed(false);
0080                 }
0081                 tmpNode = tmpNode->parent();
0082             }
0083         }
0084 
0085         // Combine the list of nodes with the current selection
0086         if (selectionOverrideMode == SelectionOverrideMode_Add) {
0087             KisNodeList currentlySelectedNodes = nodeManager->selectedNodes();
0088             Q_FOREACH(KisNodeSP node, currentlySelectedNodes) {
0089                 if (!finalSelectedNodes.contains(node)) {
0090                     finalSelectedNodes.append(node);
0091                 }
0092             }
0093         }
0094 
0095         // Try to retain the previously selected node or select the top one otherwise
0096         if (!finalSelectedNodes.contains(activeNode)) {
0097             activeNode = finalSelectedNodes.last();
0098         }
0099 
0100         // Select
0101         nodeManager->slotImageRequestNodeReselection(activeNode, finalSelectedNodes);
0102     }
0103 
0104     void selectNode(KisNodeSP node, int selectionOverrideMode) const
0105     {
0106         KisNodeList nodesToSelect;
0107         nodesToSelect.append(node);
0108         selectNodes(nodesToSelect, selectionOverrideMode, true);
0109     }
0110 };
0111 
0112 KisSelectLayerAction::KisSelectLayerAction()
0113     : KisAbstractInputAction("Select Layer")
0114     , d(new Private(this))
0115 {
0116     setName(i18n("Select Layer"));
0117     setDescription(i18n("Select layers under the cursor position"));
0118 
0119     QHash<QString, int> shortcuts;
0120     shortcuts.insert(i18n("Select Top Layer (Replace Selection)"),
0121                      d->makeShortcut(LayerSelectionMode_TopLayer, SelectionOverrideMode_Replace));
0122     shortcuts.insert(i18n("Select All Layers (Replace Selection)"),
0123                      d->makeShortcut(LayerSelectionMode_AllLayers, SelectionOverrideMode_Replace));
0124     shortcuts.insert(i18n("Select from Menu (Replace Selection)"),
0125                      d->makeShortcut(LayerSelectionMode_Ask, SelectionOverrideMode_Replace));
0126     shortcuts.insert(i18n("Select Top Layer (Add to Selection)"),
0127                      d->makeShortcut(LayerSelectionMode_TopLayer, SelectionOverrideMode_Add));
0128     shortcuts.insert(i18n("Select All Layers (Add to Selection)"),
0129                      d->makeShortcut(LayerSelectionMode_AllLayers, SelectionOverrideMode_Add));
0130     shortcuts.insert(i18n("Select from Menu (Add to Selection)"),
0131                      d->makeShortcut(LayerSelectionMode_Ask, SelectionOverrideMode_Add));
0132     setShortcutIndexes(shortcuts);
0133 }
0134 
0135 KisSelectLayerAction::~KisSelectLayerAction()
0136 {
0137     delete d;
0138 }
0139 
0140 int KisSelectLayerAction::priority() const
0141 {
0142     return 5;
0143 }
0144 
0145 void KisSelectLayerAction::activate(int shortcut)
0146 {
0147     Q_UNUSED(shortcut);
0148     QApplication::setOverrideCursor(KisCursor::pickLayerCursor());
0149 }
0150 
0151 void KisSelectLayerAction::deactivate(int shortcut)
0152 {
0153     Q_UNUSED(shortcut);
0154     QApplication::restoreOverrideCursor();
0155 }
0156 
0157 void KisSelectLayerAction::begin(int shortcut, QEvent *event)
0158 {
0159     KisAbstractInputAction::begin(shortcut, event);
0160 
0161     d->shortcut = shortcut;
0162     inputEvent(event);
0163 }
0164 
0165 void KisSelectLayerAction::inputEvent(QEvent *event)
0166 {
0167     // Event not recognized
0168     if (!event || (event->type() != QEvent::MouseMove && event->type() != QEvent::TabletMove &&
0169                    event->type() != QTouchEvent::TouchUpdate && event->type() != QEvent::MouseButtonPress &&
0170                    event->type() != QEvent::TabletPress && event->type() != QTouchEvent::TouchBegin)) {
0171         return;
0172     }
0173 
0174     const int layerSelectionMode = d->layerSelectionMode(d->shortcut);
0175     const int selectionOverrideMode = d->selectionOverrideMode(d->shortcut);
0176 
0177     // Shortcut not recognized
0178     KIS_SAFE_ASSERT_RECOVER_RETURN(
0179         (layerSelectionMode == LayerSelectionMode_TopLayer ||
0180          layerSelectionMode == LayerSelectionMode_AllLayers ||
0181          layerSelectionMode == LayerSelectionMode_Ask) &&
0182         (selectionOverrideMode == SelectionOverrideMode_Replace ||
0183          selectionOverrideMode == SelectionOverrideMode_Add)
0184     );
0185 
0186     QPoint pos =
0187         inputManager()->canvas()->
0188         coordinatesConverter()->widgetToImage(eventPosF(event)).toPoint();
0189 
0190     // First make a list with the nodes to be selected
0191     KisNodeList nodesToSelect;
0192 
0193     if (layerSelectionMode == LayerSelectionMode_TopLayer) {
0194         KisNodeSP foundNode = KisToolUtils::findNode(inputManager()->canvas()->image()->root(), pos, false);
0195         if (!foundNode) {
0196             return;
0197         }
0198         nodesToSelect.append(foundNode);
0199     } else {
0200         // Retrieve group nodes only if the mode is LayerSelectionMode_Ask
0201         const KisNodeList foundNodes = KisToolUtils::findNodes(
0202                                             inputManager()->canvas()->image()->root()->firstChild(),
0203                                             pos, false, layerSelectionMode == LayerSelectionMode_Ask);
0204 
0205         if (foundNodes.isEmpty()) {
0206             return;
0207         }
0208 
0209         if (layerSelectionMode == LayerSelectionMode_AllLayers) {
0210             nodesToSelect = foundNodes;
0211         } else { //LayerSelectionMode_Ask
0212             QWidget *canvasWidget = inputManager()->canvas()->canvasWidget();
0213             QMenu *menu = new QMenu(canvasWidget);
0214             menu->setAttribute(Qt::WA_DeleteOnClose);
0215             int numberOfLayers = 0;
0216 
0217             // Traverse the list in reverse order so that the menu entries order
0218             // resembles that of the layer stack
0219             for (int i = foundNodes.size() - 1; i >= 0; --i) {
0220                 KisNodeSP node = foundNodes[i];
0221                 int indentation = -1;
0222                 {
0223                     KisNodeSP tempNode = node;
0224                     while (tempNode->parent()) {
0225                         ++indentation;
0226                         tempNode = tempNode->parent();
0227                     }
0228                 }
0229                 QAction *action = menu->addAction(QString(4 * indentation, ' ') + node->name());
0230                 QObject::connect(action, &QAction::triggered,
0231                     [this, node, selectionOverrideMode]()
0232                     {
0233                         d->selectNode(node, selectionOverrideMode);
0234                     }
0235                 );
0236                 if (!dynamic_cast<KisGroupLayer*>(node.data())) {
0237                     ++numberOfLayers;
0238                 }
0239             }
0240 
0241             // Add separator
0242             menu->addSeparator();
0243 
0244             // Add "select all layers" menu item
0245             {
0246                 QAction *action = menu->addAction(i18nc("Menu entry for the select layer under cursor canvas input action",
0247                                                         "Select all layers"));
0248                 action->setVisible(numberOfLayers > 1);
0249                 QObject::connect(action, &QAction::triggered,
0250                     [this, foundNodes, selectionOverrideMode]()
0251                     {
0252                         d->selectNodes(foundNodes, selectionOverrideMode, false);
0253                     }
0254                 );
0255             }
0256 
0257             menu->popup(canvasWidget->mapToGlobal(eventPos(event)));
0258             return;
0259         }
0260     }
0261 
0262     // Now select the nodes
0263     d->selectNodes(nodesToSelect, selectionOverrideMode, true);
0264 }