kis_layer_box.cpp 27.3 KB
Newer Older
1 2 3 4 5
/*
 *  kis_layer_box.cc - part of Krita aka Krayon aka KimageShop
 *
 *  Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
 *  Copyright (C) 2006 Gábor Lehel <illissius@gmail.com>
6
 *  Copyright (C) 2007 Thomas Zander <zander@kde.org>
7
 *  Copyright (C) 2007 Boudewijn Rempt <boud@valdyas.org>
8
 *  Copyright (c) 2011 José Luis Vergara <pentalis@gmail.com>
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "kis_layer_box.h"

Boudewijn Rempt's avatar
Boudewijn Rempt committed
27

28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
#include <QToolButton>
#include <QLayout>
#include <QMouseEvent>
#include <QPainter>
#include <QPoint>
#include <QRect>
#include <QString>
#include <QToolTip>
#include <QWidget>
#include <QComboBox>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QPixmap>
#include <QList>
#include <QVector>
43
#include <QLabel>
44

45
#include <kis_debug.h>
46 47 48
#include <kmenu.h>
#include <klocale.h>

49
#include <KoIcon.h>
50
#include <KisDocumentSectionView.h>
51
#include <KoColorSpace.h>
52
#include <KoCompositeOpRegistry.h>
53
#include <KisDocument.h>
54 55 56 57 58 59 60

#include <kis_types.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include <kis_layer.h>
#include <kis_group_layer.h>
#include <kis_mask.h>
61
#include <kis_node.h>
62
#include <kis_composite_ops_model.h>
63

64 65
#include "kis_action.h"
#include "kis_action_manager.h"
66
#include "widgets/kis_cmb_composite.h"
67
#include "widgets/kis_slider_spin_box.h"
68
#include "KisViewManager.h"
69
#include "kis_node_manager.h"
70
#include "kis_node_model.h"
71
#include "canvas/kis_canvas2.h"
72
#include "KisDocument.h"
73
#include "kis_dummies_facade_base.h"
74
#include "kis_shape_controller.h"
75 76
#include "kis_selection_mask.h"
#include "kis_config.h"
77
#include "KisView.h"
78

79
#include "ui_wdglayerbox.h"
80

81 82 83
class ButtonAction : public KisAction
{
public:
84 85 86
    ButtonAction(QAbstractButton* button, const KIcon& icon, const QString& text, QObject* parent)
        : KisAction(icon, text, parent)
        , m_button(button)
87 88 89 90
    {
        connect(m_button, SIGNAL(clicked()), this, SLOT(trigger()));
    }

91 92 93 94 95 96 97 98 99 100 101 102
    ButtonAction(QAbstractButton* button, QObject* parent) : KisAction(parent) , m_button(button)
    {
        connect(m_button, SIGNAL(clicked()), this, SLOT(trigger()));
    }

    virtual void setActionEnabled(bool enabled) {
        KisAction::setActionEnabled(enabled);
        m_button->setEnabled(enabled);
    }
private:
    QAbstractButton* m_button;
};
103

104
inline void KisLayerBox::connectActionToButton(KisViewManager* view, QAbstractButton *button, const QString &id)
105
{
106 107
    Q_ASSERT(view);
    KisAction *action = view->actionManager()->actionByName(id);
108 109 110 111 112 113 114 115

    connect(button, SIGNAL(clicked()), action, SLOT(trigger()));
    connect(action, SIGNAL(sigEnableSlaves(bool)), button, SLOT(setEnabled(bool)));
}

inline void KisLayerBox::addActionToMenu(QMenu *menu, const QString &id)
{
    Q_ASSERT(m_canvas);
116
    menu->addAction(m_canvas->viewManager()->actionManager()->actionByName(id));
117 118
}

119
KisLayerBox::KisLayerBox()
Boudewijn Rempt's avatar
Boudewijn Rempt committed
120 121 122
        : QDockWidget(i18n("Layers"))
        , m_canvas(0)
        , m_wdgLayerBox(new Ui_WdgLayerBox)
123
{
124 125
    KisConfig cfg;

126 127 128 129
    setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);

    QWidget* mainWidget = new QWidget(this);
    setWidget(mainWidget);
130
    m_opacityDelayTimer.setSingleShot(true);
131

132
    m_wdgLayerBox->setupUi(mainWidget);
133

134
    m_wdgLayerBox->listLayers->setDefaultDropAction(Qt::MoveAction);
135
    m_wdgLayerBox->listLayers->setSelectionMode(QAbstractItemView::ExtendedSelection);
136
    m_wdgLayerBox->listLayers->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
137
    m_wdgLayerBox->listLayers->setSelectionBehavior(QAbstractItemView::SelectRows);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
138

139
    connect(m_wdgLayerBox->listLayers, SIGNAL(contextMenuRequested(const QPoint&, const QModelIndex&)),
140
            this, SLOT(slotContextMenuRequested(const QPoint&, const QModelIndex&)));
141 142
    connect(m_wdgLayerBox->listLayers, SIGNAL(collapsed(const QModelIndex&)), SLOT(slotCollapsed(const QModelIndex &)));
    connect(m_wdgLayerBox->listLayers, SIGNAL(expanded(const QModelIndex&)), SLOT(slotExpanded(const QModelIndex &)));
143
    connect(m_wdgLayerBox->listLayers, SIGNAL(selectionChanged(const QModelIndexList&)), SLOT(selectionChanged(const QModelIndexList&)));
144

Boudewijn Rempt's avatar
Boudewijn Rempt committed
145 146
    m_viewModeMenu = new KMenu(this);
    QActionGroup *group = new QActionGroup(this);
147 148
    QList<QAction*> actions;

149
    actions << m_viewModeMenu->addAction(themedIcon("view-list-text"),
Boudewijn Rempt's avatar
Boudewijn Rempt committed
150
                                         i18n("Minimal View"), this, SLOT(slotMinimalView()));
151
    actions << m_viewModeMenu->addAction(themedIcon("view-list-details"),
Boudewijn Rempt's avatar
Boudewijn Rempt committed
152
                                         i18n("Detailed View"), this, SLOT(slotDetailedView()));
153
    actions << m_viewModeMenu->addAction(themedIcon("view-preview"),
Boudewijn Rempt's avatar
Boudewijn Rempt committed
154
                                         i18n("Thumbnail View"), this, SLOT(slotThumbnailView()));
155

Boudewijn Rempt's avatar
Boudewijn Rempt committed
156 157 158
    for (int i = 0, n = actions.count(); i < n; ++i) {
        actions[i]->setCheckable(true);
        actions[i]->setActionGroup(group);
159 160
    }

161
    m_wdgLayerBox->bnAdd->setIcon(themedIcon("addlayer"));
162

163 164
    m_wdgLayerBox->bnViewMode->setMenu(m_viewModeMenu);
    m_wdgLayerBox->bnViewMode->setPopupMode(QToolButton::InstantPopup);
165
    m_wdgLayerBox->bnViewMode->setIcon(themedIcon("view-choose"));
166
    m_wdgLayerBox->bnViewMode->setText(i18n("View mode"));
167

168
    m_wdgLayerBox->bnDelete->setIcon(themedIcon("deletelayer"));
169
    m_wdgLayerBox->bnDelete->setIconSize(QSize(22, 22));
Boudewijn Rempt's avatar
Boudewijn Rempt committed
170

171
    m_wdgLayerBox->bnRaise->setEnabled(false);
172
    m_wdgLayerBox->bnRaise->setIcon(themedIcon("arrowupblr"));
173
    m_wdgLayerBox->bnRaise->setIconSize(QSize(22, 22));
174

175
    m_wdgLayerBox->bnLower->setEnabled(false);
176
    m_wdgLayerBox->bnLower->setIcon(themedIcon("arrowdown"));
177
    m_wdgLayerBox->bnLower->setIconSize(QSize(22, 22));
178

179
    m_wdgLayerBox->bnLeft->setEnabled(true);
180
    m_wdgLayerBox->bnLeft->setIcon(themedIcon("removefromfolder"));
181
    m_wdgLayerBox->bnLeft->setIconSize(QSize(22, 22));
182

183
    m_wdgLayerBox->bnRight->setEnabled(true);
184
    m_wdgLayerBox->bnRight->setIcon(themedIcon("addtofolder"));
185
    m_wdgLayerBox->bnRight->setIconSize(QSize(22, 22));
186

187
    m_wdgLayerBox->bnProperties->setIcon(themedIcon("properties"));
188
    m_wdgLayerBox->bnProperties->setIconSize(QSize(22, 22));
Boudewijn Rempt's avatar
Boudewijn Rempt committed
189

190
    m_wdgLayerBox->bnDuplicate->setIcon(themedIcon("duplicatelayer"));
191
    m_wdgLayerBox->bnDuplicate->setIconSize(QSize(22, 22));
192

193
    m_removeAction  = new ButtonAction(m_wdgLayerBox->bnDelete, themedIcon("deletelayer"), i18n("&Remove Layer"), this);
194 195
    m_removeAction->setActivationFlags(KisAction::ACTIVE_NODE);
    m_removeAction->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE);
196
    m_removeAction->setObjectName("remove_layer");
197 198
    connect(m_removeAction, SIGNAL(triggered()), this, SLOT(slotRmClicked()));
    m_actions.append(m_removeAction);
199

200
    KisAction* action  = new ButtonAction(m_wdgLayerBox->bnLeft, this);
201
    action->setText(i18n("Move Layer Left"));
202 203
    action->setActivationFlags(KisAction::ACTIVE_NODE);
    action->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE);
204
    action->setObjectName("move_layer_left");
205 206 207 208
    connect(action, SIGNAL(triggered()), this, SLOT(slotLeftClicked()));
    m_actions.append(action);

    action  = new ButtonAction(m_wdgLayerBox->bnRight, this);
209
    action->setText(i18n("Move Layer Right"));
210 211
    action->setActivationFlags(KisAction::ACTIVE_NODE);
    action->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE);
212
    action->setObjectName("move_layer_right");
213 214 215
    connect(action, SIGNAL(triggered()), this, SLOT(slotRightClicked()));
    m_actions.append(action);

216
    m_propertiesAction  = new ButtonAction(m_wdgLayerBox->bnProperties, themedIcon("properties"), i18n("&Properties..."),this);
217 218
    m_propertiesAction->setActivationFlags(KisAction::ACTIVE_NODE);
    m_propertiesAction->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE);
219
    m_propertiesAction->setObjectName("layer_properties");
220 221
    connect(m_propertiesAction, SIGNAL(triggered()), this, SLOT(slotPropertiesClicked()));
    m_actions.append(m_propertiesAction);
222

223 224 225 226 227
    // NOTE: this is _not_ a mistake. The layerbox shows the layers in the reverse order
    connect(m_wdgLayerBox->bnRaise, SIGNAL(clicked()), SLOT(slotLowerClicked()));
    connect(m_wdgLayerBox->bnLower, SIGNAL(clicked()), SLOT(slotRaiseClicked()));
    // END NOTE

228 229 230 231
    if (cfg.sliderLabels()) {
        m_wdgLayerBox->opacityLabel->hide();
        m_wdgLayerBox->doubleOpacity->setPrefix(QString("%1:  ").arg(i18n("Opacity")));
    }
232
    m_wdgLayerBox->doubleOpacity->setRange(0, 100, 0);
233 234
    m_wdgLayerBox->doubleOpacity->setSuffix("%");

235
    connect(m_wdgLayerBox->doubleOpacity, SIGNAL(valueChanged(qreal)), SLOT(slotOpacitySliderMoved(qreal)));
236
    connect(&m_opacityDelayTimer, SIGNAL(timeout()), SLOT(slotOpacityChanged()));
237

238
    connect(m_wdgLayerBox->cmbComposite, SIGNAL(activated(int)), SLOT(slotCompositeOpChanged(int)));
239

240 241
    m_selectOpaque = new KisAction(i18n("&Select Opaque"), this);
    m_selectOpaque->setActivationFlags(KisAction::ACTIVE_LAYER);
242
    m_selectOpaque->setObjectName("select_opaque");
243
    connect(m_selectOpaque, SIGNAL(triggered(bool)), this, SLOT(slotSelectOpaque()));
244
    m_actions.append(m_selectOpaque);
245

246 247 248 249
    m_newLayerMenu = new KMenu(this);
    m_wdgLayerBox->bnAdd->setMenu(m_newLayerMenu);
    m_wdgLayerBox->bnAdd->setPopupMode(QToolButton::MenuButtonPopup);

250
    m_nodeModel = new KisNodeModel(this);
251

252 253
    /**
     * Connect model updateUI() to enable/disable controls.
254
     * Note: nodeActivated() is connected separately in setImage(), because
255 256 257
     *       it needs particular order of calls: first the connection to the
     *       node manager should be called, then updateUI()
     */
