% Copyright 2006 by Till Tantau
%
% This file may be distributed and/or modified
%
% 1. under the LaTeX Project Public License and/or
% 2. under the GNU Public License.
%
% See the file doc/generic/pgf/licenses/LICENSE for more details.

\ProvidesFileRCS $Header: /cvsroot/pgf/pgf/generic/pgf/modules/pgfmoduleoo.code.tex,v 1.8 2010/02/22 22:49:21 tantau Exp $



% Support of object-oriented programming in TeX.


% The oo support works as follows:
%
% The main supported concepts are classes, objects, methods,
% attributes and signal/slots. A class defines a set of methods, which are,
% in the end, just normal TeX macros. Once a class has been created,
% it can be instantiated by calling the \pgfoonew command, resulting
% in a new object. Objects are local to their group. Given an object,
% you can send it a message, resulting in the method code of the
% object's method to be executed. While an object exists, it has a set
% of attributes whose values can change over time. Attributes values
% are not local to TeX groups, rather their life-cycle is the same as
% the object's (which, however, is local to the group in which the
% object was declared).
%
% The implementation is as follows: There is an ID counter that is
% increased each time an object is created. This counter is local to
% the group, which means that when a group ends the counter will
% revert to the previous value, allowing objects to the reused in
% subsequent groups.
%
% A method is just a normal \TeX macro, but to call it you write
% \objecthandle.methodname(parameters). The \objecthandle is a macro
% that is created when you say \pgfoonew. The special object
% \pgfoothis is the current object.
%
% Attributes are stored globally in internal TeX macros whose name is
% composed of the object number and the attribute name.




% Declares a class
%
% #1 = class name
% #2 = methods
%
% Description:
%
% This command declares a class for futurue use. Inside #2, the macro
% \method can be used to declare a method. The \method macro takes a
% method name, parameters (methods are normal TeX macros, after all)
% and body.

\long\def\pgfooclass#1#2{%
  \def\pgfoo@classname{#1}%
  \let\pgfoo@origmethod=\method%
  \let\pgfoo@origattribute=\attribute%
  \let\method=\pgfoo@declaremethod%
  \let\attribute=\pgfoo@declareattribute%
  \let\pgfoo@attribute@collection=\pgfutil@empty%
  \let\pgfoo@attribute@collection@gc=\pgfutil@empty%
  #2%
  % Attribute initializer:
  \expandafter\let\csname pgfooY\pgfoo@classname.@pgfooinit\endcsname\pgfoo@attribute@collection%
  \expandafter\let\csname pgfooY\pgfoo@classname.@pgfoogc\endcsname\pgfoo@attribute@collection@gc%
  % Always persent methods:
  \expandafter\let\csname pgfooY\pgfoo@classname.get handle\endcsname\pgfoo@obj%
  \expandafter\let\csname pgfooY\pgfoo@classname.get id\endcsname\pgfoo@id%
  \let\method=\pgfoo@origmethod%
  \let\attribute=\pgfoo@origattribute%
}



