// ###########################################################################
 // PURPOSE: Attach an event handler to an Abatan object.
 // AUTHOR:  Alan Turransky
 // ###########################################################################
 
 // ###########################################################################
 // INCLUDES
 // ###########################################################################
 #include <stdio.h>                                                            // STANDARD I/O
 #include <windows.c.h>                                                        // WINDOW DEFINITIONS
 #include <structures.h>                                                       // STRUCTURE DEFINITIONS
 #include <stdlib.h>                                                           // STANDARD LIBRARY DEFINITIONS
 
 // ###########################################################################
 // NAME:     DoMethod(S, JUSTDOWN, ConstrainHeadline, AlignmentTest);
 // PURPOSE:  Attaches before and after methods to a symbol.
 // ARGS:     SYM  *S         - An existing symbol.
 //           int (*before)() - a function pointer.
 //           int (*after)()  - a function pointer.
 //           int   mask      - a button state.
 // ###########################################################################
 int DoMethod(SYM *S, int mask, int (*before)(), int (*after)())
 {
    WRAPSTRUCT *wrap;                                                          // NEW WRAPPER STRUCTURE
    COMMAND    *comm;                                                          // TEMP COMMAND STRUCTURE
    char      **args;                                                          // TEMP ARGUMENTS POINTER
    int      (**func)();                                                       // TEMP FUNCTION POINTER
    int         indx;                                                          // INDEX COUNTER
 
    if((wrap = AllocateNewWrapper()) == NULL){                                 // TRY TO ALLOCATE A NEW WRAPPER
       null_error_return("DoMethod: out of memory.\n");                        // IF WE CANT, RETURN A NULL ERROR MESSAGE
    }
    else{
       InitializeNewWrapper(pointer, mask, FALSE);                             // SET THE WRAPPER'S POINTER, BUTTON STATE AND HIT POINT
       if(comm = S->commands){                                                 // IF THERE IS A COMMAND LIST TO ATTACH
          while((comm->next) && (comm->id != DO)) comm = comm->next;           // GO TO THE START OF THE LIST (i.e., first in, last out)
          if(comm->id != DO){                                                  // IF THE FIRST FUNCTION ISNT A "DO" (i.e., no commands to attach)
             AttachCommand(S, DO, before, wrap);                               // ATTACH THE BEFORE METHOD
             AttachCommand(S, DO, after,  wrap);                               // ATTACH THE AFTER METHOD
          }
          else{                                                                // ELSE THE COMMAND IS A "DO" COMMAND
             if((func  = (int (**)())malloc(comm->num*sizeof(int (*)()))) &&   // IF WE CAN ALLOCATE A FUNCTION POINTER TO STORE THE COMMAND AND 
                (args  = (char    **)malloc(comm->num*sizeof(char *)))){       // A POINTER TO AN ARRAY OF CHAR POINTERS TO STORE THE ARGUMENTS
                 *(func) = before;                                             // SET THE FIRST FUNCTION TO BE CALLED EQUAL TO THE "BEFORE" METHOD
                 *(args) = (char *)wrap;                                       // SET THE BEFORE METHOD'S ARGUMENT EQUAL TO THE WRAPPER
                   for(indx=0; comm->num++; indx<comm->numfuncs-1; indx++){    // LOOP THROUGH THE LIST OF COMMANDS
                     *(func+indx+1) = *(comm->functions+indx);                 // INCREMENTALLY ADD THE LIST OF FUNCTIONS TO THE FUNCTION POINTER
                     *(args+indx+1) = *(comm->arguments+indx);                 // INCREMENTALLY ADD THE LIST OF ARGUMENTS TO THE ARGUMENTS POINTER
                   }
                   if(comm->num > 1){                                          // IF THERE ARE STILL SOME COMMANDS LEFT
                      free(comm->functions);                                   // FREE THEM
                      free(comm->arguments);                                   // AND FREE THEIR ARGUMENTS LIST
                   }
                   comm->functions = func;                                     // RE-ASSIGN THE ORIGINAL COMMAND LIST EQUAL TO THE NEW LIST
                   comm->arguments = args;                                     // RE-ASSIGN THE ARGUMENTS LIST AS WELL
                   AttachCommand(S, DO, after, wrap);                          // FINALLY, ATTACH THE "AFTER" METHOD
             }
          }
       }
    }
 }