kis_base_node.cpp 10.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 *  Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
 *
 *  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.
 */
Boudewijn Rempt's avatar
Boudewijn Rempt committed
18

19
#include "kis_base_node.h"
20
#include <klocalizedstring.h>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
21

22
#include <kis_icon.h>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
23
#include <KoProperties.h>
24
#include <KoColorSpace.h>
25
#include <KoCompositeOpRegistry.h>
26 27 28
#include "kis_paint_device.h"
#include "kis_layer_properties_icons.h"

29
#include "kis_scalar_keyframe_channel.h"
30

31
struct Q_DECL_HIDDEN KisBaseNode::Private
32
{
33
    QString compositeOp;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
34
    KoProperties properties;
35
    KisBaseNode::Property hack_visible; //HACK
36
    QUuid id;
37 38 39 40
    QMap<QString, KisKeyframeChannel*> keyframeChannels;
    QScopedPointer<KisScalarKeyframeChannel> opacityChannel;

    bool systemLocked;
41
    bool collapsed;
42
    bool supportsLodMoves;
43 44 45 46
    bool animated;
    bool useInTimeline;

    Private()
47 48 49 50 51
        : id(QUuid::createUuid())
        , systemLocked(false)
        , collapsed(false)
        , supportsLodMoves(false)
        , animated(false)
52 53 54
        , useInTimeline(false)
    {
    }
55 56 57 58

    Private(const Private &rhs)
        : compositeOp(rhs.compositeOp),
          id(QUuid::createUuid()),
59
          systemLocked(false),
60 61 62 63 64 65 66 67 68 69 70
          collapsed(rhs.collapsed),
          supportsLodMoves(rhs.supportsLodMoves),
          animated(rhs.animated),
          useInTimeline(rhs.useInTimeline)
    {
        QMapIterator<QString, QVariant> iter = rhs.properties.propertyIterator();
        while (iter.hasNext()) {
            iter.next();
            properties.setProperty(iter.key(), iter.value());
        }
    }
71 72 73
};

KisBaseNode::KisBaseNode()
Boudewijn Rempt's avatar
Boudewijn Rempt committed
74
    : m_d(new Private())
75
{
76
    /**
77
     * Be cautious! These two calls are vital to warm-up KoProperties.
78
     * We use it and its QMap in a threaded environment. This is not
luz paz's avatar
luz paz committed
79
     * officially supported by Qt, but our environment guarantees, that
80 81 82 83 84
     * there will be the only writer and several readers. Whilst the
     * value of the QMap is boolean and there are no implicit-sharing
     * calls provocated, it is safe to work with it in such an
     * environment.
     */
85
    setVisible(true, true);
Cyrille Berger's avatar
Cyrille Berger committed
86
    setUserLocked(false);
87
    setCollapsed(false);
88
    setSupportsLodMoves(true);
89

90
    m_d->compositeOp = COMPOSITE_OVER;
91 92 93
}


Boudewijn Rempt's avatar
Boudewijn Rempt committed
94
KisBaseNode::KisBaseNode(const KisBaseNode & rhs)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
95
    : QObject()
96
    , KisShared()
97
    , m_d(new Private(*rhs.m_d))
98
{
99 100 101 102
    if (rhs.m_d->opacityChannel) {
        m_d->opacityChannel.reset(new KisScalarKeyframeChannel(*rhs.m_d->opacityChannel, 0));
        m_d->keyframeChannels.insert(m_d->opacityChannel->id(), m_d->opacityChannel.data());
    }
103 104 105 106 107 108 109
}

KisBaseNode::~KisBaseNode()
{
    delete m_d;
}

110 111 112 113 114
KisPaintDeviceSP KisBaseNode::colorPickSourceDevice() const
{
    return projection();
}

115 116
quint8 KisBaseNode::opacity() const
{
117
    if (m_d->opacityChannel) {
118
        qreal value = m_d->opacityChannel->currentValue();
119

120 121
        if (!qIsNaN(value)) {
            return value;
122 123 124
        }
    }

125
    return nodeProperties().intProperty("opacity", OPACITY_OPAQUE_U8);
126 127 128 129
}

void KisBaseNode::setOpacity(quint8 val)
{
130 131 132 133 134 135 136 137
    if (m_d->opacityChannel) {
        KisKeyframeSP activeKeyframe = m_d->opacityChannel->currentlyActiveKeyframe();

        if (activeKeyframe) {
            m_d->opacityChannel->setScalarValue(activeKeyframe, val);
        }
    }

138 139
    if (opacity() == val) return;

140
    setNodeProperty("opacity", val);
141 142

    baseNodeInvalidateAllFramesCallback();
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
}

quint8 KisBaseNode::percentOpacity() const
{
    return int(float(opacity() * 100) / 255 + 0.5);
}

