1 /*
2  *  Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "kis_image_signal_router.h"
20 
21 #include <QThread>
22 
23 #include "kis_image.h"
24 
25 
26 #define CONNECT_TO_IMAGE(signal)                                        \
27     connect(this, SIGNAL(signal), m_image, SIGNAL(signal), Qt::DirectConnection)
28 
29 #define CONNECT_TO_IMAGE_QUEUED(signal)                                 \
30     connect(this, SIGNAL(signal), m_image, SIGNAL(signal), Qt::QueuedConnection)
31 
32 
33 struct ImageSignalsStaticRegistrar {
ImageSignalsStaticRegistrarImageSignalsStaticRegistrar34     ImageSignalsStaticRegistrar() {
35         qRegisterMetaType<KisImageSignalType>("KisImageSignalType");
36     }
37 };
38 static ImageSignalsStaticRegistrar __registrar;
39 
40 
KisImageSignalRouter(KisImageWSP image)41 KisImageSignalRouter::KisImageSignalRouter(KisImageWSP image)
42     : QObject(image.data()),
43       m_image(image)
44 {
45     connect(this, SIGNAL(sigNotification(KisImageSignalType)),
46             SLOT(slotNotification(KisImageSignalType)));
47 
48     CONNECT_TO_IMAGE(sigImageModified());
49     CONNECT_TO_IMAGE(sigImageModifiedWithoutUndo());
50     CONNECT_TO_IMAGE(sigSizeChanged(const QPointF&, const QPointF&));
51     CONNECT_TO_IMAGE(sigResolutionChanged(double, double));
52     CONNECT_TO_IMAGE(sigRequestNodeReselection(KisNodeSP, const KisNodeList&));
53 
54     CONNECT_TO_IMAGE(sigNodeChanged(KisNodeSP));
55     CONNECT_TO_IMAGE(sigNodeAddedAsync(KisNodeSP));
56     CONNECT_TO_IMAGE(sigRemoveNodeAsync(KisNodeSP));
57     CONNECT_TO_IMAGE(sigLayersChangedAsync());
58 
59     /**
60      * Color space and profile conversion functions run without storkes,
61      * therefore they are executed in GUI hread under the global lock held.
62      *
63      * To ensure that the receiver of the signal will not deadlock by
64      * barrier-locking the image, we should make these signals queued.
65      */
66 
67     CONNECT_TO_IMAGE_QUEUED(sigProfileChanged(const KoColorProfile*));
68     CONNECT_TO_IMAGE_QUEUED(sigColorSpaceChanged(const KoColorSpace*));
69 }
70 
~KisImageSignalRouter()71 KisImageSignalRouter::~KisImageSignalRouter()
72 {
73 }
74 
emitNotifications(KisImageSignalVector notifications)75 void KisImageSignalRouter::emitNotifications(KisImageSignalVector notifications)
76 {
77     Q_FOREACH (const KisImageSignalType &type, notifications) {
78         emitNotification(type);
79     }
80 }
81 
emitNotification(KisImageSignalType type)82 void KisImageSignalRouter::emitNotification(KisImageSignalType type)
83 {
84     /**
85      * All the notifications except LayersChangedSignal should go in a
86      * queued way. And LayersChangedSignal should be delivered to the
87      * recipients in a non-reordered way
88      */
89 
90     if (type.id == LayersChangedSignal) {
91         slotNotification(type);
92     } else {
93         emit sigNotification(type);
94     }
95 }
96 
emitNodeChanged(KisNodeSP node)97 void KisImageSignalRouter::emitNodeChanged(KisNodeSP node)
98 {
99     emit sigNodeChanged(node);
100 }
101 
emitNodeHasBeenAdded(KisNode * parent,int index)102 void KisImageSignalRouter::emitNodeHasBeenAdded(KisNode *parent, int index)
103 {
104     KisNodeSP newNode = parent->at(index);
105 
106     // overlay selection masks reset frames themselves
107     if (!newNode->inherits("KisSelectionMask")) {
108         KisImageSP image = m_image.toStrongRef();
109         if (image) {
110             image->invalidateAllFrames();
111         }
112     }
113 
114     emit sigNodeAddedAsync(newNode);
115 }
116 
emitAboutToRemoveANode(KisNode * parent,int index)117 void KisImageSignalRouter::emitAboutToRemoveANode(KisNode *parent, int index)
118 {
119     KisNodeSP removedNode = parent->at(index);
120 
121     // overlay selection masks reset frames themselves
122     if (!removedNode->inherits("KisSelectionMask")) {
123         KisImageSP image = m_image.toStrongRef();
124         if (image) {
125             image->invalidateAllFrames();
126         }
127     }
128 
129     emit sigRemoveNodeAsync(removedNode);
130 }
131 
emitRequestLodPlanesSyncBlocked(bool value)132 void KisImageSignalRouter::emitRequestLodPlanesSyncBlocked(bool value)
133 {
134     emit sigRequestLodPlanesSyncBlocked(value);
135 }
136 
emitNotifyBatchUpdateStarted()137 void KisImageSignalRouter::emitNotifyBatchUpdateStarted()
138 {
139     emit sigNotifyBatchUpdateStarted();
140 }
141 
emitNotifyBatchUpdateEnded()142 void KisImageSignalRouter::emitNotifyBatchUpdateEnded()
143 {
144     emit sigNotifyBatchUpdateEnded();
145 }
146 
slotNotification(KisImageSignalType type)147 void KisImageSignalRouter::slotNotification(KisImageSignalType type)
148 {
149     KisImageSP image = m_image.toStrongRef();
150     if (!image) {
151         return;
152     }
153 
154     switch(type.id) {
155     case LayersChangedSignal:
156         image->invalidateAllFrames();
157         emit sigLayersChangedAsync();
158         break;
159     case ModifiedSignal:
160         emit sigImageModified();
161         break;
162     case ModifiedWithoutUndoSignal:
163         emit sigImageModifiedWithoutUndo();
164         break;
165     case SizeChangedSignal:
166         image->invalidateAllFrames();
167         emit sigSizeChanged(type.sizeChangedSignal.oldStillPoint,
168                             type.sizeChangedSignal.newStillPoint);
169         break;
170     case ProfileChangedSignal:
171         image->invalidateAllFrames();
172         emit sigProfileChanged(image->profile());
173         break;
174     case ColorSpaceChangedSignal:
175         image->invalidateAllFrames();
176         emit sigColorSpaceChanged(image->colorSpace());
177         break;
178     case ResolutionChangedSignal:
179         image->invalidateAllFrames();
180         emit sigResolutionChanged(image->xRes(), image->yRes());
181         break;
182     case NodeReselectionRequestSignal:
183         if (type.nodeReselectionSignal.newActiveNode ||
184             !type.nodeReselectionSignal.newSelectedNodes.isEmpty()) {
185 
186             emit sigRequestNodeReselection(type.nodeReselectionSignal.newActiveNode,
187                                            type.nodeReselectionSignal.newSelectedNodes);
188         }
189         break;
190     }
191 }
192