script, ui: added "sv_min_startmap_health" cvar
[k8vavoom.git] / source / iline.cpp
blobd7ac1d640ea466cae74969f048e0a73571435767
1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
12 //**
13 //** This program is free software: you can redistribute it and/or modify
14 //** it under the terms of the GNU General Public License as published by
15 //** the Free Software Foundation, version 3 of the License ONLY.
16 //**
17 //** This program is distributed in the hope that it will be useful,
18 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 //** GNU General Public License for more details.
21 //**
22 //** You should have received a copy of the GNU General Public License
23 //** along with this program. If not, see <http://www.gnu.org/licenses/>.
24 //**
25 //**************************************************************************
26 #include "gamedefs.h"
27 #include "iline.h"
28 #include "input.h"
29 #include "text.h"
31 #define MAX_ILINE_LENGTH (4096)
34 //==========================================================================
36 // TILine::~TILine
38 //==========================================================================
39 TILine::~TILine () {
40 Z_Free(data);
41 data = nullptr;
42 len = maxlen = 0;
43 Z_Free(temp);
44 temp = nullptr;
45 tempsize = 0;
49 //==========================================================================
51 // TILine::setup
53 //==========================================================================
54 void TILine::setup () {
55 if (maxlen < 0) maxlen = 80;
56 else if (maxlen == 0 || maxlen > MAX_ILINE_LENGTH) maxlen = MAX_ILINE_LENGTH;
57 vassert(maxlen > 0);
58 data = (char *)Z_Realloc(data, maxlen+16);
59 vassert(data);
60 data[0] = 0;
61 len = 0;
62 curpos = 0;
63 visfirst = 0;
67 //==========================================================================
69 // TILine::Init
71 //==========================================================================
72 void TILine::Init () {
73 len = 0;
74 curpos = 0;
75 visfirst = 0;
76 data[0] = 0;
80 //==========================================================================
82 // TILine::SetVisChars
84 //==========================================================================
85 void TILine::SetVisChars (int vc) {
86 if (vc < 8) vc = 8;
87 if (vc == vischars) return;
88 vischars = vc;
89 ensureCursorVisible();
93 //==========================================================================
95 // TILine::ensureCursorVisible
97 //==========================================================================
98 void TILine::ensureCursorVisible () {
99 curpos = clampval(curpos, 0, len);
100 if (curpos == len) {
101 // special case
102 visfirst = max2(0, len-(vischars-1));
103 return;
105 if (curpos < visfirst) {
106 // move left
107 visfirst = max2(0, curpos-4);
108 } else if (curpos-visfirst >= vischars-1) {
109 visfirst = curpos-(vischars-4);
110 if (visfirst+vischars > len) visfirst = len-(vischars-1);
111 if (visfirst < 0) visfirst = 0;
116 //==========================================================================
118 // TILine::AddChar
120 //==========================================================================
121 void TILine::AddChar (char ch) {
122 if (ch == '\t') ch = ' '; // why not?
123 if ((vuint8)ch < ' ' || (vuint8)ch >= 127) return;
124 if (len >= maxlen) return;
125 if (curpos >= len) {
126 data[len++] = ch;
127 data[len] = 0;
128 curpos = len;
129 } else {
130 if (curpos < 0) curpos = 0;
131 for (int f = len; f > curpos; --f) data[f] = data[f-1];
132 ++len;
133 data[len] = 0; // just in case
134 data[curpos] = ch;
135 ++curpos;
137 ensureCursorVisible();
141 //==========================================================================
143 // TILine::AddString
145 //==========================================================================
146 void TILine::AddString (const char *s) {
147 if (!s || !s[0]) return;
148 while (*s) AddChar(*s++);
152 //==========================================================================
154 // TILine::AddString
156 //==========================================================================
157 void TILine::AddString (VStr s) {
158 AddString(*s);
162 //==========================================================================
164 // TILine::DelChar
166 //==========================================================================
167 void TILine::DelChar () {
168 if (len == 0 || curpos < 1) return;
169 --curpos;
170 for (int f = curpos; f < len; ++f) data[f] = data[f+1];
171 data[--len] = 0;
172 ensureCursorVisible();
176 //==========================================================================
178 // TILine::RemoveChar
180 // this removes char at the current cursor position
181 // and doesn't move cursor
183 //==========================================================================
184 void TILine::RemoveChar () {
185 if (curpos >= len) return;
186 for (int f = curpos; f < len; ++f) data[f] = data[f+1];
187 data[--len] = 0;
188 ensureCursorVisible();
192 //==========================================================================
194 // TILine::DelWord
196 //==========================================================================
197 void TILine::DelWord () {
198 ensureCursorVisible();
199 if (curpos == 0) return;
200 if ((vuint8)data[curpos-1] <= ' ') {
201 // delete trailing spaces
202 while (curpos > 0 && (vuint8)data[curpos-1] <= ' ') DelChar();
203 } else {
204 // delete text
205 while (curpos > 0 && (vuint8)data[curpos-1] > ' ') DelChar();
210 //==========================================================================
212 // TILine::WordLeft
214 //==========================================================================
215 void TILine::WordLeft () {
216 // word left
217 if ((vuint8)data[curpos-1] <= ' ') {
218 // spaces
219 while (curpos > 0 && (vuint8)data[curpos-1] <= ' ') --curpos;
220 } else {
221 // word
222 if (data[curpos-1] == ';') { --curpos; return; }
223 while (curpos > 0) {
224 vuint8 ch = (vuint8)data[curpos-1];
225 if (ch <= ' ' || ch == ';') break;
226 --curpos;
232 //==========================================================================
234 // TILine::WordRight
236 //==========================================================================
237 void TILine::WordRight () {
238 // word right
239 if ((vuint8)data[curpos] <= ' ') {
240 // spaces
241 while (curpos < len && (vuint8)data[curpos] <= ' ') ++curpos;
242 } else {
243 // word
244 if (data[curpos] == ';') { ++curpos; return; }
245 while (curpos < len) {
246 vuint8 ch = (vuint8)data[curpos];
247 if (ch <= ' ' || ch == ';') break;
248 ++curpos;
254 //==========================================================================
256 // TILine::Key
258 // Wrapper function for handling general keyed input.
259 // Returns true if it ate the key
261 //==========================================================================
262 bool TILine::Key (const event_t &ev) {
263 if (ev.type != ev_keydown) return false;
265 switch (ev.keycode) {
266 // clipboard
267 case K_INSERT:
269 vuint32 flg = ev.modflags&(bCtrl|bAlt|bShift|bHyper);
270 // ctrl+insert: copy to clipboard
271 if (flg == bCtrl) {
272 GInput->SetClipboardText(VStr(data));
273 return true;
275 // shift+insert: insert from clipboard
276 if (flg == bShift) {
277 VStr ntx = GInput->GetClipboardText();
278 if (ntx.length()) {
279 bool prevWasBlank = false;
280 for (int f = 0; f < ntx.length(); ++f) {
281 char ch = ntx[f];
282 if (ch < 0) continue;
283 if (ch == '\n') {
284 if (data[len] != ' ' && data[len] != '\t') AddChar(' ');
285 AddChar(';');
286 prevWasBlank = false;
287 continue;
289 if (ch <= 32) {
290 if (!prevWasBlank) AddChar(' ');
291 prevWasBlank = true;
292 continue;
294 AddChar(ch);
295 prevWasBlank = false;
300 return true;
302 case K_DELETE:
304 vuint32 flg = ev.modflags&(bCtrl|bAlt|bShift|bHyper);
305 // ctrl+del: delete to the end of the line
306 if (flg == bCtrl) {
307 if (curpos < len) {
308 data[curpos] = 0;
309 len = curpos;
311 ensureCursorVisible();
312 return true;
314 // del: delete char
315 if (flg == 0) {
316 RemoveChar();
317 return true;
320 break;
322 // cursor movement
323 case K_LEFTARROW:
324 if (curpos > 0) {
325 if (ev.isCtrlDown()) WordLeft(); else --curpos;
326 ensureCursorVisible();
328 return true;
329 case K_RIGHTARROW:
330 if (curpos < len) {
331 if (ev.isCtrlDown()) WordRight(); else ++curpos;
332 ensureCursorVisible();
334 return true;
335 case K_HOME:
336 curpos = 0;
337 ensureCursorVisible();
338 return true;
339 case K_END:
340 curpos = len;
341 ensureCursorVisible();
342 return true;
344 // clear line
345 case K_y:
346 if (ev.isCtrlDown()) {
347 // clear line
348 Init();
349 return true;
351 break;
353 // to the start of the line
354 case K_a:
355 if (ev.isCtrlDown()) {
356 curpos = 0;
357 ensureCursorVisible();
358 return true;
360 break;
362 // to the end of the line
363 case K_e:
364 if (ev.isCtrlDown()) {
365 curpos = len;
366 ensureCursorVisible();
367 return true;
369 break;
371 // delete word
372 case K_w:
373 if (ev.isCtrlDown()) {
374 DelWord();
375 return true;
377 break;
380 if (ev.keycode == K_BACKSPACE) {
381 if (ev.isCtrlDown()) DelWord(); else DelChar();
382 return true;
383 } else if (ev.keycode == K_ENTER || ev.keycode == K_PADENTER) {
384 return true;
385 } else {
386 int ch = GInput->TranslateKey(ev.keycode);
387 if (ch >= ' ' && ch < 128) {
388 AddChar((char)ch);
389 return true; // ate the key
393 return false; // did not eat key
397 //==========================================================================
399 // TILine::DrawAt
401 //==========================================================================
402 void TILine::DrawAt (int x0, int y0, int clrNormal, int clrLR) {
403 if (!data) return; // just in case
404 ensureCursorVisible();
405 T_SetCursorPos(x0, y0); // for empty input lines
406 if (len == 0) { T_DrawCursorAt(x0, y0, cursorChar, CR_DEBUG_GREEN); return; }
407 // ensure that our temporary buffer is ok
408 if (tempsize < vischars+8) {
409 tempsize = vischars+16;
410 temp = (char *)Z_Realloc(temp, tempsize);
412 // draw left "arrow"
413 if (visfirst > 0) { T_DrawText(x0, y0, "<", clrLR); x0 = T_GetCursorX(); }
414 // draw text before cursor
415 int cpos = visfirst;
416 int tpos = 0;
417 while (cpos < curpos) {
418 vassert(tpos < tempsize+4);
419 temp[tpos++] = data[cpos++];
421 if (tpos > 0) {
422 temp[tpos] = 0;
423 T_DrawText(x0, y0, temp, clrNormal);
425 // remember cursor position
426 x0 = T_GetCursorX();
427 y0 = T_GetCursorY();
428 // draw text after cursor
429 tpos = 0;
430 while (cpos < len && cpos-visfirst < vischars) {
431 vassert(tpos < tempsize+4);
432 temp[tpos++] = data[cpos++];
434 if (tpos > 0) {
435 if (cpos < len) --tpos;
436 temp[tpos] = 0;
437 T_DrawText(x0, y0, temp, clrNormal);
438 // draw right "arrow"
439 if (cpos < len) T_DrawText(T_GetCursorX(), y0, ">", clrLR);
441 // draw cursor
442 T_DrawCursorAt(x0, y0, cursorChar, CR_DEBUG_GREEN);