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