258 259 260
    connect(m_nodeModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(updateUI()));
    connect(m_nodeModel, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SLOT(updateUI()));
    connect(m_nodeModel, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)), SLOT(updateUI()));
261
    connect(m_nodeModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), SLOT(updateUI()));
262 263
    connect(m_nodeModel, SIGNAL(modelReset()), SLOT(updateUI()));

264 265 266 267 268 269 270 271 272
    KisAction *showGlobalSelectionMask = new KisAction(i18n("&Show Global Selection Mask"), this);
    showGlobalSelectionMask->setObjectName("show-global-selection-mask");
    showGlobalSelectionMask->setToolTip(i18nc("@info:tooltip", "Shows global selection as a usual selection mask in <interface>Layers</interface> docker"));
    showGlobalSelectionMask->setCheckable(true);
    connect(showGlobalSelectionMask, SIGNAL(triggered(bool)), SLOT(slotEditGlobalSelection(bool)));
    m_actions.append(showGlobalSelectionMask);

    showGlobalSelectionMask->setChecked(cfg.showGlobalSelection());

273
    m_wdgLayerBox->listLayers->setModel(m_nodeModel);
274 275 276 277
}

KisLayerBox::~KisLayerBox()
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
278
    delete m_wdgLayerBox;
279 280
}

