1 /*
2  * This file is part of d-dazzle.
3  *
4  * d-dazzle is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 3
7  * of the License, or (at your option) any later version, with
8  * some exceptions, please read the COPYING file.
9  *
10  * d-dazzle is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with d-dazzle; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
18  */
19 module dazzle.SignalGroup;
20 
21 private import dazzle.c.functions;
22 public  import dazzle.c.types;
23 private import glib.ConstructionException;
24 private import glib.Str;
25 private import gobject.ObjectG;
26 private import gobject.Signals;
27 private import std.algorithm;
28 
29 
30 /**
31  * #DzlSignalGroup manages to simplify the process of connecting
32  * many signals to a #GObject as a group. As such there is no API
33  * to disconnect a signal from the group.
34  * 
35  * In particular, this allows you to:
36  * 
37  * - Change the target instance, which automatically causes disconnection
38  * of the signals from the old instance and connecting to the new instance.
39  * - Block and unblock signals as a group
40  * - Ensuring that blocked state transfers across target instances.
41  * 
42  * One place you might want to use such a structure is with #GtkTextView and
43  * #GtkTextBuffer. Often times, you'll need to connect to many signals on
44  * #GtkTextBuffer from a #GtkTextView subclass. This allows you to create a
45  * signal group during instance construction, simply bind the
46  * #GtkTextView:buffer property to #DzlSignalGroup:target and connect
47  * all the signals you need. When the #GtkTextView:buffer property changes
48  * all of the signals will be transitioned correctly.
49  */
50 public class SignalGroup : ObjectG
51 {
52 	/** the main Gtk struct */
53 	protected DzlSignalGroup* dzlSignalGroup;
54 
55 	/** Get the main Gtk struct */
56 	public DzlSignalGroup* getSignalGroupStruct(bool transferOwnership = false)
57 	{
58 		if (transferOwnership)
59 			ownedRef = false;
60 		return dzlSignalGroup;
61 	}
62 
63 	/** the main Gtk struct as a void* */
64 	protected override void* getStruct()
65 	{
66 		return cast(void*)dzlSignalGroup;
67 	}
68 
69 	/**
70 	 * Sets our main struct and passes it to the parent class.
71 	 */
72 	public this (DzlSignalGroup* dzlSignalGroup, bool ownedRef = false)
73 	{
74 		this.dzlSignalGroup = dzlSignalGroup;
75 		super(cast(GObject*)dzlSignalGroup, ownedRef);
76 	}
77 
78 
79 	/** */
80 	public static GType getType()
81 	{
82 		return dzl_signal_group_get_type();
83 	}
84 
85 	/**
86 	 * Creates a new #DzlSignalGroup for target instances of @target_type.
87 	 *
88 	 * Params:
89 	 *     targetType = the #GType of the target instance.
90 	 *
91 	 * Returns: a new #DzlSignalGroup
92 	 *
93 	 * Throws: ConstructionException GTK+ fails to create the object.
94 	 */
95 	public this(GType targetType)
96 	{
97 		auto p = dzl_signal_group_new(targetType);
98 
99 		if(p is null)
100 		{
101 			throw new ConstructionException("null returned by new");
102 		}
103 
104 		this(cast(DzlSignalGroup*) p, true);
105 	}
106 
107 	/**
108 	 * Blocks all signal handlers managed by @self so they will not
109 	 * be called during any signal emissions. Must be unblocked exactly
110 	 * the same number of times it has been blocked to become active again.
111 	 *
112 	 * This blocked state will be kept across changes of the target instance.
113 	 *
114 	 * See: g_signal_handler_block().
115 	 */
116 	public void block()
117 	{
118 		dzl_signal_group_block(dzlSignalGroup);
119 	}
120 
121 	/**
122 	 * Connects @callback to the signal @detailed_signal
123 	 * on the target instance of @self.
124 	 *
125 	 * See: g_signal_connect().
126 	 *
127 	 * Params:
128 	 *     detailedSignal = a string of the form "signal-name::detail"
129 	 *     cHandler = the #GCallback to connect
130 	 *     data = the data to pass to @callback calls
131 	 */
132 	public void connect(string detailedSignal, GCallback cHandler, void* data)
133 	{
134 		dzl_signal_group_connect(dzlSignalGroup, Str.toStringz(detailedSignal), cHandler, data);
135 	}
136 
137 	/**
138 	 * Connects @callback to the signal @detailed_signal
139 	 * on the target instance of @self.
140 	 *
141 	 * The @callback will be called after the default handler of the signal.
142 	 *
143 	 * See: g_signal_connect_after().
144 	 *
145 	 * Params:
146 	 *     detailedSignal = a string of the form "signal-name::detail"
147 	 *     cHandler = the #GCallback to connect
148 	 *     data = the data to pass to @callback calls
149 	 */
150 	public void connectAfter(string detailedSignal, GCallback cHandler, void* data)
151 	{
152 		dzl_signal_group_connect_after(dzlSignalGroup, Str.toStringz(detailedSignal), cHandler, data);
153 	}
154 
155 	/**
156 	 * Connects @callback to the signal @detailed_signal
157 	 * on the target instance of @self.
158 	 *
159 	 * See: g_signal_connect_data().
160 	 *
161 	 * Params:
162 	 *     detailedSignal = a string of the form "signal-name::detail"
163 	 *     cHandler = the #GCallback to connect
164 	 *     data = the data to pass to @callback calls
165 	 *     notify = function to be called when disposing of @self
166 	 *     flags = the flags used to create the signal connection
167 	 */
168 	public void connectData(string detailedSignal, GCallback cHandler, void* data, GClosureNotify notify, GConnectFlags flags)
169 	{
170 		dzl_signal_group_connect_data(dzlSignalGroup, Str.toStringz(detailedSignal), cHandler, data, notify, flags);
171 	}
172 
173 	/**
174 	 * Connects @callback to the signal @detailed_signal
175 	 * on the target object of @self.
176 	 *
177 	 * Ensures that the @object stays alive during the call to @callback
178 	 * by temporarily adding a reference count. When the @object is destroyed
179 	 * the signal handler will automatically be removed.
180 	 *
181 	 * See: g_signal_connect_object().
182 	 *
183 	 * Params:
184 	 *     detailedSignal = a string of the form "signal-name::detail"
185 	 *     cHandler = the #GCallback to connect
186 	 *     object = the #GObject to pass as data to @callback calls
187 	 */
188 	public void connectObject(string detailedSignal, GCallback cHandler, void* object, GConnectFlags flags)
189 	{
190 		dzl_signal_group_connect_object(dzlSignalGroup, Str.toStringz(detailedSignal), cHandler, object, flags);
191 	}
192 
193 	/**
194 	 * Connects @callback to the signal @detailed_signal
195 	 * on the target instance of @self.
196 	 *
197 	 * The instance on which the signal is emitted and @data
198 	 * will be swapped when calling @callback.
199 	 *
200 	 * See: g_signal_connect_swapped().
201 	 *
202 	 * Params:
203 	 *     detailedSignal = a string of the form "signal-name::detail"
204 	 *     cHandler = the #GCallback to connect
205 	 *     data = the data to pass to @callback calls
206 	 */
207 	public void connectSwapped(string detailedSignal, GCallback cHandler, void* data)
208 	{
209 		dzl_signal_group_connect_swapped(dzlSignalGroup, Str.toStringz(detailedSignal), cHandler, data);
210 	}
211 
212 	/**
213 	 * Gets the target instance used when connecting signals.
214 	 *
215 	 * Returns: The target instance.
216 	 */
217 	public ObjectG getTarget()
218 	{
219 		auto p = dzl_signal_group_get_target(dzlSignalGroup);
220 
221 		if(p is null)
222 		{
223 			return null;
224 		}
225 
226 		return ObjectG.getDObject!(ObjectG)(cast(GObject*) p);
227 	}
228 
229 	/**
230 	 * Sets the target instance used when connecting signals. Any signal
231 	 * that has been registered with dzl_signal_group_connect_object() or
232 	 * similar functions will be connected to this object.
233 	 *
234 	 * If the target instance was previously set, signals will be
235 	 * disconnected from that object prior to connecting to @target.
236 	 *
237 	 * Params:
238 	 *     target = The target instance used
239 	 *         when connecting signals.
240 	 */
241 	public void setTarget(ObjectG target)
242 	{
243 		dzl_signal_group_set_target(dzlSignalGroup, (target is null) ? null : target.getObjectGStruct());
244 	}
245 
246 	/**
247 	 * Unblocks all signal handlers managed by @self so they will be
248 	 * called again during any signal emissions unless it is blocked
249 	 * again. Must be unblocked exactly the same number of times it
250 	 * has been blocked to become active again.
251 	 *
252 	 * See: g_signal_handler_unblock().
253 	 */
254 	public void unblock()
255 	{
256 		dzl_signal_group_unblock(dzlSignalGroup);
257 	}
258 
259 	/**
260 	 * This signal is emitted when the target instance of @self
261 	 * is set to a new #GObject.
262 	 *
263 	 * This signal will only be emitted if the target of @self is non-%NULL.
264 	 *
265 	 * Params:
266 	 *     instance_ = a #GObject
267 	 */
268 	gulong addOnBind(void delegate(ObjectG, SignalGroup) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0)
269 	{
270 		return Signals.connect(this, "bind", dlg, connectFlags ^ ConnectFlags.SWAPPED);
271 	}
272 
273 	/**
274 	 * This signal is emitted when the target instance of @self
275 	 * is set to a new #GObject.
276 	 *
277 	 * This signal will only be emitted if the previous target
278 	 * of @self is non-%NULL.
279 	 */
280 	gulong addOnUnbind(void delegate(SignalGroup) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0)
281 	{
282 		return Signals.connect(this, "unbind", dlg, connectFlags ^ ConnectFlags.SWAPPED);
283 	}
284 }