Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

signal.d

Go to the documentation of this file.
00001 /*
00002 
00003   Copyright 2004, 2005 Bastiaan Veelo
00004 
00005   This file is part of dcouple.
00006 
00007   Dcouple is free software; you can redistribute it and/or modify
00008   it under the terms of the GNU General Public License as published by
00009   the Free Software Foundation; either version 2 of the License, or
00010   (at your option) any later version.
00011 
00012   You are free to negociate a different license with the copyright holder.
00013 
00014   Dcouple is distributed in the hope that it will be useful,
00015   but WITHOUT ANY WARRANTY; without even the implied warranty of
00016   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017   GNU General Public License for more details.
00018 
00019   You should have received a copy of the GNU General Public License
00020   along with dcouple; if not, write to the Free Software
00021   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 
00023   Some concepts in this file were inspired by Andy Friesen's listener.d
00024   (http://andy.tadan.us/d/listener.d).
00025 
00026 */
00027 
00028 module dcouple.signal;
00029 
00030 private import dcouple.slot;
00031 private import dcouple.signalslot;
00032 
00033 //! An interface for the management of signals.
00034 /*! Provides an interface that must be implemented by classes that own \link Signal Signals \endlink in a managed way. */
00035 interface SignalManager
00036 {
00037   /*! Deriving classes must implement this function to keep a list of 
00038   \link Signal Signals \endlink that it owns, and disconnect() them in the 
00039   destructor (so that connected \link Slot Slots \endlink remove their 
00040   reference). Also, deriving classes must provide and use a method (say, 
00041   .discard()) to disconnect all signals when the object is no longer used, 
00042   so that it may be garbage collected.
00043   */
00044   void register(GenericSignal);
00045   void deregister(GenericSignal);
00046 public:
00047   bool signalsBlocked();
00048   bool signalsBlocked(bool block);
00049 }
00050 
00051 /*! Implementation of a SignalManager. To be mixed in:
00052 \code
00053 class MyClass : SignalManager
00054 {
00055   mixin SignalManagement;
00056   //...
00057 }
00058 \endcode */
00059 template SignalManagement()
00060 {
00061 /*      ~this()
00062         {
00063                 deleteSignals();
00064         }
00065 */      
00066 protected:
00067   /*! SignalManager implementation */
00068   void register(GenericSignal s)
00069   {
00070     //  _signals[s] = s;
00071     int len = _signals.length;
00072     int n = len-1;
00073     while (n >= 0 && _signals[n] is null)
00074       --n;
00075     if (n < len-1)
00076       _signals[n+1] = s;
00077     else
00078       _signals ~= s;
00079   }
00080 
00081   void deregister(GenericSignal s)
00082   {
00083     //_signals.remove(s);
00084     if (_signals.length == 0) return;
00085     bool found = false;
00086     int n;
00087     for (n=0 ; n < _signals.length-1; ++n) {
00088       if (!found && _signals[n] is s) found = true;
00089       if (found) _signals[n] = _signals[n+1];
00090       if (_signals[n] is null) break;
00091     }
00092     if (found || _signals[length-1] is s)
00093       _signals[length-1] = null;
00094   }
00095         
00096 public:
00097   /*! Returns true if signals are blocked; otherwise returns false.
00098 
00099   Signals are not blocked by default.
00100 
00101   \sa blockSignals()
00102   */
00103   bool signalsBlocked() { return _signalsBlocked; }
00104 
00105   /*! Blocks ordinary signals if \a block is true, or unblocks signals if \a
00106   block is false.
00107 
00108   Emitted signals disappear into hyperspace if signals are blocked.
00109   Note that a Signal will be emitted even if the signals
00110   for this object have been blocked.
00111 
00112   \returns new block value.
00113   \warning Returned value is opposite from QObject::blockSignals.
00114   */
00115   bool signalsBlocked(bool block) { return _signalsBlocked = block; }
00116 
00117 protected:
00118   GenericSignal[] _signals;
00119   void deleteSignals()
00120   {
00121     debug printf("SignalManager::deleteSignals entered.\n_signals:\n");
00122     debug foreach(GenericSignal s; _signals) printf("signal %p.\n",s);
00123     foreach(GenericSignal s; _signals) {
00124       if (s is null) break;
00125       // prevent s from manipulating _signals:
00126       s.freeFrom(this);
00127       debug printf("SignalManager::deleteSignals: deleting %p.\n", s);
00128       delete s;
00129       pragma(msg, "_signals contains dangling references. OK?");
00130     }
00131   }
00132 private:
00133   bool _signalsBlocked = false;
00134 }
00135 
00136 
00137 //! An interface shared by all Signals
00138 /*! \todo access specifiers*/
00139 interface GenericSignal
00140 //abstract class GenericSignal
00141 {
00142   //! Disconnect all. 
00143   /*! Breaks all connections from this Signal. */
00144   //void disconnect();
00145   void disconnect();
00146   SignalManager owner();
00147 
00148 pragma(msg, "Want access specifier here.");
00149   void genericConnect(GenericSlot);
00150   void genericDisconnect(GenericSlot);
00151 
00152   uint numberOfArguments();
00153   char[][] arguments();
00154 
00155   void freeFrom(SignalManager);
00156 }
00157 
00158 
00159 /*! The generic part of \link Signal Signals. \endlink */
00160 private template SignalGenericCore() {
00161   //! Constructor.
00162   /*! The Signal \link SignalManager::register(Signal) registers \endlink 
00163       itself with its owner. 
00164   */
00165   //this() { this(null); }
00166   //this(SignalManager owner)
00167   this(SignalManager owner=null)
00168   {
00169     _owner = owner;
00170     if (owner) owner.register(this);
00171     initArguments();
00172   }
00173 
00174   //! Destructor.
00175   /*! Disconnects all connected \link Slot Slots \endlink, and \link 
00176       SignalManager::deregister(Signal) de-registers \endlink itself with its 
00177       owner. 
00178   */
00179   ~this()
00180   {
00181     disconnect();
00182     /* Strictly spoken, destructors are not allowed to reference 
00183     other objects, because the order in which the GC calls 
00184     destructors is not defined, so those objects may not exist 
00185     anymore. The disconnect() function above breaks this rule 
00186     indirectly. But this particular case is OK, because we only 
00187     consider references to CompatibleSlots that *are still alive*. 
00188     Slots that are already destructed have removed our reference to 
00189     them, just like we have removed references to us in other slots 
00190     in disconnect(). */
00191     if(_owner) _owner.deregister(this);
00192   }
00193 
00194   /*! Establishes a connection between this Signal and Slot \a s, and vice 
00195       versa (i.e., registers this Signal within Slot \a s).
00196   */
00197   void connect(CompatibleSlot s)
00198   {
00199     debug printf("Signal::connect entered.\n");
00200     connectToo(s);
00201     s.connectToo(this);
00202   }
00203 
00204   package void connectToo(CompatibleSlot s)
00205   {
00206     debug printf("Signal::connectToo entered.\n");
00207     debug printf("_slots before:\n");
00208     debug foreach( CompatibleSlot s; _slots ) printf( "slot %p\n", s);
00209     //if(s in _slots) {} else {
00210     //  _slots[s] = s;
00211     int len = _slots.length;
00212     int n = len-1;
00213     while (n >= 0 && _slots[n] is null)
00214       --n;
00215     if (n < len-1)
00216       _slots[n+1] = s;
00217     else
00218       _slots ~= s;
00219     debug printf("_slots after:\n");
00220     debug foreach( CompatibleSlot s; _slots ) printf( "slot %p\n", s);
00221   }
00222 
00223   void genericConnect(GenericSlot gs)
00224   {
00225     debug printf( "Signal::genericConnect entered.\n");
00226     CompatibleSlot s = cast(CompatibleSlot) gs;
00227     if( s )
00228       connect(s);
00229     else {
00230       if (_owner)
00231         printf("dcouple WARNING: %.*s.%.*s!(", 
00232                           _owner.classinfo.name, this.classinfo.name);
00233       else
00234         printf("dcouple WARNING: %.*s!(", this.classinfo.name);
00235       if(numberOfArguments()>0)
00236         printf("%.*s",arguments()[0]);
00237       for(int i=1; i<numberOfArguments();i++)
00238         printf(",%.*s",arguments()[i]);
00239       if (gs.owner())
00240         printf(") is incompatible with %.*s.%.*s!(", 
00241                           gs.owner().classinfo.name,  gs.classinfo.name);
00242       else
00243         printf(") is incompatible with %.*s!(", gs.classinfo.name);
00244       if(gs.numberOfArguments()>0)
00245         printf("%.*s",gs.arguments()[0]);
00246       for(int i=1; i<gs.numberOfArguments();i++)
00247         printf(",%.*s",gs.arguments()[i]);
00248       printf("); not connected. (Sorry, no line number...)\n");
00249     }
00250   }
00251 
00252   /*! Breaks the connection between this Signal and Slot \a s (if it 
00253       exists), and vice versa (i.e., de-registers this Signal from Slot \a s).
00254   */
00255   void disconnect(CompatibleSlot s)
00256   {
00257     debug printf("signal::disconnect entered.\n");
00258     if (disconnectToo(s))
00259       s.disconnectToo(this);
00260   }
00261   package bool disconnectToo(CompatibleSlot s)
00262   {
00263     debug printf("_slots before:\n");
00264     debug foreach( CompatibleSlot s; _slots ) printf( "slot %p\n", s);
00265 
00266     debug printf("signal::disconnectToo: signal %p disconnecting from slot %p.\n", this, s);
00267     if (_slots.length == 0) return false;
00268     bool found = false;
00269     for (int n=0 ; n < _slots.length-1; ++n) {
00270       if (!found && _slots[n] is s) found = true;
00271       if (found) _slots[n] = _slots[n+1];
00272       if (_slots[n] is null) break;
00273     }
00274     if (found || _slots[length-1] is s)
00275       _slots[length-1] = null;
00276 
00277     debug printf("_slots after:\n");
00278     debug foreach( CompatibleSlot s; _slots ) printf( "slot %p\n", s);
00279 
00280     return found;
00281   }
00282 
00283   void genericDisconnect(GenericSlot gs)
00284   {
00285     debug printf("signal::genericDisconnect entered.\n");
00286     CompatibleSlot s = cast(CompatibleSlot) gs;
00287     if( s )
00288       disconnect(s);
00289     else {
00290       if (_owner)
00291         printf("dcouple WARNING: %.*s.%.*s!(", 
00292                           _owner.classinfo.name, this.classinfo.name);
00293       else
00294         printf("dcouple WARNING: %.*s!(", this.classinfo.name);
00295       if(numberOfArguments()>0)
00296         printf("%.*s",arguments()[0]);
00297       for(int i=1; i<numberOfArguments();i++)
00298         printf(",%.*s",arguments()[i]);
00299       if (gs.owner())
00300         printf(") is incompatible with %.*s.%.*s!(", 
00301                           gs.owner().classinfo.name,  gs.classinfo.name);
00302       else
00303         printf(") is incompatible with %.*s!(", gs.classinfo.name);
00304       if(gs.numberOfArguments()>0)
00305         printf("%.*s",gs.arguments()[0]);
00306       for(int i=1; i<gs.numberOfArguments();i++)
00307         printf(",%.*s",gs.arguments()[i]);
00308       printf("); not disconnected. (Sorry, no line number...)\n");
00309     }
00310   }
00311 
00312   //! Disconnect all. 
00313   /*! Breaks all connections from this Signal. */
00314   void disconnect()
00315   {
00316     //foreach does not work because disconnect(s) manipulates _slots. Is this a dmd bug or feature?
00317     /*foreach(CompatibleSlot s; _slots) {
00318       if (s is null) break;
00319       disconnect(s);
00320     }
00321     */
00322     for( int i=_slots.length-1; i>= 0; i-- ) {
00323       if( _slots[i] !is null ) disconnect(_slots[i]);
00324     }
00325   }
00326 
00327   /*! \returns the number of connected \link Slot Slots. \endlink */
00328   int count() 
00329   {
00330     return _slots.length;
00331   }
00332 
00333   uint numberOfArguments()
00334   {
00335     return _arguments.length;
00336   }
00337 
00338   char[][] arguments() 
00339   {
00340     return _arguments;
00341   }
00342 
00343   SignalManager owner() 
00344   {
00345     return _owner;
00346   }
00347 
00348   void freeFrom(SignalManager owner)
00349   {
00350     // Only the real owner can set us free.
00351     assert(owner is _owner);
00352     _owner = null;
00353     // Do not deregister, owner is in charge.
00354   }
00355 
00356 protected:
00357   //! This Signal is managed by _owner.
00358   SignalManager _owner;
00359   //! Collection of connected \link Slot Slots. \endlink
00360   CompatibleSlot[] _slots;
00361   char[][] _arguments;  // Can be static?
00362 }
00363 
00364 //! A signal for the connection to slots.
00365 /*! A Signal should be owned by a \link SignalManager managing \endlink class,
00366 and can be \link connect() connected \endlink to one or more \link Slot Slots, \endlink possibly in other classes. These \link Slot Slots \endlink are notified
00367 when the Signal is \link emit() emitted. \endlink
00368 */
00369 class Signal() : GenericSignal
00370 {
00371   alias Slot!() CompatibleSlot;
00372   mixin SignalGenericCore;
00373 
00374   /*! Causes all connected \link Slot Slots \endlink to be notified. 
00375   \todo We would like to only allow emit() to be called by the owner. Should we 
00376   require a this pointer to be passed which we can check? A bit hackish, and 
00377   Signals that are not owned can not be protected this way. We could just 
00378   assume that the user does not call emit from strange places, but it may cause 
00379   a security risk in some application. */
00380   void emit()
00381   {
00382     debug printf("Signal!() emit() entered.\n");
00383     debug printf("_slots:\n");
00384     debug foreach(CompatibleSlot s; _slots) printf("slot %p\n", s);
00385     if (_owner && _owner.signalsBlocked())
00386       return;
00387     foreach( CompatibleSlot s; _slots ) {
00388       if (s is null) break;
00389       debug printf("emitting on %p\n", s);
00390     s();
00391     }
00392   }
00393 
00394   void initArguments() {}
00395 }
00396 
00397 class Signal(T1) : GenericSignal
00398 {
00399   alias Slot!(T1) CompatibleSlot;
00400   mixin SignalGenericCore;
00401 
00402   /*! Causes all connected \link Slot Slots \endlink to be notified. */
00403   void emit(T1 t1)
00404   {
00405     if (_owner && _owner.signalsBlocked()) return;
00406     foreach( CompatibleSlot s; _slots ) {
00407       if (s is null) break;
00408       s(t1);
00409     }
00410   }
00411 
00412   void initArguments()
00413   {
00414     _arguments ~= typeid(T1).toString();
00415   }
00416 }
00417 
00418 class Signal(T1,T2) : GenericSignal
00419 {
00420   alias Slot!(T1,T2) CompatibleSlot;
00421   mixin SignalGenericCore;
00422 
00423   /*! Causes all connected \link Slot Slots \endlink to be notified. */
00424   void emit(T1 t1, T2 t2)
00425   {
00426     if (_owner && _owner.signalsBlocked()) return;
00427     foreach( CompatibleSlot s; _slots ) {
00428       if (s is null) break;
00429       s(t1, t2);
00430     }
00431   }
00432 
00433   void initArguments()
00434   {
00435     _arguments ~= typeid(T1).toString();
00436     _arguments ~= typeid(T2).toString();
00437   }
00438 }
00439 
00440 class Signal(T1,T2,T3) : GenericSignal
00441 {
00442   alias Slot!(T1,T2,T3) CompatibleSlot;
00443   mixin SignalGenericCore;
00444 
00445   /*! Causes all connected \link Slot Slots \endlink to be notified. */
00446   void emit(T1 t1, T2 t2, T3 t3)
00447   {
00448     if (_owner && _owner.signalsBlocked()) return;
00449     foreach( CompatibleSlot s; _slots ) {
00450       if (s is null) break;
00451       s(t1, t2, t3);
00452     }
00453   }
00454 
00455   void initArguments()
00456   {
00457     _arguments ~= typeid(T1).toString();
00458     _arguments ~= typeid(T2).toString();
00459     _arguments ~= typeid(T3).toString();
00460   }
00461 }
00462 
00463 class Signal(T1,T2,T3,T4) : GenericSignal
00464 {
00465   alias Slot!(T1,T2,T3,T4) CompatibleSlot;
00466   mixin SignalGenericCore;
00467 
00468   /*! Causes all connected \link Slot Slots \endlink to be notified. */
00469   void emit(T1 t1, T2 t2, T3 t3, T4 t4)
00470   {
00471     if (_owner && _owner.signalsBlocked()) return;
00472     foreach( CompatibleSlot s; _slots ) {
00473       if (s is null) break;
00474       s(t1, t2, t3, t4);
00475     }
00476   }
00477 
00478   void initArguments()
00479   {
00480     _arguments ~= typeid(T1).toString();
00481     _arguments ~= typeid(T2).toString();
00482     _arguments ~= typeid(T3).toString();
00483     _arguments ~= typeid(T4).toString();
00484   }
00485 }
00486 
00487 class Signal(T1,T2,T3,T4,T5) : GenericSignal
00488 {
00489   alias Slot!(T1,T2,T3,T4,T5) CompatibleSlot;
00490   mixin SignalGenericCore;
00491   
00492   /*! Causes all connected \link Slot Slots \endlink to be notified. */
00493   void emit(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
00494   {
00495     if (_owner && _owner.signalsBlocked()) return;
00496     foreach( CompatibleSlot s; _slots ) {
00497       if (s is null) break;
00498       s(t1, t2, t3, t4, t5);
00499     }
00500   }
00501 
00502   void initArguments()
00503   {
00504     _arguments ~= typeid(T1).toString();
00505     _arguments ~= typeid(T2).toString();
00506     _arguments ~= typeid(T3).toString();
00507     _arguments ~= typeid(T4).toString();
00508     _arguments ~= typeid(T5).toString();
00509   }
00510 }
00511 
00512 class Signal(T1,T2,T3,T4,T5,T6) : GenericSignal
00513 {
00514   alias Slot!(T1,T2,T3,T4,T5,T6) CompatibleSlot;
00515   mixin SignalGenericCore;
00516 
00517   /*! Causes all connected \link Slot Slots \endlink to be notified. */
00518   void emit(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
00519   {
00520     if (_owner && _owner.signalsBlocked()) return;
00521     foreach( CompatibleSlot s; _slots ) {
00522       if (s is null) break;
00523       s(t1, t2, t3, t4, t5, t6);
00524     }
00525   }
00526 
00527   void initArguments()
00528   {
00529     _arguments ~= typeid(T1).toString();
00530     _arguments ~= typeid(T2).toString();
00531     _arguments ~= typeid(T3).toString();
00532     _arguments ~= typeid(T4).toString();
00533     _arguments ~= typeid(T5).toString();
00534     _arguments ~= typeid(T6).toString();
00535   }
00536 }
00537 
00538 class Signal(T1,T2,T3,T4,T5,T6,T7) : GenericSignal
00539 {
00540   alias Slot!(T1,T2,T3,T4,T5,T6,T7) CompatibleSlot;
00541   mixin SignalGenericCore;
00542 
00543   /*! Causes all connected \link Slot Slots \endlink to be notified. */
00544   void emit(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
00545   {
00546     if (_owner && _owner.signalsBlocked()) return;
00547     foreach( CompatibleSlot s; _slots ) {
00548       if (s is null) break;
00549       s(t1, t2, t3, t4, t5, t6, t7);
00550     }
00551   }
00552 
00553   void initArguments()
00554   {
00555     _arguments ~= typeid(T1).toString();
00556     _arguments ~= typeid(T2).toString();
00557     _arguments ~= typeid(T3).toString();
00558     _arguments ~= typeid(T4).toString();
00559     _arguments ~= typeid(T5).toString();
00560     _arguments ~= typeid(T6).toString();
00561     _arguments ~= typeid(T7).toString();
00562   }
00563 }
00564 

Generated on Mon Sep 12 22:10:39 2005 for dcouple by  doxygen 1.4.3