281

282
void expandNodesRecursively(KisNodeSP root, QPointer<KisNodeModel> nodeModel, KisDocumentSectionView *sectionView)
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
{
    if (!root) return;
    if (nodeModel.isNull()) return;
    if (!sectionView) return;

    sectionView->blockSignals(true);

    KisNodeSP node = root->firstChild();
    while (node) {
        QModelIndex idx = nodeModel->indexFromNode(node);
        if (idx.isValid()) {
            if (node->collapsed()) {
                sectionView->collapse(idx);
            }
        }
        if (node->childCount() > 0) {
            expandNodesRecursively(node, nodeModel, sectionView);
        }
        node = node->nextSibling();
    }
    sectionView->blockSignals(false);
}

306
void KisLayerBox::setMainWindow(KisViewManager* kisview)
307
{
308
    m_nodeManager = kisview->nodeManager();
309

310 311 312 313 314 315 316

    foreach(KisAction *action, m_actions) {
        kisview->actionManager()->
            addAction(action->objectName(),
                      action);
    }

317 318 319 320 321 322
    connectActionToButton(kisview, m_wdgLayerBox->bnAdd, "add_new_paint_layer");
    connectActionToButton(kisview, m_wdgLayerBox->bnDuplicate, "duplicatelayer");
}

void KisLayerBox::setCanvas(KoCanvasBase *canvas)
{
323
    setEnabled(canvas != 0);
324
    if (m_canvas) {
325
        m_canvas->disconnectCanvasObserver(this);
326
        m_nodeModel->setDummiesFacade(0, 0, 0);
327

328 329 330 331
        disconnect(m_image, 0, this, 0);
        disconnect(m_nodeManager, 0, this, 0);
        disconnect(m_nodeModel, 0, m_nodeManager, 0);
        disconnect(m_nodeModel, SIGNAL(nodeActivated(KisNodeSP)), this, SLOT(updateUI()));
332
    }
333

334
    m_canvas = dynamic_cast<KisCanvas2*>(canvas);
335

336
    if (m_canvas) {
337
        m_image = m_canvas->image();
338

339 340
        connect(m_image, SIGNAL(sigImageUpdated(QRect)), SLOT(updateThumbnail()));

341 342
        KisDocument* doc = static_cast<KisDocument*>(m_canvas->imageView()->document());
        KisShapeController *kritaShapeController = dynamic_cast<KisShapeController*>(doc->shapeController());
343 344
        KisDummiesFacadeBase *kritaDummiesFacade = static_cast<KisDummiesFacadeBase*>(kritaShapeController);
        m_nodeModel->setDummiesFacade(kritaDummiesFacade, m_image, kritaShapeController);
345

346
        connect(m_image, SIGNAL(sigAboutToBeDeleted()), SLOT(notifyImageDeleted()));
347
        connect(m_image, SIGNAL(sigNodeCollapsedChanged()), SLOT(slotNodeCollapsedChanged()));
348

349
        // cold start
350 351 352 353 354 355
        if (m_nodeManager) {
            setCurrentNode(m_nodeManager->activeNode());
        }
        else {
            setCurrentNode(m_canvas->imageView()->currentNode());
        }
356 357

        // Connection KisNodeManager -> KisLayerBox
358
        connect(m_nodeManager, SIGNAL(sigUiNeedChangeActiveNode(KisNodeSP)), this, SLOT(setCurrentNode(KisNodeSP)));
359 360 361

        // Connection KisLayerBox -> KisNodeManager
        // The order of these connections is important! See comment in the ctor
362
        connect(m_nodeModel, SIGNAL(nodeActivated(KisNodeSP)), m_nodeManager, SLOT(slotUiActivatedNode(KisNodeSP)));
363
        connect(m_nodeModel, SIGNAL(nodeActivated(KisNodeSP)), SLOT(updateUI()));
364

365 366 367
        // Connection KisLayerBox -> KisNodeManager (isolate layer)
        connect(m_nodeModel, SIGNAL(toggleIsolateActiveNode()), m_nodeManager, SLOT(toggleIsolateActiveNode()));

368
        // Node manipulation methods are forwarded to the node manager
369 370 371 372
        connect(m_nodeModel, SIGNAL(requestAddNode(KisNodeSP, KisNodeSP, KisNodeSP)),
                m_nodeManager, SLOT(addNodeDirect(KisNodeSP, KisNodeSP, KisNodeSP)));
        connect(m_nodeModel, SIGNAL(requestMoveNode(KisNodeSP, KisNodeSP, KisNodeSP)),
                m_nodeManager, SLOT(moveNodeDirect(KisNodeSP, KisNodeSP, KisNodeSP)));
373 374 375

        m_wdgLayerBox->listLayers->expandAll();
        expandNodesRecursively(m_image->rootLayer(), m_nodeModel, m_wdgLayerBox->listLayers);
376
        m_wdgLayerBox->listLayers->scrollTo(m_wdgLayerBox->listLayers->currentIndex());
377 378 379 380 381 382

        addActionToMenu(m_newLayerMenu, "add_new_paint_layer");
        addActionToMenu(m_newLayerMenu, "add_new_group_layer");
        addActionToMenu(m_newLayerMenu, "add_new_clone_layer");
        addActionToMenu(m_newLayerMenu, "add_new_shape_layer");
        addActionToMenu(m_newLayerMenu, "add_new_adjustment_layer");
383
        addActionToMenu(m_newLayerMenu, "add_new_fill_layer");
384 385 386 387
        addActionToMenu(m_newLayerMenu, "add_new_file_layer");
        m_newLayerMenu->addSeparator();
        addActionToMenu(m_newLayerMenu, "add_new_transparency_mask");
        addActionToMenu(m_newLayerMenu, "add_new_filter_mask");
388
        addActionToMenu(m_newLayerMenu, "add_new_transform_mask");
389
        addActionToMenu(m_newLayerMenu, "add_new_selection_mask");
390
    }
391

392 393
}