% Declare a method
%
% Description:
%
% Defines a method. To use/invoke this method for an object \object,
% you write \object.methodname(parameters). This will cause the method
% body to be invoked with the argument "(parameters)".
%
% To define the method \method should be directly followed by the
% method name and, then, by (, followed by a parameter pattern,
% followed by ), followed by the message body. Spaces are allowed only
% after "\method" and after the closing ).
%
% Example:
%
% \pgfooclass{MyPlot}{
%
%   \attribute x=0;
%   \attribute y=0;
%
%   \method MyPlot() {
%   }
%
%   \method getX(#1) {
%     \pgfooget{x}{#1}
%   }
%
%   \method setPoint(#1,#2) {
%     \pgfooset{x}{#1}
%     \pgfooset{y}{#2}
%   }
% }

\def\pgfoo@declaremethod#1(#2)#3{%
  \expandafter\long\expandafter\def\csname pgfooY\pgfoo@classname.#1\endcsname(#2){#3}%
}


% Declare an attribute
%
% #1 = attribute name
% #2 = optional default value
%
% Description:
%
% Declares the attribute #1 for the current class. If the attribute
% name is followed by =, the text following the equal sign is the
% default value.

\def\pgfoo@declareattribute#1;{%
  \pgfutil@in@{ =}{#1}%
  \ifpgfutil@in@%
    \pgfoo@declareunpackspace#1;%
  \else%
    \pgfutil@in@={#1}%
    \ifpgfutil@in@%
      \pgfoo@declareunpack#1;%
    \else%
      \expandafter\def\expandafter\pgfoo@attribute@collection\expandafter{\pgfoo@attribute@collection\pgfoolet{#1}\pgfutil@empty}%  
      \expandafter\def\expandafter\pgfoo@attribute@collection@gc\expandafter{\pgfoo@attribute@collection@gc\pgfoolet{#1}\relax}%  
    \fi%  
  \fi%  
}
\def\pgfoo@declareunpack#1=#2;{%
  \expandafter\def\expandafter\pgfoo@attribute@collection\expandafter{\pgfoo@attribute@collection\pgfooset{#1}{#2}}%  
  \expandafter\def\expandafter\pgfoo@attribute@collection@gc\expandafter{\pgfoo@attribute@collection@gc\pgfoolet{#1}\relax}%  
}
\def\pgfoo@declareunpackspace#1 =#2;{%
  \expandafter\def\expandafter\pgfoo@attribute@collection\expandafter{\pgfoo@attribute@collection\pgfooset{#1}{#2}}%  
  \expandafter\def\expandafter\pgfoo@attribute@collection@gc\expandafter{\pgfoo@attribute@collection@gc\pgfoolet{#1}\relax}%  
}


\newcount\pgfoo@objectcount
\newcount\pgfoothis@count

% The attribute value method
%
% #1 = attr
%
% This method inserts the current value of the given attribute for the
% current object.

\def\pgfoovalueof#1{%
  \csname pgfooX\the\pgfoothis@count @#1\endcsname%
}


% The attribute get method
%
% #1 = attr
% #2 = macro
%
% This method makes the macro equal to the current value of the
% attribute for the current object.

\def\pgfooget#1#2{%
  \expandafter\let\expandafter#2\csname pgfooX\the\pgfoothis@count @#1\endcsname%
}


% The attribute set method
%
% #1 = attr
% #2 = value
%
% This method sets the given attribute for the current object to the
% given value.

\long\def\pgfooset#1#2{%
  \expandafter\gdef\csname pgfooX\the\pgfoothis@count @#1\endcsname{#2}%
}


% The attribute set method (expanded version)
%
% #1 = attr
% #2 = value
%
% This method sets the given attribute for the current object to the
% expanded version of the given value.

\long\def\pgfooeset#1#2{%
  \expandafter\xdef\csname pgfooX\the\pgfoothis@count @#1\endcsname{#2}%
}


% The attribute let method
%
% #1 = attr
% #2 = value
%
% This method sets the given attribute for the current object to the
% given value using \let.

\def\pgfoolet#1#2{%
  \expandafter\global\expandafter\let\csname pgfooX\the\pgfoothis@count @#1\endcsname#2%
}


% Add something to an attribute at the end
%
% #1 = attr
% #2 = code
%
% This method adds the give code to the attr at the end.

\def\pgfooappend#1#2{%
  \expandafter\expandafter\expandafter\def%
  \expandafter\expandafter\expandafter\pgf@oo@temp\expandafter\expandafter\expandafter{\csname pgfooX\the\pgfoothis@count @#1\endcsname#2}%
  \expandafter\global\expandafter\let\csname pgfooX\the\pgfoothis@count @#1\endcsname\pgf@oo@temp%
}


% Add something to an attribute at the beginning
%
% #1 = attr
% #2 = code
%
% This method adds the give code to the attr at the beginning.

\def\pgfooprefix#1#2{%
  \pgfooget{#1}\pgf@oo@temp%
  \def\pgf@oo@@temp{#2}%
  \expandafter\expandafter\expandafter\def%
  \expandafter\expandafter\expandafter\pgf@oo@temp%
  \expandafter\expandafter\expandafter{\expandafter\pgf@oo@@temp\pgf@oo@temp}%
  \pgfoolet{#1}\pgf@oo@temp%
}



% Instantiate an object
%
% Possible syntax:
%
% 1) \pgfoonew new <class name>(<constructor parameters)
% 2) \pgfoonew \<objectname>=new <class name>(<constructor parameters)
% 3) \pgfoonew{attribute}=new <class name>(<constructor parameters)
%
% Description:
%
% Creates an object. After the object has been created, the method
% called <class name> (the constructor) is invoked. If the
% \<objectname>= part is present, the macro is assigned to the newly
% created object.

\def\pgfoonew{%
  \pgfutil@ifnextchar n{%
    \pgfoo@new\pgfoo@dummy=%
  }{%
    \pgfutil@ifnextchar\bgroup{%
      \pgfoo@new@attribute%
    }{%
      \pgfoo@new%
    }%
  }%
}
\def\pgfoo@new#1=new #2(#3){%
  \expandafter\ifx\csname pgfooY#2.get id\endcsname\relax%
    \PackageError{pgfoo}{Unknown class '#2'}{}%
  \else%
    \advance\pgfoo@objectcount by 1\relax%
    \edef\pgfoolastobj{\noexpand\pgfoo@caller{\the\pgfoo@objectcount}}%
    \expandafter\gdef\csname pgfooX\the\pgfoo@objectcount @class\endcsname{#2}%
    \let#1\pgfoolastobj%
    {%
      \pgfoothis@count\pgfoo@objectcount%
      \csname pgfooY\csname pgfooX\the\pgfoothis@count @class\endcsname.@pgfooinit\endcsname%
    }%
    \pgfoolastobj.#2(#3)%
    \aftergroup\pgfoogc% cleanup after group
  \fi%
}
\def\pgfoo@new@attribute#1=new #2(#3){%
  \pgfoo@new\pgf@oo@temp=new #2(#3)%
  \pgfoolet{#1}\pgf@oo@temp%
}


\def\pgfoo@caller#1.#2(#3){%
  \def\pgf@marshal{%
    \pgfoothis@count=#1\relax%
    \csname pgfooY\csname pgfooX\the\pgfoothis@count @class\endcsname.#2\endcsname(#3)%
  }%
  \expandafter\pgf@marshal\expandafter\pgfoothis@count\the\pgfoothis@count\relax%
}
\let\pgfoo@orig@caller=\pgfoo@caller

% The special "this" object

\def\pgfoothis.#1(#2){%
  \csname pgfooY\csname pgfooX\the\pgfoothis@count @class\endcsname.#1\endcsname(#2)%
}





% Get the object id
%
% #1 = macro to store the id
%
% Description:
%
% This special method allows you to access the object id. You can then
% use \pgfoocall to call a method using this id. This is mainly useful
% when you wish to store the id for a longer time.

\def\pgfoo@id(#1){%
  \edef#1{\the\pgfoothis@count}%
}

% Yields the object with the given id
%
% #1 = id
%
% Description:
%
% Given an object id, \pgfooobj{<id>} will yield the object having
% this id.

\def\pgfooobj#1{%
  \pgfoo@caller{#1}%
}


% Get the object
%
% #1 = macro to store the object
%
% Description:
%
% This special method allows you to get a new handle to a given
% object. If \obj is an object, you could normally just say
% \let#1=\obj. However, if \obj happens to be \pgfoothis, then you may
% wish to get a handle to the object itself, not to the special macro
% \pgfoothis. In this case you can say \obj.get handle(#1).

\def\pgfoo@obj(#1){%
  \edef#1{\noexpand\pgfoo@caller{\the\pgfoothis@count}}%
}



% The garbage collector
%
% Description:
%
% This method frees space ocupied by unused objects. Garbage are
% objects that have been destroyed because of the end of the scope in
% which they were created. In this case, however, the memory used by
% this object is not immediately reused because the attributes of the 
% object are actually stored in global variables. When the garbage
% collector is called, it will set all these global variables to
% \relax, thereby ensuring that no memory is needed for them.

\def\pgfoogc{%
  {%
    % We do this in a group...
    \pgfoothis@count\pgfoo@objectcount% this is temporary...
    \let\pgfoo@next=\pgfoo@dogc%
    \pgfoo@next%
  }%
}
\def\pgfoo@dogc{%
  \advance\pgfoothis@count by 1\relax%
  \expandafter\ifx\csname pgfooX\the\pgfoothis@count @class\endcsname\relax%
    \let\pgfoo@next=\relax%
  \else%
    % Cleanup this object:
    % The following is the fast version of \pgfooobj{\the\pgfoo@objectcount}.@pgfoogc:
    \csname pgfooY\csname pgfooX\the\pgfoothis@count @class\endcsname.@pgfoogc\endcsname%
    \expandafter\global\expandafter\let\csname pgfooX\the\pgfoothis@count @class\endcsname\relax%
  \fi%
  \pgfoo@next%
}
  



%
%
% Signal class
%
%

\pgfooclass{signal}
{
  %
  % This class implements signals.
  %
  % After you have created a signal object, you can call
  % connect to connect a slot. Then, whenever the emit method is
  % called, all connected methods get called.

  % Attribute
  \attribute emitter;
  % Collects the objects that should be called.
  
  % Constructor
  \method signal() {}

  % Connect a slot (method) #2 of object #1
  \method connect(#1,#2) {%
    {%
      #1.get id(\pgf@tempid)%
      % Save in emmiter
      \pgfooget{emitter}\pgf@temp%
      \let\pgfoo@signal@call=\relax% avoid expansion
      \edef\pgf@temp{\pgf@temp\pgfoo@signal@call{\pgf@tempid}{#2}}%
      \pgfoolet{emitter}\pgf@temp%
    }%
  }

  \def\pgfoo@signal@call#1#2{%
    \def\pgf@temp{\pgfooobj{#1}.#2}%
    \expandafter\pgf@temp\expandafter(\pgfoo@signal@args)%
  }
  
  % Emit a signal
  \method emit(#1) {%
    \def\pgfoo@signal@args{#1}%
    \pgfoovalueof{emitter}
  }
}




\endinput
