00001
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
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
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
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
00064 {"^", EA_OPERATOR_XOR, EA_BINARYOPERATOR},
00065
00066
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
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
00217 const char *next = UI_GenCommandReadProperty(cin, propertyName, sizeof(propertyName));
00218 if (next) {
00219
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
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
00282 } else {
00283
00284 if (context->source) {
00285
00286 const value_t *property = UI_GetPropertyFromBehaviour(context->source->behaviour, propertyName);
00287 if (property) {
00288 const char* value;
00289 int l;
00290
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
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
00322 assert(cin[0] == '\0');
00323
00324 if (addNewLine)
00325 *cout++ = '\n';
00326
00327 *cout++ = '\0';
00328
00329
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
00346
00347 if (value->type == EA_VALUE_STRING) {
00348 const char* string = value->d.terminal.d1.string;
00349
00350 UI_InitRawActionValue(value, node, property, string);
00351 }
00352
00353
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
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
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
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
00524 break;
00525 default:
00526 Com_Error(ERR_FATAL, "UI_ReleaseVariable: Variable type \"%d\" unsupported", variable->type);
00527 }
00528
00529
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
00543 break;
00544
00545 case EA_CMD:
00546
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
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
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
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
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
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
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
00840 data = (void*) &functionNode->onClick;
00841
00842
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 }