394 395 396

void KisLayerBox::unsetCanvas()
{
397
    setEnabled(false);
398
    if (m_canvas) {
399
        m_newLayerMenu->clear();
400
    }
401
    setCanvas(0);
402
    m_nodeManager->setSelectedNodes(QList<KisNodeSP>());
403 404
}

405 406
void KisLayerBox::notifyImageDeleted()
{
407
    setCanvas(0);
408
    m_nodeManager->setSelectedNodes(QList<KisNodeSP>());
409
}
410

411 412
void KisLayerBox::updateUI()
{
413 414
    if (!m_canvas) return;
    if (!m_nodeManager) return;
415

416
    KisNodeSP activeNode = m_nodeManager->activeNode();
417

418
    m_wdgLayerBox->bnRaise->setEnabled(activeNode && activeNode->isEditable(false) && (activeNode->nextSibling()
419
                                       || (activeNode->parent() && activeNode->parent() != m_image->root())));
420
    m_wdgLayerBox->bnLower->setEnabled(activeNode && activeNode->isEditable(false) && (activeNode->prevSibling()
421
                                       || (activeNode->parent() && activeNode->parent() != m_image->root())));
422

423
    m_wdgLayerBox->doubleOpacity->setEnabled(activeNode && activeNode->isEditable(false));
424

425
    m_wdgLayerBox->cmbComposite->setEnabled(activeNode && activeNode->isEditable(false));
426

427
    if (activeNode) {
428
        if (m_nodeManager->activePaintDevice()) {
429
            slotFillCompositeOps(m_nodeManager->activeColorSpace());
430
        } else {
431
            slotFillCompositeOps(m_image->colorSpace());
432 433
        }

434 435 436
        if (activeNode->inherits("KisMask")) {
            m_wdgLayerBox->cmbComposite->setEnabled(false);
            m_wdgLayerBox->doubleOpacity->setEnabled(false);
437
        }
438 439 440 441 442 443

        if (activeNode->inherits("KisLayer")) {
            m_wdgLayerBox->cmbComposite->setEnabled(true);
            m_wdgLayerBox->doubleOpacity->setEnabled(true);

            KisLayerSP l = qobject_cast<KisLayer*>(activeNode.data());
444
            slotSetOpacity(l->opacity() * 100.0 / 255);
445 446

            const KoCompositeOp* compositeOp = l->compositeOp();
447
            if (compositeOp) {
448 449 450 451
                slotSetCompositeOp(compositeOp);
            } else {
                m_wdgLayerBox->cmbComposite->setEnabled(false);
            }
452
        }
453 454 455
    }
}