void KisBaseNode::setPercentOpacity(quint8 val)
{
    setOpacity(int(float(val * 255) / 100 + 0.5));
}

const QString& KisBaseNode::compositeOpId() const
{
    return m_d->compositeOp;
}

160
void KisBaseNode::setCompositeOpId(const QString& compositeOp)
161
{
162 163
    if (m_d->compositeOp == compositeOp) return;

164
    m_d->compositeOp = compositeOp;
165
    baseNodeChangedCallback();
166
    baseNodeInvalidateAllFramesCallback();
167
}
168

169
KisBaseNode::PropertyList KisBaseNode::sectionModelProperties() const
170
{
171
    KisBaseNode::PropertyList l;
172 173
    l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::visible, visible(), m_d->hack_visible.isInStasis, m_d->hack_visible.stateInStasis);
    l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::locked, userLocked());
174 175 176
    return l;
}

177
void KisBaseNode::setSectionModelProperties(const KisBaseNode::PropertyList &properties)
178
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
179
    setVisible(properties.at(0).state.toBool());
180
    m_d->hack_visible = properties.at(0);
Cyrille Berger's avatar
Cyrille Berger committed
181
    setUserLocked(properties.at(1).state.toBool());
182 183
}

184
const KoProperties & KisBaseNode::nodeProperties() const
Boudewijn Rempt's avatar
Boudewijn Rempt committed
185 186 187 188
{
    return m_d->properties;
}

189 190 191 192 193 194
void KisBaseNode::setNodeProperty(const QString & name, const QVariant & value)
{
    m_d->properties.setProperty(name, value);
    baseNodeChangedCallback();
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
195
void KisBaseNode::mergeNodeProperties(const KoProperties & properties)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
196 197
{
    QMapIterator<QString, QVariant> iter = properties.propertyIterator();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
198
    while (iter.hasNext()) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
199
        iter.next();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
200
        m_d->properties.setProperty(iter.key(), iter.value());
Boudewijn Rempt's avatar
Boudewijn Rempt committed
201
    }
202
    baseNodeChangedCallback();
203
    baseNodeInvalidateAllFramesCallback();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
204 205
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
206
bool KisBaseNode::check(const KoProperties & properties) const
Boudewijn Rempt's avatar
Boudewijn Rempt committed
207 208
{
    QMapIterator<QString, QVariant> iter = properties.propertyIterator();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
209
    while (iter.hasNext()) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
210
        iter.next();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
211 212
        if (m_d->properties.contains(iter.key())) {
            if (m_d->properties.value(iter.key()) != iter.value())
Boudewijn Rempt's avatar
Boudewijn Rempt committed
213 214 215 216 217 218 219
                return false;
        }
    }
    return true;
}


Boudewijn Rempt's avatar
Boudewijn Rempt committed
220
QImage KisBaseNode::createThumbnail(qint32 w, qint32 h)
221 222
{
    try {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
223 224 225
        QImage image(w, h, QImage::Format_ARGB32);
        image.fill(0);
        return image;
226
    } catch (const std::bad_alloc&) {
227 228 229 230 231
        return QImage();
    }

}

232 233 234 235 236 237
QImage KisBaseNode::createThumbnailForFrame(qint32 w, qint32 h, int time)
{
    Q_UNUSED(time)
    return createThumbnail(w, h);
}

