00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "../ui_main.h"
00027 #include "../ui_internal.h"
00028 #include "../ui_font.h"
00029 #include "../ui_actions.h"
00030 #include "../ui_parse.h"
00031 #include "../ui_render.h"
00032 #include "ui_node_text.h"
00033 #include "ui_node_abstractnode.h"
00034
00035 #include "../../client.h"
00036 #include "../../../shared/parse.h"
00037
00038 #define EXTRADATA_TYPE textExtraData_t
00039 #define EXTRADATA(node) UI_EXTRADATA(node, EXTRADATA_TYPE)
00040 #define EXTRADATACONST(node) UI_EXTRADATACONST(node, EXTRADATA_TYPE)
00041
00042 static void UI_TextUpdateCache(uiNode_t *node);
00043
00044 static void UI_TextValidateCache (uiNode_t *node)
00045 {
00046 int v;
00047 if (EXTRADATA(node).dataID == TEXT_NULL || node->text != NULL)
00048 return;
00049
00050 v = UI_GetDataVersion(EXTRADATA(node).dataID);
00051 if (v != EXTRADATA(node).versionId) {
00052 UI_TextUpdateCache(node);
00053 }
00054 }
00055
00059 void UI_TextNodeSelectLine (uiNode_t *node, int num)
00060 {
00061 if (EXTRADATA(node).textLineSelected == num)
00062 return;
00063 EXTRADATA(node).textLineSelected = num;
00064 if (node->onChange)
00065 UI_ExecuteEventActions(node, node->onChange);
00066 }
00067
00072 void UI_TextScrollEnd (const char* nodePath)
00073 {
00074 uiNode_t *node = UI_GetNodeByPath(nodePath);
00075 if (!node) {
00076 Com_DPrintf(DEBUG_CLIENT, "Node '%s' could not be found\n", nodePath);
00077 return;
00078 }
00079
00080 if (!UI_NodeInstanceOf(node, "text")) {
00081 Com_Printf("UI_TextScrollBottom: '%s' node is not an 'text'.\n", Cmd_Argv(1));
00082 return;
00083 }
00084
00085 UI_TextValidateCache(node);
00086
00087 if (EXTRADATA(node).super.scrollY.fullSize > EXTRADATA(node).super.scrollY.viewSize) {
00088 EXTRADATA(node).super.scrollY.viewPos = EXTRADATA(node).super.scrollY.fullSize - EXTRADATA(node).super.scrollY.viewSize;
00089 UI_ExecuteEventActions(node, EXTRADATA(node).super.onViewChange);
00090 }
00091 }
00092
00100 static int UI_TextNodeGetLine (const uiNode_t *node, int x, int y)
00101 {
00102 int lineHeight;
00103 int line;
00104 assert(UI_NodeInstanceOf(node, "text"));
00105
00106 lineHeight = EXTRADATACONST(node).lineHeight;
00107 if (lineHeight == 0) {
00108 const char *font = UI_GetFontFromNode(node);
00109 lineHeight = UI_FontGetHeight(font);
00110 }
00111
00112 UI_NodeAbsoluteToRelativePos(node, &x, &y);
00113 y -= node->padding;
00114
00115
00116 if (y < 0)
00117 return -1;
00118 line = (int) (y / lineHeight) + EXTRADATACONST(node).super.scrollY.viewPos;
00119
00120
00121 if (line >= EXTRADATACONST(node).super.scrollY.fullSize)
00122 return -1;
00123
00124 return line;
00125 }
00126
00127 static void UI_TextNodeMouseMove (uiNode_t *node, int x, int y)
00128 {
00129 EXTRADATA(node).lineUnderMouse = UI_TextNodeGetLine(node, x, y);
00130 }
00131
00132 #define UI_TEXTNODE_BUFFERSIZE 32768
00133
00142 static void UI_TextNodeDrawText (uiNode_t* node, const char *text, const linkedList_t* list, qboolean noDraw)
00143 {
00145 char textCopy[UI_TEXTNODE_BUFFERSIZE];
00146 char newFont[MAX_VAR];
00147 const char* oldFont = NULL;
00148 vec4_t colorHover;
00149 vec4_t colorSelectedHover;
00150 char *cur, *tab, *end;
00151 int fullSizeY;
00152 int x1;
00153 const char *font = UI_GetFontFromNode(node);
00154 vec2_t pos;
00155 int x, y, width;
00156 int viewSizeY;
00157
00158 UI_GetNodeAbsPos(node, pos);
00159
00160 if (UI_AbstractScrollableNodeIsSizeChange(node)) {
00161 int lineHeight = EXTRADATA(node).lineHeight;
00162 if (lineHeight == 0) {
00163 const char *font = UI_GetFontFromNode(node);
00164 lineHeight = UI_FontGetHeight(font);
00165 }
00166 viewSizeY = node->size[1] / lineHeight;
00167 } else {
00168 viewSizeY = EXTRADATA(node).super.scrollY.viewSize;
00169 }
00170
00171
00172 x = pos[0] + node->padding;
00173 y = pos[1] + node->padding;
00174 width = node->size[0] - node->padding - node->padding;
00175
00176 if (text) {
00177 Q_strncpyz(textCopy, text, sizeof(textCopy));
00178 } else if (list) {
00179 Q_strncpyz(textCopy, (const char*)list->data, sizeof(textCopy));
00180 } else
00181 return;
00183 cur = textCopy;
00184
00185
00186 VectorScale(node->color, 0.8, colorHover);
00187 colorHover[3] = node->color[3];
00188
00189
00190 VectorScale(node->selectedColor, 0.8, colorSelectedHover);
00191 colorSelectedHover[3] = node->selectedColor[3];
00192
00193
00194 switch (node->textalign % 3) {
00195 case 0:
00196 break;
00197 case 1:
00198 x += width / 2;
00199 break;
00200 case 2:
00201 x += width;
00202 break;
00203 }
00204
00205 R_Color(node->color);
00206
00207 fullSizeY = 0;
00208 do {
00209 qboolean haveTab;
00210
00211 x1 = x;
00212 if (oldFont) {
00213 font = oldFont;
00214 oldFont = NULL;
00215 }
00216
00217
00218 if (cur[0] == '^') {
00219 switch (toupper(cur[1])) {
00220 case 'B':
00221 Com_sprintf(newFont, sizeof(newFont), "%s_bold", font);
00222 oldFont = font;
00223 font = newFont;
00224 cur += 2;
00225 break;
00226 }
00227 }
00228
00229
00230 end = strchr(cur, '\n');
00231 if (end)
00232
00233
00234 *end++ = '\0';
00235
00236
00237 if (fullSizeY == EXTRADATA(node).textLineSelected && EXTRADATA(node).textLineSelected >= 0) {
00238
00239 R_Color(node->selectedColor);
00240 } else {
00241 R_Color(node->color);
00242 }
00243
00244 if (node->state && EXTRADATA(node).mousefx && fullSizeY == EXTRADATA(node).lineUnderMouse) {
00245
00247 if (fullSizeY == EXTRADATA(node).textLineSelected && EXTRADATA(node).textLineSelected >= 0) {
00248 R_Color(colorSelectedHover);
00249 } else {
00250 R_Color(colorHover);
00251 }
00252 }
00253
00254
00255 haveTab = strchr(cur, '\t') != NULL;
00256 if (haveTab) {
00257 while (cur && *cur) {
00258 int tabwidth;
00259
00260 tab = strchr(cur, '\t');
00261
00262
00263
00264 if (!EXTRADATA(node).tabWidth)
00265 tabwidth = width / 3;
00266 else
00267 tabwidth = EXTRADATA(node).tabWidth;
00268
00269 if (tab) {
00270 int numtabs = strspn(tab, "\t");
00271 tabwidth *= numtabs;
00272 while (*tab == '\t')
00273 *tab++ = '\0';
00274 } else {
00275
00276 tabwidth = width - (x1 - x);
00277 if (tabwidth < 0)
00278 tabwidth = 0;
00279 }
00280
00281
00282 if ((x1 - x) + tabwidth > width)
00283 tabwidth = width - (x1 - x);
00284
00285
00286 if (tabwidth < 0)
00287 tabwidth = 0;
00288
00289 if (tabwidth != 0)
00290 UI_DrawString(font, node->textalign, x1, y, x1, tabwidth - 1, EXTRADATA(node).lineHeight, cur, viewSizeY, EXTRADATA(node).super.scrollY.viewPos, &fullSizeY, qfalse, LONGLINES_PRETTYCHOP);
00291
00292
00293 x1 += tabwidth;
00294 cur = tab;
00295 }
00296 fullSizeY++;
00297 }
00298
00299
00300
00301
00302 if (cur && (cur[0] || end || list)) {
00303
00304 if (!cur) {
00305 fullSizeY++;
00306 } else {
00307 if (noDraw) {
00308 int lines = 0;
00309 R_FontTextSize (font, cur, width, EXTRADATA(node).longlines, NULL, NULL, &lines, NULL);
00310 fullSizeY += lines;
00311 } else
00312 UI_DrawString(font, node->textalign, x1, y, x, width, EXTRADATA(node).lineHeight, cur, viewSizeY, EXTRADATA(node).super.scrollY.viewPos, &fullSizeY, qtrue, EXTRADATA(node).longlines);
00313 }
00314 }
00315
00316 if (EXTRADATA(node).mousefx)
00317 R_Color(node->color);
00318
00319
00320 cur = end;
00321 if (!cur && list) {
00322 list = list->next;
00323 if (list) {
00324 Q_strncpyz(textCopy, (const char*)list->data, sizeof(textCopy));
00325 cur = textCopy;
00326 }
00327 }
00328 } while (cur);
00329
00330
00331 UI_AbstractScrollableNodeSetY(node, -1, viewSizeY, fullSizeY);
00332
00333 R_Color(NULL);
00334 }
00335
00336 static void UI_TextUpdateCache (uiNode_t *node)
00337 {
00338 const uiSharedData_t *shared;
00339
00340 if (EXTRADATA(node).dataID == TEXT_NULL && node->text != NULL)
00341 return;
00342
00343 shared = &ui_global.sharedData[EXTRADATA(node).dataID];
00344
00345 switch (shared->type) {
00346 case UI_SHARED_TEXT:
00347 {
00348 const char* t = shared->data.text;
00349 if (t[0] == '_')
00350 t = _(++t);
00351 UI_TextNodeDrawText(node, t, NULL, qtrue);
00352 }
00353 break;
00354 case UI_SHARED_LINKEDLISTTEXT:
00355 UI_TextNodeDrawText(node, NULL, shared->data.linkedListText, qtrue);
00356 break;
00357 default:
00358 break;
00359 }
00360
00361 EXTRADATA(node).versionId = shared->versionId;
00362 }
00363
00367 static void UI_TextNodeDraw (uiNode_t *node)
00368 {
00369 const uiSharedData_t *shared;
00370
00371 if (EXTRADATA(node).dataID == TEXT_NULL && node->text != NULL) {
00372 const char* t = UI_GetReferenceString(node, node->text);
00373 if (t[0] == '_')
00374 t = _(++t);
00375 UI_TextNodeDrawText(node, t, NULL, qfalse);
00376 return;
00377 }
00378
00379 shared = &ui_global.sharedData[EXTRADATA(node).dataID];
00380
00381 switch (shared->type) {
00382 case UI_SHARED_TEXT:
00383 {
00384 const char* t = shared->data.text;
00385 if (t[0] == '_')
00386 t = _(++t);
00387 UI_TextNodeDrawText(node, t, NULL, qfalse);
00388 break;
00389 }
00390 case UI_SHARED_LINKEDLISTTEXT:
00391 UI_TextNodeDrawText(node, NULL, shared->data.linkedListText, qfalse);
00392 break;
00393 default:
00394 break;
00395 }
00396
00397 EXTRADATA(node).versionId = shared->versionId;
00398 }
00399
00404 static void UI_TextNodeClick (uiNode_t * node, int x, int y)
00405 {
00406 int line = UI_TextNodeGetLine(node, x, y);
00407
00408 if (line < 0 || line >= EXTRADATA(node).super.scrollY.fullSize)
00409 return;
00410
00411 UI_TextNodeSelectLine(node, line);
00412
00413 if (node->onClick)
00414 UI_ExecuteEventActions(node, node->onClick);
00415 }
00416
00421 static void UI_TextNodeRightClick (uiNode_t * node, int x, int y)
00422 {
00423 int line = UI_TextNodeGetLine(node, x, y);
00424
00425 if (line < 0 || line >= EXTRADATA(node).super.scrollY.fullSize)
00426 return;
00427
00428 UI_TextNodeSelectLine(node, line);
00429
00430 if (node->onRightClick)
00431 UI_ExecuteEventActions(node, node->onRightClick);
00432 }
00433
00436 static void UI_TextNodeMouseWheel (uiNode_t *node, qboolean down, int x, int y)
00437 {
00438 UI_AbstractScrollableNodeScrollY(node, (down ? 1 : -1));
00439 if (node->onWheelUp && !down)
00440 UI_ExecuteEventActions(node, node->onWheelUp);
00441 if (node->onWheelDown && down)
00442 UI_ExecuteEventActions(node, node->onWheelDown);
00443 if (node->onWheel)
00444 UI_ExecuteEventActions(node, node->onWheel);
00445 }
00446
00447 static void UI_TextNodeLoading (uiNode_t *node)
00448 {
00449 EXTRADATA(node).textLineSelected = -1;
00450 Vector4Set(node->selectedColor, 1.0, 1.0, 1.0, 1.0);
00451 Vector4Set(node->color, 1.0, 1.0, 1.0, 1.0);
00452 }
00453
00454 static void UI_TextNodeLoaded (uiNode_t *node)
00455 {
00456 int lineheight = EXTRADATA(node).lineHeight;
00457
00458
00459 if (lineheight == 0) {
00460
00461 const char *font = UI_GetFontFromNode(node);
00462 lineheight = UI_FontGetHeight(font);
00463 }
00464
00465
00466 if (EXTRADATA(node).super.scrollY.viewSize == 0) {
00467 if (node->size[1] != 0 && lineheight != 0) {
00468 EXTRADATA(node).super.scrollY.viewSize = node->size[1] / lineheight;
00469 } else {
00470 EXTRADATA(node).super.scrollY.viewSize = 1;
00471 Com_Printf("UI_TextNodeLoaded: node '%s' has no rows value\n", UI_GetPath(node));
00472 }
00473 }
00474
00475
00476 if (node->size[1] == 0) {
00477 node->size[1] = EXTRADATA(node).super.scrollY.viewSize * lineheight;
00478 }
00479
00480
00481 if (EXTRADATA(node).dataID >= UI_MAX_DATAID)
00482 Com_Error(ERR_DROP, "Error in node %s - max shared data id num exceeded (num: %i, max: %i)", UI_GetPath(node), EXTRADATA(node).dataID, UI_MAX_DATAID);
00483
00484 #ifdef DEBUG
00485 if (EXTRADATA(node).super.scrollY.viewSize != (int)(node->size[1] / lineheight)) {
00486 Com_Printf("UI_TextNodeLoaded: rows value (%i) of node '%s' differs from size (%.0f) and format (%i) values\n",
00487 EXTRADATA(node).super.scrollY.viewSize, UI_GetPath(node), node->size[1], lineheight);
00488 }
00489 #endif
00490
00491 if (node->text == NULL && EXTRADATA(node).dataID == TEXT_NULL)
00492 Com_Printf("UI_TextNodeLoaded: 'textid' property of node '%s' is not set\n", UI_GetPath(node));
00493 }
00494
00495 static const value_t properties[] = {
00496
00497 {"lineselected", V_INT, UI_EXTRADATA_OFFSETOF(textExtraData_t, textLineSelected), MEMBER_SIZEOF(textExtraData_t, textLineSelected)},
00498
00499
00500
00501
00502
00503
00504
00505 {"dataid", V_UI_DATAID, UI_EXTRADATA_OFFSETOF(textExtraData_t, dataID), MEMBER_SIZEOF(textExtraData_t, dataID)},
00506
00507 {"lineheight", V_INT, UI_EXTRADATA_OFFSETOF(textExtraData_t, lineHeight), MEMBER_SIZEOF(textExtraData_t, lineHeight)},
00508
00509 {"tabwidth", V_INT, UI_EXTRADATA_OFFSETOF(textExtraData_t, tabWidth), MEMBER_SIZEOF(textExtraData_t, tabWidth)},
00510
00511
00512
00513 {"longlines", V_INT, UI_EXTRADATA_OFFSETOF(textExtraData_t, longlines), MEMBER_SIZEOF(textExtraData_t, longlines)},
00514
00515
00516
00517
00518
00519 {"rows", V_INT, UI_EXTRADATA_OFFSETOF(textExtraData_t, super.scrollY.viewSize), MEMBER_SIZEOF(textExtraData_t, super.scrollY.viewSize)},
00520
00521
00522
00523
00524 {"lines", V_INT, UI_EXTRADATA_OFFSETOF(textExtraData_t, super.scrollY.fullSize), MEMBER_SIZEOF(textExtraData_t, super.scrollY.fullSize)},
00525
00529 {"mousefx", V_BOOL, UI_EXTRADATA_OFFSETOF(textExtraData_t, mousefx), MEMBER_SIZEOF(textExtraData_t, mousefx)},
00530 {NULL, V_NULL, 0, 0}
00531 };
00532
00533 void UI_RegisterTextNode (uiBehaviour_t *behaviour)
00534 {
00535 behaviour->name = "text";
00536 behaviour->extends = "abstractscrollable";
00537 behaviour->draw = UI_TextNodeDraw;
00538 behaviour->leftClick = UI_TextNodeClick;
00539 behaviour->rightClick = UI_TextNodeRightClick;
00540 behaviour->mouseWheel = UI_TextNodeMouseWheel;
00541 behaviour->mouseMove = UI_TextNodeMouseMove;
00542 behaviour->loading = UI_TextNodeLoading;
00543 behaviour->loaded = UI_TextNodeLoaded;
00544 behaviour->properties = properties;
00545 behaviour->extraDataSize = sizeof(EXTRADATA_TYPE);
00546
00547 Com_RegisterConstInt("LONGLINES_WRAP", LONGLINES_WRAP);
00548 Com_RegisterConstInt("LONGLINES_CHOP", LONGLINES_CHOP);
00549 Com_RegisterConstInt("LONGLINES_PRETTYCHOP", LONGLINES_PRETTYCHOP);
00550 }