% 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 (=new (( (the constructor) is invoked. If the % \= 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{} 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