238
bool KisBaseNode::visible(bool recursive) const
239
{
240
    bool isVisible = m_d->properties.boolProperty(KisLayerPropertiesIcons::visible.id(), true);
241 242 243
    KisBaseNodeSP parentNode = parentCallback();

    return recursive && isVisible && parentNode ?
244
        parentNode->visible(recursive) : isVisible;
245 246
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
247
void KisBaseNode::setVisible(bool visible, bool loading)
248
{
249
    const bool isVisible = m_d->properties.boolProperty(KisLayerPropertiesIcons::visible.id(), true);
250 251
    if (!loading && isVisible == visible) return;

252
    m_d->properties.setProperty(KisLayerPropertiesIcons::visible.id(), visible);
253 254
    notifyParentVisibilityChanged(visible);

Boudewijn Rempt's avatar
Boudewijn Rempt committed
255 256 257
    if (!loading) {
        emit visibilityChanged(visible);
        baseNodeChangedCallback();
258
        baseNodeInvalidateAllFramesCallback();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
259
    }
260 261
}

Cyrille Berger's avatar
Cyrille Berger committed
262
bool KisBaseNode::userLocked() const
263
{
264
    return m_d->properties.boolProperty(KisLayerPropertiesIcons::locked.id(), false);
265 266
}

Cyrille Berger's avatar
Cyrille Berger committed
267
void KisBaseNode::setUserLocked(bool locked)
268
{
269
    const bool isLocked = m_d->properties.boolProperty(KisLayerPropertiesIcons::locked.id(), true);
270 271
    if (isLocked == locked) return;

272
    m_d->properties.setProperty(KisLayerPropertiesIcons::locked.id(), locked);
273 274
    emit userLockingChanged(locked);
    baseNodeChangedCallback();
Cyrille Berger's avatar
Cyrille Berger committed
275 276
}

277
bool KisBaseNode::isEditable(bool checkVisibility) const
278
{
279 280
    bool editable = true;
    if (checkVisibility) {
281
        editable = (visible(false) && !userLocked());
282 283
    }
    else {
284
        editable = (!userLocked());
285
    }
286 287 288 289

    if (editable) {
        KisBaseNodeSP parentNode = parentCallback();
        if (parentNode && parentNode != this) {
290
            editable = parentNode->isEditable(checkVisibility);
291 292 293
        }
    }
    return editable;
294 295
}

296 297 298 299 300
bool KisBaseNode::hasEditablePaintDevice() const
{
    return paintDevice() && isEditable();
}

301 302 303 304 305 306 307 308 309 310
void KisBaseNode::setCollapsed(bool collapsed)
{
    m_d->collapsed = collapsed;
}

bool KisBaseNode::collapsed() const
{
    return m_d->collapsed;
}

311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
void KisBaseNode::setColorLabelIndex(int index)
{
    const int currentLabel = colorLabelIndex();

    if (currentLabel == index) return;

    m_d->properties.setProperty(KisLayerPropertiesIcons::colorLabelIndex.id(), index);
    baseNodeChangedCallback();
}

int KisBaseNode::colorLabelIndex() const
{
    return m_d->properties.intProperty(KisLayerPropertiesIcons::colorLabelIndex.id(), 0);
}

326 327 328 329 330 331 332 333
QUuid KisBaseNode::uuid() const
{
    return m_d->id;
}

void KisBaseNode::setUuid(const QUuid& id)
{
    m_d->id = id;
334
    baseNodeChangedCallback();
335 336
}

337 338 339 340 341
bool KisBaseNode::supportsLodMoves() const
{
    return m_d->supportsLodMoves;
}

342 343 344 345 346
void KisBaseNode::setImage(KisImageWSP image)
{
    Q_UNUSED(image);
}

347 348 349 350
void KisBaseNode::setSupportsLodMoves(bool value)
{
    m_d->supportsLodMoves = value;
}
351

352

353
QMap<QString, KisKeyframeChannel*> KisBaseNode::keyframeChannels() const
354
{
355
    return m_d->keyframeChannels;
356 357 358 359
}

KisKeyframeChannel * KisBaseNode::getKeyframeChannel(const QString &id) const
{
360 361 362 363
    QMap<QString, KisKeyframeChannel*>::const_iterator i = m_d->keyframeChannels.constFind(id);
    if (i == m_d->keyframeChannels.constEnd()) {
        return 0;
    }
364 365 366
    return i.value();
}

367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
KisKeyframeChannel * KisBaseNode::getKeyframeChannel(const QString &id, bool create)
{
    KisKeyframeChannel *channel = getKeyframeChannel(id);

    if (!channel && create) {
        channel = requestKeyframeChannel(id);

        if (channel) {
            addKeyframeChannel(channel);
        }
    }

    return channel;
}

382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
bool KisBaseNode::isAnimated() const
{
    return m_d->animated;
}

void KisBaseNode::enableAnimation()
{
    m_d->animated = true;
    baseNodeChangedCallback();
}

bool KisBaseNode::useInTimeline() const
{
    return m_d->useInTimeline;
}

void KisBaseNode::setUseInTimeline(bool value)
{
    if (value == m_d->useInTimeline) return;

    m_d->useInTimeline = value;
    baseNodeChangedCallback();
}

void KisBaseNode::addKeyframeChannel(KisKeyframeChannel *channel)
{
    m_d->keyframeChannels.insert(channel->id(), channel);
409
    emit keyframeChannelAdded(channel);
410
}
411 412 413

KisKeyframeChannel *KisBaseNode::requestKeyframeChannel(const QString &id)
{
414 415 416 417 418 419 420 421 422
    if (id == KisKeyframeChannel::Opacity.id()) {
        Q_ASSERT(m_d->opacityChannel.isNull());

        KisPaintDeviceSP device = original();

        if (device) {
            KisScalarKeyframeChannel * channel = new KisScalarKeyframeChannel(
                KisKeyframeChannel::Opacity,
                0, 255,
423 424
                device->defaultBounds(),
                KisKeyframe::Linear
425 426 427 428 429 430 431 432
            );

            m_d->opacityChannel.reset(channel);

            return channel;
        }
    }

433 434
    return 0;
}