456 457 458 459 460

/**
 * This method is callen *only* when non-GUI code requested the
 * change of the current node
 */
Boudewijn Rempt's avatar
Boudewijn Rempt committed
461
void KisLayerBox::setCurrentNode(KisNodeSP node)
462
{
463 464
    QModelIndex index = node ? m_nodeModel->indexFromNode(node) : QModelIndex();

465
    m_wdgLayerBox->listLayers->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
466
    updateUI();
467 468 469 470
}

void KisLayerBox::slotSetCompositeOp(const KoCompositeOp* compositeOp)
{
471 472
    KoID opId = KoCompositeOpRegistry::instance().getKoID(compositeOp->id());

473
    m_wdgLayerBox->cmbComposite->blockSignals(true);
474
    m_wdgLayerBox->cmbComposite->selectCompositeOp(opId);
475
    m_wdgLayerBox->cmbComposite->blockSignals(false);
476 477
}

478
void KisLayerBox::slotFillCompositeOps(const KoColorSpace* colorSpace)
479
{
480
    m_wdgLayerBox->cmbComposite->validate(colorSpace);
481 482 483 484 485
}

// range: 0-100
void KisLayerBox::slotSetOpacity(double opacity)
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
486
    Q_ASSERT(opacity >= 0 && opacity <= 100);
