1 // Not actually part of forms, but is needed.
2 // This code is public domain.
3 
4 /// Event handling.
5 module dfl.event;
6 
7 import dfl.internal.dlib;
8 import std.functional;
9 
10 
11 // Create an event handler; old style.
12 deprecated template Event(TArgs : EventArgs = EventArgs) {
13    alias Event!(Object, TArgs) Event;
14 }
15 
16 
17 /** Managing event handlers.
18     Params:
19       T1 = the sender type.
20       T2 = the event arguments type.
21 **/
22 template Event(T1, T2) { // docmain
23    /// Managing event handlers.
24    struct Event { // docmain
25       alias void delegate(T1, T2) Handler; /// Event handler type.
26 
27 
28       /// Add an event handler with the exact type.
29       void addHandlerExact(Handler handler)
30    in {
31       assert(handler);
32    }
33    body {
34       if(!_array.length) {
35          _array = new Handler[2];
36          _array[1] = handler;
37          unsetHot();
38       } else
39       {
40          if(!isHot()) {
41             _array ~= handler;
42          } else // Hot.
43          {
44             _array = _array ~ (&handler)[0 .. 1]; // Force duplicate.
45             unsetHot();
46          }
47       }
48    }
49 
50 
51    /// Add an event handler with parameter contravariance.
52    void addHandler(TDG)(TDG handler)
53    in {
54       assert(handler);
55    }
56    body {
57       mixin _validateHandler!(TDG);
58 
59       addHandlerExact(cast(Handler)toDelegate(handler));
60    }
61 
62 
63    /// Shortcut for addHandler().
64    void opCatAssign(TDG)(TDG handler) {
65       addHandler(toDelegate(handler));
66    }
67 
68 
69    /// Remove the specified event handler with the exact Handler type.
70    void removeHandlerExact(Handler handler) {
71       if(!_array.length) {
72          return;
73       }
74 
75       size_t iw;
76       for(iw = 1; iw != _array.length; iw++) {
77          if(handler == _array[iw]) {
78             if(iw == 1 && _array.length == 2) {
79                _array = null;
80                break;
81             }
82 
83             if(iw == _array.length - 1) {
84                _array[iw] = null;
85                _array = _array[0 .. iw];
86                break;
87             }
88 
89             if(!isHot()) {
90                _array[iw] = _array[_array.length - 1];
91                _array[_array.length - 1] = null;
92                _array = _array[0 .. _array.length - 1];
93             } else { // Hot.
94                _array = _array[0 .. iw] ~ _array[iw + 1 .. _array.length]; // Force duplicate.
95                unsetHot();
96             }
97             break;
98          }
99       }
100    }
101 
102 
103    /// Remove the specified event handler with parameter contravariance.
104    void removeHandler(TDG)(TDG handler) {
105       mixin _validateHandler!(TDG);
106 
107       removeHandlerExact(cast(Handler)toDelegate(handler));
108    }
109 
110 
111    /// Fire the event handlers.
112    void opCall(T1 v1, T2 v2) {
113       if(!_array.length) {
114          return;
115       }
116       setHot();
117 
118       Handler[] local;
119       local = _array[1 .. _array.length];
120       foreach(Handler handler; local) {
121          handler(v1, v2);
122       }
123 
124       if(!_array.length) {
125          return;
126       }
127       unsetHot();
128    }
129 
130 
131 
132    int opApply(int delegate(Handler) dg) {
133       if(!_array.length) {
134          return 0;
135       }
136       setHot();
137 
138       int result = 0;
139 
140       Handler[] local;
141       local = _array[1 .. _array.length];
142       foreach(Handler handler; local) {
143          result = dg(handler);
144          if(result) {
145             break;
146          }
147       }
148 
149       if(_array.length) {
150          unsetHot();
151       }
152 
153       return result;
154    }
155 
156 
157 
158    @property bool hasHandlers() pure nothrow { // getter
159       return _array.length > 1;
160    }
161 
162 
163    // Use opApply and hasHandlers instead.
164    deprecated @property Handler[] handlers() pure nothrow { // getter
165       if(!hasHandlers) {
166          return null;
167       }
168       try
169       {
170          return _array[1 .. _array.length].dup; // Because _array can be modified. Function is deprecated anyway.
171       } catch (DThrowable e) {
172          return null;
173       }
174    }
175 
176 
177    private:
178    Handler[] _array; // Not what it seems.
179 
180 
181    void setHot() {
182       assert(_array.length);
183       _array[0] = cast(Handler)&setHot; // Non-null, GC friendly.
184    }
185 
186 
187    void unsetHot() {
188       assert(_array.length);
189       _array[0] = null;
190    }
191 
192 
193    Handler isHot() {
194       assert(_array.length);
195       return _array[0];
196    }
197 
198 
199    // Thanks to Tomasz "h3r3tic" Stachowiak for his assistance.
200    template _validateHandler(TDG) {
201       static assert(is(typeof(toDelegate(TDG.init))), "DFL: Event handler must be a callable");
202 
203       alias ParameterTypeTuple!(TDG) TDGParams;
204       static assert(TDGParams.length == 2, "DFL: Event handler needs exactly 2 parameters");
205 
206       static if(is(TDGParams[0] : Object)) {
207          static assert(is(T1: TDGParams[0]), "DFL: Event handler parameter 1 type mismatch");
208       } else {
209          static assert(is(T1 == TDGParams[0]), "DFL: Event handler parameter 1 type mismatch");
210       }
211 
212       static if(is(TDGParams[1] : Object)) {
213          static assert(is(T2 : TDGParams[1]), "DFL: Event handler parameter 2 type mismatch");
214       } else {
215          static assert(is(T2 == TDGParams[1]), "DFL: Event handler parameter 2 type mismatch");
216       }
217    }
218    }
219 }
220 
221 
222 /// Base event arguments.
223 class EventArgs { // docmain
224    /+
225    private static byte[] buf;
226    private import std.gc; // <-- ...
227 
228 
229    new(uint sz) {
230       void* result;
231 
232       // synchronized // Slows it down a lot.
233       {
234          if(sz > buf.length) {
235             buf = new byte[100 + sz];
236          }
237 
238          result = buf[0 .. sz];
239          buf = buf[sz .. buf.length];
240       }
241 
242       // std.gc.addRange(result, result + sz); // So that it can contain pointers.
243       return result;
244    }
245    +/
246 
247 
248    /+
249    delete(void* p) {
250       std.gc.removeRange(p);
251    }
252    +/
253 
254 
255    //private static const EventArgs _e;
256    private static EventArgs _e;
257 
258 
259    static this() {
260       _e = new EventArgs;
261    }
262 
263 
264    /// Property: get a reusable, _empty EventArgs.
265    static @property EventArgs empty() nothrow { // getter
266       return _e;
267    }
268 }
269 
270 
271 // Simple event handler.
272 alias Event!(Object, EventArgs) EventHandler; // deprecated
273 
274 
275 
276 class ThreadExceptionEventArgs: EventArgs {
277 
278    // The exception that occured.
279    this(DThrowable theException) pure nothrow {
280       except = theException;
281    }
282 
283 
284 
285    final @property DThrowable exception() pure nothrow { // getter
286       return except;
287    }
288 
289 
290  private:
291    DThrowable except;
292 }
293