File: | client/ui/node/ui_node_textentry.cpp |
Location: | line 175, column 3 |
Description: | Value stored to 'length' is never read |
1 | /** |
2 | * @file |
3 | * @brief This node allow to edit a cvar text with the keyboard. When we |
4 | * click on the node, we active the edition, we can validate it with the ''RETURN'' key, |
5 | * or abort it with ''ESCAPE'' key. A validation fire a scriptable callback event. |
6 | * We can custom the mouse behaviour when we click outside the node in edition mode. |
7 | * It can validate or abort the edition. |
8 | * @todo allow to edit text without any cvar |
9 | * @todo add a custom max size |
10 | */ |
11 | |
12 | /* |
13 | Copyright (C) 2002-2011 UFO: Alien Invasion. |
14 | |
15 | This program is free software; you can redistribute it and/or |
16 | modify it under the terms of the GNU General Public License |
17 | as published by the Free Software Foundation; either version 2 |
18 | of the License, or (at your option) any later version. |
19 | |
20 | This program is distributed in the hope that it will be useful, |
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
23 | |
24 | See the GNU General Public License for more details. |
25 | |
26 | You should have received a copy of the GNU General Public License |
27 | along with this program; if not, write to the Free Software |
28 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
29 | |
30 | */ |
31 | |
32 | #include "../ui_main.h" |
33 | #include "../ui_nodes.h" |
34 | #include "../ui_font.h" |
35 | #include "../ui_parse.h" |
36 | #include "../ui_behaviour.h" |
37 | #include "../ui_input.h" |
38 | #include "../ui_actions.h" |
39 | #include "../ui_render.h" |
40 | #include "../ui_sprite.h" |
41 | #include "ui_node_textentry.h" |
42 | #include "ui_node_abstractnode.h" |
43 | #include "ui_node_panel.h" |
44 | |
45 | #include "../../client.h" |
46 | #include "../../../shared/utf8.h" |
47 | |
48 | #define EXTRADATA_TYPEtextEntryExtraData_t textEntryExtraData_t |
49 | #define EXTRADATA(node)(*((textEntryExtraData_t*)((char*)node + sizeof(uiNode_t)))) UI_EXTRADATA(node, EXTRADATA_TYPE)(*((textEntryExtraData_t*)((char*)node + sizeof(uiNode_t)))) |
50 | |
51 | static const char CURSOR = '|'; /**< Use as the cursor when we edit the text */ |
52 | static const char HIDECHAR = '*'; /**< use as a mask for password */ |
53 | |
54 | /* limit the input for cvar editing (base name, save slots and so on) */ |
55 | #define MAX_CVAR_EDITING_LENGTH256 256 /* MAXCMDLINE */ |
56 | |
57 | /* global data */ |
58 | static char cvarValueBackup[MAX_CVAR_EDITING_LENGTH256]; |
59 | static cvar_t *editedCvar = NULL__null; |
60 | static bool isAborted = false; |
61 | |
62 | /** |
63 | * @brief callback from the keyboard |
64 | */ |
65 | static void UI_TextEntryNodeValidateEdition (uiNode_t *node) |
66 | { |
67 | /* invalidate cache */ |
68 | editedCvar = NULL__null; |
69 | cvarValueBackup[0] = '\0'; |
70 | |
71 | /* fire change event */ |
72 | if (node->onChange) { |
73 | UI_ExecuteEventActions(node, node->onChange); |
74 | } |
75 | } |
76 | |
77 | /** |
78 | * @brief callback from the keyboard |
79 | */ |
80 | static void UI_TextEntryNodeAbortEdition (uiNode_t *node) |
81 | { |
82 | assert(editedCvar)(__builtin_expect(!(editedCvar), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_textentry.cpp" , 82, "editedCvar") : (void)0); |
83 | |
84 | /* set the old cvar value */ |
85 | Cvar_ForceSet(editedCvar->name, cvarValueBackup); |
86 | |
87 | /* invalidate cache */ |
88 | editedCvar = NULL__null; |
89 | cvarValueBackup[0] = '\0'; |
90 | |
91 | /* fire abort event */ |
92 | if (EXTRADATA(node)(*((textEntryExtraData_t*)((char*)node + sizeof(uiNode_t)))).onAbort) { |
93 | UI_ExecuteEventActions(node, EXTRADATA(node)(*((textEntryExtraData_t*)((char*)node + sizeof(uiNode_t)))).onAbort); |
94 | } |
95 | } |
96 | |
97 | /** |
98 | * @brief force edition of a textentry node |
99 | * @note the textentry must be on the active window |
100 | */ |
101 | static void UI_TextEntryNodeFocus (uiNode_t *node, const uiCallContext_t *context) |
102 | { |
103 | /* remove the focus to show changes */ |
104 | if (!UI_HasFocus(node)) { |
105 | UI_RequestFocus(node); |
106 | } |
107 | } |
108 | |
109 | /** |
110 | * @brief Called when the user click with the right mouse button |
111 | */ |
112 | void uiTextEntryNode::onLeftClick (uiNode_t *node, int x, int y) |
113 | { |
114 | if (node->disabled) |
115 | return; |
116 | |
117 | /* no cvar */ |
118 | if (!node->text) |
119 | return; |
120 | if (!Q_strstart(node->text, "*cvar:")) |
121 | return; |
122 | |
123 | if (!UI_HasFocus(node)) { |
124 | if (node->onClick) { |
125 | UI_ExecuteEventActions(node, node->onClick); |
126 | } |
127 | UI_RequestFocus(node); |
128 | } |
129 | } |
130 | |
131 | /** |
132 | * @brief Called when the node got the focus |
133 | */ |
134 | void uiTextEntryNode::onFocusGained (uiNode_t *node) |
135 | { |
136 | assert(editedCvar == NULL)(__builtin_expect(!(editedCvar == __null), 0) ? __assert_rtn( __func__, "src/client/ui/node/ui_node_textentry.cpp", 136, "editedCvar == NULL" ) : (void)0); |
137 | /* skip '*cvar ' */ |
138 | editedCvar = Cvar_Get(&((char*)node->text)[6]); |
139 | assert(editedCvar)(__builtin_expect(!(editedCvar), 0) ? __assert_rtn(__func__, "src/client/ui/node/ui_node_textentry.cpp" , 139, "editedCvar") : (void)0); |
140 | Q_strncpyz(cvarValueBackup, editedCvar->string, sizeof(cvarValueBackup))Q_strncpyzDebug( cvarValueBackup, editedCvar->string, sizeof (cvarValueBackup), "src/client/ui/node/ui_node_textentry.cpp" , 140 ); |
141 | isAborted = false; |
142 | } |
143 | |
144 | /** |
145 | * @brief Called when the node lost the focus |
146 | */ |
147 | void uiTextEntryNode::onFocusLost (uiNode_t *node) |
148 | { |
149 | /* already aborted/changed with the keyboard */ |
150 | if (editedCvar == NULL__null) |
151 | return; |
152 | |
153 | /* release the keyboard */ |
154 | if (isAborted || EXTRADATA(node)(*((textEntryExtraData_t*)((char*)node + sizeof(uiNode_t)))).clickOutAbort) { |
155 | UI_TextEntryNodeAbortEdition(node); |
156 | } else { |
157 | UI_TextEntryNodeValidateEdition(node); |
158 | } |
159 | } |
160 | |
161 | /** |
162 | * @brief edit the current cvar with a char |
163 | */ |
164 | static void UI_TextEntryNodeEdit (uiNode_t *node, unsigned int key) |
165 | { |
166 | char buffer[MAX_CVAR_EDITING_LENGTH256]; |
167 | int length; |
168 | |
169 | /* copy the cvar */ |
170 | Q_strncpyz(buffer, editedCvar->string, sizeof(buffer))Q_strncpyzDebug( buffer, editedCvar->string, sizeof(buffer ), "src/client/ui/node/ui_node_textentry.cpp", 170 ); |
171 | length = strlen(buffer); |
172 | |
173 | /* compute result */ |
174 | if (key == K_BACKSPACE) { |
175 | length = UTF8_delete_char(buffer, length - 1); |
Value stored to 'length' is never read | |
176 | } else { |
177 | int charLength = UTF8_encoded_len(key); |
178 | /* is buffer full? */ |
179 | if (length + charLength >= sizeof(buffer)) |
180 | return; |
181 | |
182 | length += UTF8_insert_char(buffer, sizeof(buffer), length, key); |
183 | } |
184 | |
185 | /* update the cvar */ |
186 | Cvar_ForceSet(editedCvar->name, buffer); |
187 | } |
188 | |
189 | /** |
190 | * @brief Called when we press a key when the node got the focus |
191 | * @return True, if we use the event |
192 | */ |
193 | bool uiTextEntryNode::onKeyPressed (uiNode_t *node, unsigned int key, unsigned short unicode) |
194 | { |
195 | switch (key) { |
196 | /* remove the last char */ |
197 | case K_BACKSPACE: |
198 | UI_TextEntryNodeEdit(node, K_BACKSPACE); |
199 | return true; |
200 | /* cancel the edition */ |
201 | case K_ESCAPE: |
202 | isAborted = true; |
203 | UI_RemoveFocus(); |
204 | return true; |
205 | /* validate the edition */ |
206 | case K_ENTER: |
207 | case K_KP_ENTER: |
208 | UI_TextEntryNodeValidateEdition(node); |
209 | UI_RemoveFocus(); |
210 | return true; |
211 | } |
212 | |
213 | /* non printable */ |
214 | if (unicode < 32 || (unicode >= 127 && unicode < 192)) |
215 | return false; |
216 | |
217 | /* add a char */ |
218 | UI_TextEntryNodeEdit(node, unicode); |
219 | return true; |
220 | } |
221 | |
222 | void uiTextEntryNode::draw (uiNode_t *node) |
223 | { |
224 | const float *textColor; |
225 | vec2_t pos; |
226 | static vec4_t disabledColor = {0.5, 0.5, 0.5, 1.0}; |
227 | const char *font = UI_GetFontFromNode(node); |
228 | uiSpriteStatus_t iconStatus = SPRITE_STATUS_NORMAL; |
229 | |
230 | if (node->disabled) { |
231 | /** @todo need custom color when node is disabled */ |
232 | textColor = disabledColor; |
233 | iconStatus = SPRITE_STATUS_DISABLED; |
234 | } else if (node->state) { |
235 | textColor = node->color; |
236 | iconStatus = SPRITE_STATUS_HOVER; |
237 | } else { |
238 | textColor = node->color; |
239 | } |
240 | if (UI_HasFocus(node)) { |
241 | textColor = node->selectedColor; |
242 | } |
243 | |
244 | UI_GetNodeAbsPos(node, pos); |
245 | |
246 | if (EXTRADATA(node)(*((textEntryExtraData_t*)((char*)node + sizeof(uiNode_t)))).background) { |
247 | UI_DrawSpriteInBox(false, EXTRADATA(node)(*((textEntryExtraData_t*)((char*)node + sizeof(uiNode_t)))).background, iconStatus, pos[0], pos[1], node->box.size[0], node->box.size[1]); |
248 | } |
249 | |
250 | if (char const* const text = UI_GetReferenceString(node, node->text)) { |
251 | char buf[MAX_VAR64]; |
252 | char* c = buf; |
253 | if (EXTRADATA(node)(*((textEntryExtraData_t*)((char*)node + sizeof(uiNode_t)))).isPassword) { |
254 | size_t const size = UTF8_strlen(text); |
255 | memset(buf, HIDECHAR, size); |
256 | c += size; |
257 | } else { |
258 | size_t const size = strlen(text); |
259 | memcpy(buf, text, size); |
260 | c += size; |
261 | } |
262 | |
263 | if (UI_HasFocus(node) && CL_Milliseconds() % 1000 < 500) { |
264 | *c++ = CURSOR; |
265 | } |
266 | |
267 | *c = '\0'; |
268 | |
269 | if (*buf != '\0') { |
270 | R_Color(textColor); |
271 | UI_DrawStringInBox(font, (align_t)node->contentAlign, |
272 | pos[0] + node->padding, pos[1] + node->padding, |
273 | node->box.size[0] - node->padding - node->padding, node->box.size[1] - node->padding - node->padding, |
274 | buf); |
275 | R_Color(NULL__null); |
276 | } |
277 | } |
278 | |
279 | } |
280 | |
281 | /** |
282 | * @brief Call before the script initialization of the node |
283 | */ |
284 | void uiTextEntryNode::onLoading (uiNode_t *node) |
285 | { |
286 | node->padding = 8; |
287 | node->contentAlign = ALIGN_CL; |
288 | Vector4Set(node->color, 1, 1, 1, 1)((node->color)[0]=(1), (node->color)[1]=(1), (node-> color)[2]=(1), (node->color)[3]=(1)); |
289 | Vector4Set(node->selectedColor, 1, 1, 1, 1)((node->selectedColor)[0]=(1), (node->selectedColor)[1] =(1), (node->selectedColor)[2]=(1), (node->selectedColor )[3]=(1)); |
290 | } |
291 | |
292 | void UI_RegisterTextEntryNode (uiBehaviour_t *behaviour) |
293 | { |
294 | behaviour->name = "textentry"; |
295 | behaviour->manager = new uiTextEntryNode(); |
296 | behaviour->extraDataSize = sizeof(EXTRADATA_TYPEtextEntryExtraData_t); |
297 | |
298 | /* Call back event called when we click on the node. If the click select the node, |
299 | * it called before we start the cvar edition. |
300 | */ |
301 | UI_RegisterOveridedNodeProperty(behaviour, "onClick");; |
302 | |
303 | /* Call back event (like click...) fired when the text is changed, after |
304 | * validation. An abort of the edition dont fire this event. |
305 | */ |
306 | UI_RegisterOveridedNodeProperty(behaviour, "onChange");; |
307 | |
308 | /* Custom the draw behaviour by hiding each character of the text with a star (''*''). */ |
309 | UI_RegisterExtradataNodeProperty(behaviour, "isPassword", V_BOOL, textEntryExtraData_t, isPassword)UI_RegisterNodePropertyPosSize_(behaviour, "isPassword", V_BOOL , ((size_t) &((textEntryExtraData_t *)(((textEntryExtraData_t *)((char*)0 + sizeof(uiNode_t)))))->isPassword), sizeof((( textEntryExtraData_t *)0)->isPassword)); |
310 | /* ustom the mouse event behaviour. When we are editing the text, if we click out of the node, the edition is aborted. Changes on |
311 | * the text are canceled, and no change event are fired. |
312 | */ |
313 | UI_RegisterExtradataNodeProperty(behaviour, "clickOutAbort", V_BOOL, textEntryExtraData_t, clickOutAbort)UI_RegisterNodePropertyPosSize_(behaviour, "clickOutAbort", V_BOOL , ((size_t) &((textEntryExtraData_t *)(((textEntryExtraData_t *)((char*)0 + sizeof(uiNode_t)))))->clickOutAbort), sizeof (((textEntryExtraData_t *)0)->clickOutAbort)); |
314 | /* Call it when we abort the edition */ |
315 | UI_RegisterExtradataNodeProperty(behaviour, "onAbort", V_UI_ACTION, textEntryExtraData_t, onAbort)UI_RegisterNodePropertyPosSize_(behaviour, "onAbort", (0x8000 + 0), ((size_t) &((textEntryExtraData_t *)(((textEntryExtraData_t *)((char*)0 + sizeof(uiNode_t)))))->onAbort), sizeof(((textEntryExtraData_t *)0)->onAbort)); |
316 | /* Call it to force node edition */ |
317 | UI_RegisterNodeMethod(behaviour, "edit", UI_TextEntryNodeFocus); |
318 | /* Sprite used to display the background */ |
319 | UI_RegisterExtradataNodeProperty(behaviour, "background", V_UI_SPRITEREF, EXTRADATA_TYPE, background)UI_RegisterNodePropertyPosSize_(behaviour, "background", (0x8000 + 3), ((size_t) &((textEntryExtraData_t *)(((textEntryExtraData_t *)((char*)0 + sizeof(uiNode_t)))))->background), sizeof((( textEntryExtraData_t *)0)->background)); |
320 | } |