487 488 489
    m_wdgLayerBox->doubleOpacity->blockSignals(true);
    m_wdgLayerBox->doubleOpacity->setValue(opacity);
    m_wdgLayerBox->doubleOpacity->blockSignals(false);
490 491 492 493 494
}

void KisLayerBox::slotContextMenuRequested(const QPoint &pos, const QModelIndex &index)
{
    QMenu menu;
495

Boudewijn Rempt's avatar
Boudewijn Rempt committed
496
    if (index.isValid()) {
497
        menu.addAction(m_propertiesAction);
498
        menu.addSeparator();
499
        menu.addAction(m_removeAction);
500 501

        addActionToMenu(&menu, "duplicatelayer");
502
        addActionToMenu(&menu, "flatten_image");
503
        addActionToMenu(&menu, "flatten_layer");
504

505 506
        // TODO: missing icon "edit-merge"
        QAction* mergeLayerDown = menu.addAction(i18n("&Merge with Layer Below"), this, SLOT(slotMergeLayer()));
507
        if (!index.sibling(index.row() + 1, 0).isValid()) mergeLayerDown->setEnabled(false);
508
        menu.addSeparator();
509 510 511 512 513

        QMenu *convertToMenu = menu.addMenu(i18n("&Convert"));
        addActionToMenu(convertToMenu, "convert_to_paint_layer");
        addActionToMenu(convertToMenu, "convert_to_transparency_mask");
        addActionToMenu(convertToMenu, "convert_to_filter_mask");
514
        addActionToMenu(convertToMenu, "convert_to_transform_mask");
515 516
        addActionToMenu(convertToMenu, "convert_to_selection_mask");

517 518 519 520 521
        QMenu *splitAlphaMenu = menu.addMenu(i18n("S&plit Alpha"));
        addActionToMenu(splitAlphaMenu, "split_alpha_into_mask");
        addActionToMenu(splitAlphaMenu, "split_alpha_write");
        addActionToMenu(splitAlphaMenu, "split_alpha_save_merged");

522
        addActionToMenu(&menu, "isolate_layer");
523
    }
524
    menu.addSeparator();
525 526
    addActionToMenu(&menu, "add_new_transparency_mask");
    addActionToMenu(&menu, "add_new_filter_mask");
527
    addActionToMenu(&menu, "add_new_transform_mask");
528
    addActionToMenu(&menu, "add_new_selection_mask");
529 530
    menu.addSeparator();
    menu.addAction(m_selectOpaque);
531 532 533
    menu.exec(pos);
}

