ui_actions.c

Go to the documentation of this file.
00001 
00005 /*
00006 Copyright (C) 2002-2010 UFO: Alien Invasion.
00007 
00008 This program is free software; you can redistribute it and/or
00009 modify it under the terms of the GNU General Public License
00010 as published by the Free Software Foundation; either version 2
00011 of the License, or (at your option) any later version.
00012 
00013 This program is distributed in the hope that it will be useful,
00014 but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00016 
00017 See the GNU General Public License for more details.
00018 
00019 You should have received a copy of the GNU General Public License
00020 along with this program; if not, write to the Free Software
00021 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00022 
00023 */
00024 
00025 #include "ui_main.h"
00026 #include "ui_internal.h"
00027 #include "ui_parse.h"
00028 #include "ui_input.h"
00029 #include "ui_actions.h"
00030 #include "node/ui_node_abstractnode.h"
00031 
00032 #include "../client.h"
00033 
00034 typedef struct {
00035     char* token;
00036     int type;
00037     int group;
00038 } ui_typedActionToken_t;
00039 
00044 static const ui_typedActionToken_t actionTokens[] = {
00045 
00046     /* 0x2x */
00047     {"!", EA_OPERATOR_NOT, EA_UNARYOPERATOR},
00048     {"!=", EA_OPERATOR_NE, EA_BINARYOPERATOR},
00049     {"%", EA_OPERATOR_MOD, EA_BINARYOPERATOR},
00050     {"&&", EA_OPERATOR_AND, EA_BINARYOPERATOR},
00051     {"*", EA_OPERATOR_MUL, EA_BINARYOPERATOR},
00052     {"+", EA_OPERATOR_ADD, EA_BINARYOPERATOR},
00053     {"-", EA_OPERATOR_SUB, EA_BINARYOPERATOR},
00054     {"/", EA_OPERATOR_DIV, EA_BINARYOPERATOR},
00055 
00056     /* 0x3x */
00057     {"<", EA_OPERATOR_LT, EA_BINARYOPERATOR},
00058     {"<=", EA_OPERATOR_LE, EA_BINARYOPERATOR},
00059     {"==", EA_OPERATOR_EQ, EA_BINARYOPERATOR},
00060     {">", EA_OPERATOR_GT, EA_BINARYOPERATOR},
00061     {">=", EA_OPERATOR_GE, EA_BINARYOPERATOR},
00062 
00063     /* 0x5x */
00064     {"^", EA_OPERATOR_XOR, EA_BINARYOPERATOR},
00065 
00066     /* 'a'..'z' */
00067     {"and", EA_OPERATOR_AND, EA_BINARYOPERATOR},
00068     {"call", EA_CALL, EA_ACTION},
00069     {"cmd", EA_CMD, EA_ACTION},
00070     {"delete", EA_DELETE, EA_ACTION},
00071     {"elif", EA_ELIF, EA_ACTION},
00072     {"else", EA_ELSE, EA_ACTION},
00073     {"eq", EA_OPERATOR_STR_EQ, EA_BINARYOPERATOR},
00074     {"exists", EA_OPERATOR_EXISTS, EA_UNARYOPERATOR},
00075     {"if", EA_IF, EA_ACTION},
00076     {"ne", EA_OPERATOR_STR_NE, EA_BINARYOPERATOR},
00077     {"not", EA_OPERATOR_NOT, EA_UNARYOPERATOR},
00078     {"or", EA_OPERATOR_OR, EA_BINARYOPERATOR},
00079     {"while", EA_WHILE, EA_ACTION},
00080 
00081     /* 0x7x */
00082     {"||", EA_OPERATOR_OR, EA_BINARYOPERATOR},
00083 };
00084 
00085 static void UI_ExecuteActions(const uiAction_t* firstAction, uiCallContext_t *context);
00086 
00091 static void UI_CheckActionTokenTypeSanity (void)
00092 {
00093     int i;
00094     for (i = 0; i < lengthof(actionTokens) - 1; i++) {
00095         const char *a = actionTokens[i].token;
00096         const char *b = actionTokens[i + 1].token;
00097         if (strcmp(a, b) >= 0) {
00098             Sys_Error("UI_CheckActionTokenTypeSanity: '%s' is before '%s'. actionList must be sorted by alphabet", a, b);
00099         }
00100     }
00101 }
00102 
00110 int UI_GetActionTokenType (const char* token, int group)
00111 {
00112     unsigned char min = 0;
00113     unsigned char max = lengthof(actionTokens);
00114 
00115     while (min != max) {
00116         const int mid = (min + max) >> 1;
00117         const char diff = strcmp(actionTokens[mid].token, token);
00118         assert(mid < max);
00119         assert(mid >= min);
00120 
00121         if (diff == 0) {
00122             if (actionTokens[mid].group == group)
00123                 return actionTokens[mid].type;
00124             else
00125                 return EA_NULL;
00126         }
00127 
00128         if (diff > 0)
00129             max = mid;
00130         else
00131             min = mid + 1;
00132     }
00133     return EA_NULL;
00134 }
00135 
00140 static inline const char* UI_GenCommandReadProperty (const char* input, char* output, int outputSize)
00141 {
00142     assert(input[0] == '<');
00143     outputSize--;
00144     input++;
00145 
00146     while (outputSize && *input != '\0' && *input != ' ' && *input != '>') {
00147         *output++ = *input++;
00148         outputSize--;
00149     }
00150 
00151     if (input[0] != '>')
00152         return NULL;
00153 
00154     output[0] = '\0';
00155     return ++input;
00156 }
00157 
00163 int UI_GetParamNumber (const uiCallContext_t *context)
00164 {
00165     if (context->useCmdParam)
00166         return Cmd_Argc() - 1;
00167     return context->paramNumber;
00168 }
00169 
00176 const char* UI_GetParam (const uiCallContext_t *context, int paramID)
00177 {
00178     linkedList_t *current;
00179     assert(paramID >= 1);
00180 
00181     if (context->useCmdParam)
00182         return Cmd_Argv(paramID);
00183 
00184     if (paramID > context->paramNumber) {
00185         Com_Printf("UI_GetParam: %i out of range\n", paramID);
00186         return "";
00187     }
00188 
00189     current = context->params;
00190     while (paramID > 1) {
00191         current = current->next;
00192         paramID--;
00193     }
00194 
00195     return (char*) current->data;
00196 }
00197 
00206 const char* UI_GenInjectedString (const char* input, qboolean addNewLine, const uiCallContext_t *context)
00207 {
00208     static char cmd[256];
00209     int length = sizeof(cmd) - (addNewLine ? 2 : 1);
00210     static char propertyName[MAX_VAR];
00211     const char *cin = input;
00212     char *cout = cmd;
00213 
00214     while (length && cin[0] != '\0') {
00215         if (cin[0] == '<') {
00216             /* read propertyName between '<' and '>' */
00217             const char *next = UI_GenCommandReadProperty(cin, propertyName, sizeof(propertyName));
00218             if (next) {
00219                 /* cvar injection */
00220                 if (!strncmp(propertyName, "cvar:", 5)) {
00221                     const cvar_t *cvar = Cvar_Get(propertyName + 5, "", 0, NULL);
00222                     const int l = snprintf(cout, length, "%s", cvar->string);
00223                     cout += l;
00224                     cin = next;
00225                     length -= l;
00226                     continue;
00227 
00228                 } else if (!strncmp(propertyName, "node:", 5)) {
00229                     const char *path = propertyName + 5;
00230                     uiNode_t *node;
00231                     const value_t *property;
00232                     const char* string;
00233                     int l;
00234                     UI_ReadNodePath(path, context->source, &node, &property);
00235                     if (!node) {
00236                         Com_Printf("UI_GenInjectedString: Node '%s' wasn't found; '' returned\n", path);
00237 #ifdef DEBUG
00238                         Com_Printf("UI_GenInjectedString: Path relative to '%s'\n", UI_GetPath(context->source));
00239 #endif
00240                         string = "";
00241                     } else if (!property) {
00242                         Com_Printf("UI_GenInjectedString: Property '%s' wasn't found; '' returned\n", path);
00243                         string = "";
00244                     } else {
00245                         string = UI_GetStringFromNodeProperty(node, property);
00246                         if (string == NULL) {
00247                             Com_Printf("UI_GenInjectedString: String getter for '%s' property do not exists; '' injected\n", path);
00248                             string = "";
00249                         }
00250                     }
00251 
00252                     l = snprintf(cout, length, "%s", string);
00253                     cout += l;
00254                     cin = next;
00255                     length -= l;
00256                     continue;
00257 
00258                 /* source path injection */
00259                 } else if (!strncmp(propertyName, "path:", 5)) {
00260                     if (context->source) {
00261                         const char *command = propertyName + 5;
00262                         const uiNode_t *node = NULL;
00263                         if (!strcmp(command, "root"))
00264                             node = context->source->root;
00265                         else if (!strcmp(command, "this"))
00266                             node = context->source;
00267                         else if (!strcmp(command, "parent"))
00268                             node = context->source->parent;
00269                         else
00270                             Com_Printf("UI_GenCommand: Command '%s' for path injection unknown\n", command);
00271 
00272                         if (node) {
00273                             const int l = snprintf(cout, length, "%s", UI_GetPath(node));
00274                             cout += l;
00275                             cin = next;
00276                             length -= l;
00277                             continue;
00278                         }
00279                     }
00280 
00281                 /* no prefix */
00282                 } else {
00283                     /* source property injection */
00284                     if (context->source) {
00285                         /* find property definition */
00286                         const value_t *property = UI_GetPropertyFromBehaviour(context->source->behaviour, propertyName);
00287                         if (property) {
00288                             const char* value;
00289                             int l;
00290                             /* inject the property value */
00291                             value = UI_GetStringFromNodeProperty(context->source, property);
00292                             if (value == NULL)
00293                                 value = "";
00294                             l = snprintf(cout, length, "%s", value);
00295                             cout += l;
00296                             cin = next;
00297                             length -= l;
00298                             continue;
00299                         }
00300                     }
00301 
00302                     /* param injection */
00303                     if (UI_GetParamNumber(context) != 0) {
00304                         int arg;
00305                         const int checked = sscanf(propertyName, "%d", &arg);
00306                         if (checked == 1 && arg >= 1 && arg <= UI_GetParamNumber(context)) {
00307                             const int l = snprintf(cout, length, "%s", UI_GetParam(context, arg));
00308                             cout += l;
00309                             cin = next;
00310                             length -= l;
00311                             continue;
00312                         }
00313                     }
00314                 }
00315             }
00316         }
00317         *cout++ = *cin++;
00318         length--;
00319     }
00320 
00321     /* is buffer too small? */
00322     assert(cin[0] == '\0');
00323 
00324     if (addNewLine)
00325         *cout++ = '\n';
00326 
00327     *cout++ = '\0';
00328 
00329     /* copy the result into a free va slot */
00330     return va("%s", cmd);
00331 }
00332 
00343 static void UI_NodeSetPropertyFromActionValue (uiNode_t *node, const value_t *property, const uiCallContext_t *context, uiAction_t* value)
00344 {
00345     /* @todo we can use a new EA_VALUE type to flag already parsed values, we dont need to do it again and again */
00346     /* pre compute value if possible */
00347     if (value->type == EA_VALUE_STRING) {
00348         const char* string = value->d.terminal.d1.string;
00349         /* @todo here we must catch error in a better way, and using cvar for error code to create unittest automations */
00350         UI_InitRawActionValue(value, node, property, string);
00351     }
00352 
00353     /* decode RAW value */
00354     if (value->type == EA_VALUE_RAW) {
00355         void *rawValue = value->d.terminal.d1.data;
00356         int rawType = value->d.terminal.d2.integer;
00357         UI_NodeSetPropertyFromRAW(node, property, rawValue, rawType);
00358     }
00359     /* else it is an expression */
00360     else {
00362         const char* string = UI_GetStringFromExpression(value, context);
00363         UI_NodeSetProperty(node, property, string);
00364     }
00365 }
00366 
00367 static inline void UI_ExecuteSetAction (const uiAction_t* action, const uiCallContext_t *context)
00368 {
00369     const char* path;
00370     uiNode_t *node;
00371     const value_t *property;
00372     const uiAction_t *left;
00373     uiAction_t *right;
00374 
00375     left = action->d.nonTerminal.left;
00376     if (left == NULL) {
00377         Com_Printf("UI_ExecuteSetAction: Action without left operand skipped.\n");
00378         return;
00379     }
00380 
00381     right = action->d.nonTerminal.right;
00382     if (right == NULL) {
00383         Com_Printf("UI_ExecuteSetAction: Action without right operand skipped.\n");
00384         return;
00385     }
00386 
00387     if (left->type == EA_VALUE_CVARNAME || left->type == EA_VALUE_CVARNAME_WITHINJECTION) {
00388         const char *cvarName;
00389         const char* textValue;
00390 
00391         if (left->type == EA_VALUE_CVARNAME)
00392             cvarName = left->d.terminal.d1.string;
00393         else
00394             cvarName = UI_GenInjectedString(left->d.terminal.d1.string, qfalse, context);
00395 
00396         textValue = UI_GetStringFromExpression(right, context);
00397 
00398         if (textValue[0] == '_')
00399             textValue = _(textValue + 1);
00400 
00401         Cvar_ForceSet(cvarName, textValue);
00402         return;
00403     }
00404 
00405     /* search the node */
00406     if (left->type == EA_VALUE_PATHPROPERTY)
00407         path = left->d.terminal.d1.string;
00408     else if (left->type == EA_VALUE_PATHPROPERTY_WITHINJECTION)
00409         path = UI_GenInjectedString(left->d.terminal.d1.string, qfalse, context);
00410     else
00411         Com_Error(ERR_FATAL, "UI_ExecuteSetAction: Property setter with wrong type '%d'", left->type);
00412 
00413     UI_ReadNodePath(path, context->source, &node, &property);
00414     if (!node) {
00415         Com_Printf("UI_ExecuteSetAction: node \"%s\" doesn't exist (source: %s)\n", path, UI_GetPath(context->source));
00416         return;
00417     }
00418     if (!property) {
00419         Com_Printf("UI_ExecuteSetAction: property \"%s\" doesn't exist (source: %s)\n", path, UI_GetPath(context->source));
00420         return;
00421     }
00422 
00423     UI_NodeSetPropertyFromActionValue(node, property, context, right);
00424 }
00425 
00426 static inline void UI_ExecuteCallAction (const uiAction_t* action, const uiCallContext_t *context)
00427 {
00428     uiNode_t* callNode = NULL;
00429     uiAction_t* param;
00430     uiAction_t* left = action->d.nonTerminal.left;
00431     uiCallContext_t newContext;
00432     const value_t* callProperty = NULL;
00433     const char* path = left->d.terminal.d1.constString;
00434 
00435     if (left->type == EA_VALUE_PATHPROPERTY || left->type == EA_VALUE_PATHNODE)
00436         path = left->d.terminal.d1.string;
00437     else if (left->type == EA_VALUE_PATHPROPERTY_WITHINJECTION || left->type == EA_VALUE_PATHNODE_WITHINJECTION)
00438         path = UI_GenInjectedString(left->d.terminal.d1.string, qfalse, context);
00439     UI_ReadNodePath(path, context->source, &callNode, &callProperty);
00440 
00441     if (callNode == NULL) {
00442         Com_Printf("UI_ExecuteCallAction: Node from path \"%s\" not found (relative to \"%s\").\n", path, UI_GetPath(context->source));
00443         return;
00444     }
00445 
00446     if (callProperty != NULL && callProperty->type != V_UI_ACTION && callProperty->type != V_UI_NODEMETHOD) {
00447         Com_Printf("UI_ExecuteCallAction: Call operand %d unsupported. (%s)\n", callProperty->type, UI_GetPath(callNode));
00448         return;
00449     }
00450 
00451     newContext.source = callNode;
00452     newContext.params = NULL;
00453     newContext.paramNumber = 0;
00454     newContext.varNumber = 0;
00455     newContext.varPosition = context->varPosition + context->varNumber;
00456 
00457     if (action->type == EA_LISTENER) {
00458         newContext.useCmdParam = context->useCmdParam;
00459 
00460         if (!newContext.useCmdParam) {
00461             linkedList_t *p = context->params;
00462             while (p) {
00463                 const char* value = (char*) p->data;
00464                 LIST_AddString(&newContext.params, value);
00465                 newContext.paramNumber++;
00466                 p = p->next;
00467             }
00468         }
00469     } else {
00470         newContext.useCmdParam = qfalse;
00471 
00472         param = action->d.nonTerminal.right;
00473         while (param) {
00474             const char* value;
00475             value = UI_GetStringFromExpression(param, context);
00476             LIST_AddString(&newContext.params, value);
00477             newContext.paramNumber++;
00478             param = param->next;
00479         }
00480     }
00481 
00482     if (callProperty == NULL || callProperty->type == V_UI_ACTION) {
00483         uiAction_t *actionsRef = NULL;
00484         if (callProperty == NULL)
00485             actionsRef = callNode->onClick;
00486         else
00487             actionsRef = *(uiAction_t **) ((byte *) callNode + callProperty->ofs);
00488         UI_ExecuteActions(actionsRef, &newContext);
00489     } else if (callProperty->type == V_UI_NODEMETHOD) {
00490         uiNodeMethod_t func = (uiNodeMethod_t) callProperty->ofs;
00491         func(callNode, &newContext);
00492     } else {
00493         /* unreachable, already checked few line before */
00494         assert(qfalse);
00495     }
00496 
00497     LIST_Delete(&newContext.params);
00498 }
00499 
00505 uiValue_t* UI_GetVariable (const uiCallContext_t *context, int relativeVarId)
00506 {
00507     const int varId = context->varPosition + relativeVarId;
00508     assert(relativeVarId >= 0);
00509     assert(relativeVarId < context->varNumber);
00510     return &(ui_global.variableStack[varId]);
00511 }
00512 
00513 static void UI_ReleaseVariable (uiValue_t *variable)
00514 {
00516     switch (variable->type) {
00517     case EA_VALUE_STRING:
00518         UI_FreeStringProperty(variable->value.string);
00519         break;
00520     case EA_VALUE_FLOAT:
00521     case EA_VALUE_NODE:
00522     case EA_VALUE_CVAR:
00523         /* nothing */
00524         break;
00525     default:
00526         Com_Error(ERR_FATAL, "UI_ReleaseVariable: Variable type \"%d\" unsupported", variable->type);
00527     }
00528 
00529     /* bug safe, but useless */
00530     memset(variable, 0, sizeof(*variable));
00531 }
00532 
00538 static void UI_ExecuteAction (const uiAction_t* action, uiCallContext_t *context)
00539 {
00540     switch (action->type) {
00541     case EA_NULL:
00542         /* do nothing */
00543         break;
00544 
00545     case EA_CMD:
00546         /* execute a command */
00547         if (action->d.terminal.d1.string)
00548             Cbuf_AddText(UI_GenInjectedString(action->d.terminal.d1.string, qtrue, context));
00549         break;
00550 
00551     case EA_CALL:
00552     case EA_LISTENER:
00553         UI_ExecuteCallAction(action, context);
00554         break;
00555 
00556     case EA_POPVARS:
00557         {
00558             int i;
00559             const int number = action->d.terminal.d1.integer;
00560             assert(number <= context->varNumber);
00561             for (i = 0; i < number; i++) {
00562                 const int varId = context->varPosition + context->varNumber - i - 1;
00563                 UI_ReleaseVariable(&(ui_global.variableStack[varId]));
00564             }
00565             context->varNumber -= number;
00566         }
00567         break;
00568 
00569     case EA_PUSHVARS:
00570 #ifdef DEBUG
00571         /* check sanity */
00573 #endif
00574         context->varNumber += action->d.terminal.d1.integer;
00575         if (context->varNumber >= UI_MAX_VARIABLESTACK)
00576             Com_Error(ERR_FATAL, "UI_ExecuteAction: Variable stack full. UI_MAX_VARIABLESTACK hited.");
00577         break;
00578 
00579     case EA_ASSIGN:
00580         UI_ExecuteSetAction(action, context);
00581         break;
00582 
00583     case EA_DELETE:
00584         {
00585             const char* cvarname = action->d.nonTerminal.left->d.terminal.d1.constString;
00586             Cvar_Delete(cvarname);
00587             break;
00588         }
00589 
00590     case EA_WHILE:
00591         {
00592             int loop = 0;
00593             while (UI_GetBooleanFromExpression(action->d.nonTerminal.left, context)) {
00594                 UI_ExecuteActions(action->d.nonTerminal.right, context);
00595                 if (loop > 1000) {
00596                     Com_Printf("UI_ExecuteAction: Infinite loop. Force breaking 'while'\n");
00597                     break;
00598                 }
00599                 loop++;
00600             }
00601             break;
00602         }
00603 
00604     case EA_IF:
00605         if (UI_GetBooleanFromExpression(action->d.nonTerminal.left, context)) {
00606             UI_ExecuteActions(action->d.nonTerminal.right, context);
00607             return;
00608         }
00609         action = action->next;
00610         while (action && action->type == EA_ELIF) {
00611             if (UI_GetBooleanFromExpression(action->d.nonTerminal.left, context)) {
00612                 UI_ExecuteActions(action->d.nonTerminal.right, context);
00613                 return;
00614             }
00615             action = action->next;
00616         }
00617         if (action && action->type == EA_ELSE) {
00618             UI_ExecuteActions(action->d.nonTerminal.right, context);
00619         }
00620         break;
00621 
00625     case EA_ELSE:
00626     case EA_ELIF:
00627         /* previous EA_IF execute this action */
00628         break;
00629 
00630     default:
00631         Com_Error(ERR_FATAL, "UI_ExecuteAction: Unknown action type %i", action->type);
00632     }
00633 }
00634 
00635 static void UI_ExecuteActions (const uiAction_t* firstAction, uiCallContext_t *context)
00636 {
00637     static int callnumber = 0;
00638     const uiAction_t *action;
00639     if (callnumber++ > 20) {
00640         Com_Printf("UI_ExecuteActions: Break possible infinit recursion\n");
00641         return;
00642     }
00643     for (action = firstAction; action; action = action->next) {
00644         UI_ExecuteAction(action, context);
00645     }
00646     callnumber--;
00647 }
00648 
00652 void UI_ExecuteConFuncActions (uiNode_t* source, const uiAction_t* firstAction)
00653 {
00654     uiCallContext_t context;
00655     memset(&context, 0, sizeof(context));
00656     context.source = source;
00657     context.useCmdParam = qtrue;
00658     UI_ExecuteActions(firstAction, &context);
00659 }
00660 
00661 void UI_ExecuteEventActions (uiNode_t* source, const uiAction_t* firstAction)
00662 {
00663     uiCallContext_t context;
00664     memset(&context, 0, sizeof(context));
00665     context.source = source;
00666     context.useCmdParam = qfalse;
00667     UI_ExecuteActions(firstAction, &context);
00668 }
00669 
00675 qboolean UI_IsInjectedString (const char *string)
00676 {
00677     const char *c = string;
00678     assert(string);
00679     while (*c != '\0') {
00680         if (*c == '<') {
00681             const char *d = c + 1;
00682             if (*d != '>') {
00683                 while (*d) {
00684                     if (*d == '>')
00685                         return qtrue;
00686                     if (*d == ' ' || *d == '\t' || *d == '\n' || *d == '\r')
00687                         break;
00688                     d++;
00689                 }
00690             }
00691         }
00692         c++;
00693     }
00694     return qfalse;
00695 }
00696 
00702 void UI_FreeStringProperty (void* pointer)
00703 {
00704     /* skip const string */
00705     if ((uintptr_t)ui_global.adata <= (uintptr_t)pointer && (uintptr_t)pointer < (uintptr_t)ui_global.adata + (uintptr_t)ui_global.adataize)
00706         return;
00707 
00708     /* skip pointer out of mn_dynStringPool */
00709     if (!_Mem_AllocatedInPool(ui_dynStringPool, pointer))
00710         return;
00711 
00712     Mem_Free(pointer);
00713 }
00714 
00720 uiAction_t* UI_AllocStaticCommandAction (char *command)
00721 {
00722     uiAction_t* action = UI_AllocStaticAction();
00723     action->type = EA_CMD;
00724     action->d.terminal.d1.string = command;
00725     return action;
00726 }
00727 
00738 void UI_PoolAllocAction (uiAction_t** action, int type, const void *data)
00739 {
00740     if (*action)
00741         Com_Error(ERR_FATAL, "There is already an action assigned");
00742     *action = (uiAction_t *)Mem_PoolAlloc(sizeof(**action), ui_sysPool, 0);
00743     (*action)->type = type;
00744     switch (type) {
00745     case EA_CMD:
00746         (*action)->d.terminal.d1.string = Mem_PoolStrDup((const char *)data, ui_sysPool, 0);
00747         break;
00748     default:
00749         Com_Error(ERR_FATAL, "Action type %i is not yet implemented", type);
00750     }
00751 }
00752 
00759 void UI_AddListener (uiNode_t *node, const value_t *property, uiNode_t *functionNode)
00760 {
00761     uiAction_t *lastAction;
00762     uiAction_t *action;
00763     uiAction_t *value;
00764 
00765     if (node->dynamic) {
00766         Com_Printf("UI_AddListener: '%s' is a dynamic node. We can't listen it.\n", UI_GetPath(node));
00767         return;
00768     }
00769     if (functionNode->dynamic) {
00770         Com_Printf("UI_AddListener: '%s' is a dynamic node. It can't be a listener callback.\n", UI_GetPath(functionNode));
00771         return;
00772     }
00773 
00774     /* create the call action */
00775     action = (uiAction_t*) Mem_PoolAlloc(sizeof(*action), ui_sysPool, 0);
00776     value = (uiAction_t*) Mem_PoolAlloc(sizeof(*action), ui_sysPool, 0);
00777     value->d.terminal.d1.constString = Mem_PoolStrDup(UI_GetPath(functionNode), ui_sysPool, 0);
00778     value->next = NULL;
00779     action->type = EA_LISTENER;
00780     action->d.nonTerminal.left = value;
00782     action->d.terminal.d2.data = &functionNode->onClick;
00783     action->next = NULL;
00784 
00785     /* insert the action */
00786     lastAction = *(uiAction_t**)((char*)node + property->ofs);
00787     if (lastAction) {
00788         while (lastAction->next)
00789             lastAction = lastAction->next;
00790         lastAction->next = action;
00791     } else
00792         *(uiAction_t**)((char*)node + property->ofs) = action;
00793 }
00794 
00798 static void UI_AddListener_f (void)
00799 {
00800     uiNode_t *node;
00801     uiNode_t *function;
00802     const value_t *property;
00803 
00804     if (Cmd_Argc() != 3) {
00805         Com_Printf("Usage: %s <pathnode@event> <pathnode>\n", Cmd_Argv(0));
00806         return;
00807     }
00808 
00809     UI_ReadNodePath(Cmd_Argv(1), NULL, &node, &property);
00810     if (node == NULL) {
00811         Com_Printf("UI_AddListener_f: '%s' node not found.\n", Cmd_Argv(1));
00812         return;
00813     }
00814     if (property == NULL || property->type != V_UI_ACTION) {
00815         Com_Printf("UI_AddListener_f: '%s' property not found, or is not an event.\n", Cmd_Argv(1));
00816         return;
00817     }
00818 
00819     function = UI_GetNodeByPath(Cmd_Argv(2));
00820     if (function == NULL) {
00821         Com_Printf("UI_AddListener_f: '%s' node not found.\n", Cmd_Argv(2));
00822         return;
00823     }
00824 
00825     UI_AddListener(node, property, function);
00826 }
00827 
00834 void UI_RemoveListener (uiNode_t *node, const value_t *property, uiNode_t *functionNode)
00835 {
00836     void *data;
00837     uiAction_t *lastAction;
00838 
00839     /* data we must remove */
00840     data = (void*) &functionNode->onClick;
00841 
00842     /* remove the action */
00843     lastAction = *(uiAction_t**)((char*)node + property->ofs);
00844     if (lastAction) {
00845         uiAction_t *tmp = NULL;
00846         if (lastAction->type == EA_LISTENER && lastAction->d.terminal.d2.data == data) {
00847             tmp = lastAction;
00848             *(uiAction_t**)((char*)node + property->ofs) = lastAction->next;
00849         } else {
00850             while (lastAction->next) {
00851                 if (lastAction->next->type == EA_LISTENER && lastAction->next->d.terminal.d2.data == data)
00852                     break;
00853                 lastAction = lastAction->next;
00854             }
00855             if (lastAction->next) {
00856                 tmp = lastAction->next;
00857                 lastAction->next = lastAction->next->next;
00858             }
00859         }
00860         if (tmp) {
00861             uiAction_t* value = tmp->d.nonTerminal.left;
00862             Mem_Free(value->d.terminal.d1.data);
00863             Mem_Free(value);
00864             Mem_Free(tmp);
00865         }
00866         else
00867             Com_Printf("UI_RemoveListener_f: '%s' into '%s' not found.\n", Cmd_Argv(2), Cmd_Argv(1));
00868     }
00869 }
00870 
00874 static void UI_RemoveListener_f (void)
00875 {
00876     uiNode_t *node;
00877     uiNode_t *function;
00878     const value_t *property;
00879 
00880     if (Cmd_Argc() != 3) {
00881         Com_Printf("Usage: %s <pathnode@event> <pathnode>\n", Cmd_Argv(0));
00882         return;
00883     }
00884 
00885     UI_ReadNodePath(Cmd_Argv(1), NULL, &node, &property);
00886     if (node == NULL) {
00887         Com_Printf("UI_RemoveListener_f: '%s' node not found.\n", Cmd_Argv(1));
00888         return;
00889     }
00890     if (property == NULL || property->type != V_UI_ACTION) {
00891         Com_Printf("UI_RemoveListener_f: '%s' property not found, or is not an event.\n", Cmd_Argv(1));
00892         return;
00893     }
00894 
00895     function = UI_GetNodeByPath(Cmd_Argv(2));
00896     if (function == NULL) {
00897         Com_Printf("UI_RemoveListener_f: '%s' node not found.\n", Cmd_Argv(2));
00898         return;
00899     }
00900 
00901     UI_RemoveListener(node, property, function);
00902 }
00903 
00904 void UI_InitActions (void)
00905 {
00906     UI_CheckActionTokenTypeSanity();
00907     Cmd_AddCommand("mn_addlistener", UI_AddListener_f, "Add a function into a node event");
00908     Cmd_AddCommand("mn_removelistener", UI_RemoveListener_f, "Remove a function from a node event");
00909 }

Generated by  doxygen 1.6.2