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