534
void KisLayerBox::slotMergeLayer()
535
{
536
    if (!m_canvas) return;
537
    m_nodeManager->mergeLayerDown();
538 539
}

540 541
void KisLayerBox::slotMinimalView()
{
542
    m_wdgLayerBox->listLayers->setDisplayMode(KisDocumentSectionView::MinimalMode);
543 544 545 546
}

void KisLayerBox::slotDetailedView()
{
547
    m_wdgLayerBox->listLayers->setDisplayMode(KisDocumentSectionView::DetailedMode);
548 549 550 551
}

void KisLayerBox::slotThumbnailView()
{
552
    m_wdgLayerBox->listLayers->setDisplayMode(KisDocumentSectionView::ThumbnailMode);
553 554 555 556
}

void KisLayerBox::slotRmClicked()
{
557
    if (!m_canvas) return;
558
    m_nodeManager->removeNode();
559 560 561 562
}

void KisLayerBox::slotRaiseClicked()
{
563
    if (!m_canvas) return;
564
    KisNodeSP node = m_nodeManager->activeNode();
565
    KisNodeSP parent = node->parent();
566 567 568
    KisNodeSP grandParent = parent->parent();

    if (!m_nodeManager->activeNode()->prevSibling()) {
569
        if (!grandParent) return;
570
        if (!grandParent->parent() && node->inherits("KisMask")) return;
571
        m_nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent));
572 573 574
    } else {
        m_nodeManager->raiseNode();
    }
575 576 577 578
}

void KisLayerBox::slotLowerClicked()
{
579
    if (!m_canvas) return;
580 581 582
    KisNodeSP node = m_nodeManager->activeNode();
    KisNodeSP parent = node->parent();
    KisNodeSP grandParent = parent->parent();
583

584
    if (!m_nodeManager->activeNode()->nextSibling()) {
585
        if (!grandParent) return;
586 587
        if (!grandParent->parent() && node->inherits("KisMask")) return;
        m_nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent) + 1);
588 589
    }
    else {
590 591
        m_nodeManager->lowerNode();
    }
592 593
}

594 595
void KisLayerBox::slotLeftClicked()
{
596 597 598 599 600
    if (!m_canvas) return;
    foreach(KisNodeSP node, m_nodeManager->selectedNodes()) {
        KisNodeSP parent = node->parent();
        KisNodeSP grandParent = parent->parent();
        quint16 nodeIndex = parent->index(node);
601

602 603 604 605 606 607 608 609 610
        if (!grandParent) continue;
        if (!grandParent->parent() && node->inherits("KisMask")) continue;

        if (nodeIndex <= parent->childCount() / 2) {
            m_nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent));
        }
        else {
            m_nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent) + 1);
        }
611
    }
612 613 614 615
}

void KisLayerBox::slotRightClicked()
{
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
    if (!m_canvas) return;

    foreach(KisNodeSP node, m_nodeManager->selectedNodes()) {
        KisNodeSP parent = m_nodeManager->activeNode()->parent();
        KisNodeSP newParent;
        int nodeIndex = parent->index(node);
        int indexAbove = nodeIndex + 1;
        int indexBelow = nodeIndex - 1;

        if (parent->at(indexBelow) && parent->at(indexBelow)->allowAsChild(node)) {
            newParent = parent->at(indexBelow);
            m_nodeManager->moveNodeAt(node, newParent, newParent->childCount());
        }
        else if (parent->at(indexAbove) && parent->at(indexAbove)->allowAsChild(node)) {
            newParent = parent->at(indexAbove);
            m_nodeManager->moveNodeAt(node, newParent, 0);
        }
633
    }
634 635
}

636 637
void KisLayerBox::slotPropertiesClicked()
{
638
    if (!m_canvas) return;
639 640 641
    if (KisNodeSP active = m_nodeManager->activeNode()) {
        m_nodeManager->nodeProperties(active);
    }
642 643
}

644
void KisLayerBox::slotCompositeOpChanged(int index)
645
{
646
    Q_UNUSED(index);
647
    if (!m_canvas) return;
648

649 650
    QString compositeOp = m_wdgLayerBox->cmbComposite->selectedCompositeOp().id();
    m_nodeManager->nodeCompositeOpChanged(m_nodeManager->activeColorSpace()->compositeOp(compositeOp));
651 652
}

653
void KisLayerBox::slotOpacityChanged()
654
{
655
    if (!m_canvas) return;
656
    m_nodeManager->nodeOpacityChanged(m_newOpacity, true);
657 658
}

659 660 661
void KisLayerBox::slotOpacitySliderMoved(qreal opacity)
{
    m_newOpacity = opacity;
662
    m_opacityDelayTimer.start(200);
663 664
}

665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
void KisLayerBox::slotCollapsed(const QModelIndex &index)
{
    KisNodeSP node = m_nodeModel->nodeFromIndex(index);
    if (node) {
        node->setCollapsed(true);
    }
}

void KisLayerBox::slotExpanded(const QModelIndex &index)
{
    KisNodeSP node = m_nodeModel->nodeFromIndex(index);
    if (node) {
        node->setCollapsed(false);
    }
}
680

681 682
void KisLayerBox::slotSelectOpaque()
{
683
    if (!m_canvas) return;
684
    QAction *action = m_canvas->viewManager()->actionManager()->actionByName("selectopaque");
685 686 687 688 689
    if (action) {
        action->trigger();
    }
}

690 691 692 693 694 695
void KisLayerBox::slotNodeCollapsedChanged()
{
    m_wdgLayerBox->listLayers->expandAll();
    expandNodesRecursively(m_image->rootLayer(), m_nodeModel, m_wdgLayerBox->listLayers);
}

696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
inline bool isSelectionMask(KisNodeSP node)
{
    return dynamic_cast<KisSelectionMask*>(node.data());
}

KisNodeSP KisLayerBox::findNonHidableNode(KisNodeSP startNode)
{
    if (isSelectionMask(startNode) &&
        startNode->parent() &&
        !startNode->parent()->parent()) {


        KisNodeSP node = startNode->prevSibling();
        while (node && isSelectionMask(node)) {
            node = node->prevSibling();
        }

        if (!node) {
            node = startNode->nextSibling();
            while (node && isSelectionMask(node)) {
                node = node->nextSibling();
            }
        }

        if (!node) {
            node = m_image->root()->lastChild();
            while (node && isSelectionMask(node)) {
                node = node->prevSibling();
            }
        }

        KIS_ASSERT_RECOVER_NOOP(node && "cannot activate any node!");
        startNode = node;
    }

    return startNode;
}

void KisLayerBox::slotEditGlobalSelection(bool showSelections)
{
    KisNodeSP lastActiveNode = m_nodeManager->activeNode();
    KisNodeSP activateNode = lastActiveNode;

    if (!showSelections) {
        activateNode = findNonHidableNode(activateNode);
    }

    m_nodeModel->setShowGlobalSelection(showSelections);

    if (showSelections) {
        KisNodeSP newMask = m_image->rootLayer()->selectionMask();
        if (newMask) {
            activateNode = newMask;
        }
    }

    if (activateNode) {
        if (lastActiveNode != activateNode) {
            m_nodeManager->slotNonUiActivatedNode(activateNode);
        } else {
            setCurrentNode(lastActiveNode);
        }
    }
}
760

761 762
void KisLayerBox::selectionChanged(const QModelIndexList selection)
{
763 764
    if (!m_nodeManager) return;

765
    if (selection.isEmpty() && m_nodeManager->activeNode()) {
766 767 768 769
        m_wdgLayerBox->listLayers->selectionModel()->
            setCurrentIndex(
                m_nodeModel->indexFromNode(m_nodeManager->activeNode()),
                QItemSelectionModel::ClearAndSelect);
770 771 772 773
        return;
    }


774 775 776 777
    QList<KisNodeSP> selectedNodes;
    foreach(const QModelIndex &idx, selection) {
        selectedNodes << m_nodeModel->nodeFromIndex(idx);
    }
778 779


780
    m_nodeManager->setSelectedNodes(selectedNodes);
781
    updateUI();
782 783
}

784 785 786 787 788
void KisLayerBox::updateThumbnail()
{
    m_wdgLayerBox->listLayers->updateNode(m_wdgLayerBox->listLayers->currentIndex());
}

789
#include "kis_layer_box.moc"