GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tech/texteditors.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 0 213 0.0%
Functions: 0 15 0.0%
Branches: 0 85 0.0%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/tech/texteditors.h"
30 #include "gate/results.h"
31
32 gate_result_t gate_texteditor_create(gate_texteditor_t* te, gate_size_t width, gate_size_t height)
33 {
34 gate_result_t ret = GATE_RESULT_FAILED;
35 gate_strbuilder_t builder;
36
37 do
38 {
39 if (width == 0) width = 80;
40 if (height == 0) height = 24;
41 gate_mem_clear(te, sizeof(gate_texteditor_t));
42 if (NULL == gate_slotlist_create(&te->lines, sizeof(gate_strbuilder_t), &gate_strbuilder_copy_constructor, &gate_strbuilder_destructor))
43 {
44 ret = GATE_RESULT_OUTOFMEMORY;
45 break;
46 }
47
48 gate_strbuilder_create(&builder, 0);
49 gate_slotlist_add(&te->lines, &builder);
50
51 te->window_width = width;
52 te->window_height = height;
53 te->window_start_col = 0;
54 te->window_start_row = 0;
55 te->cur_col = 0;
56 te->cur_row = 0;
57 te->state = 0;
58
59 ret = GATE_RESULT_OK;
60 } while (0);
61
62 if (GATE_FAILED(ret))
63 {
64 gate_texteditor_destroy(te);
65 }
66
67 return ret;
68 }
69
70 gate_result_t gate_texteditor_clear(gate_texteditor_t* te)
71 {
72 gate_strbuilder_t builder;
73 gate_strbuilder_create(&builder, 0);
74 gate_slotlist_clear(&te->lines);
75 te->window_start_col = 0;
76 te->window_start_row = 0;
77 te->cur_col = 0;
78 te->cur_row = 0;
79 gate_slotlist_add(&te->lines, &builder);
80 return gate_texteditor_fix_view(te);
81 }
82
83
84 gate_result_t gate_texteditor_destroy(gate_texteditor_t* te)
85 {
86 gate_slotlist_destroy(&te->lines);
87 gate_mem_clear(te, sizeof(gate_texteditor_t));
88 return GATE_RESULT_OK;
89 }
90
91 static gate_strbuilder_t* gate_texteditor_get_line(gate_texteditor_t* te, gate_size_t line)
92 {
93 static gate_string_t const empty_line = GATE_STRING_INIT_EMPTY;
94 while (te->cur_row >= gate_texteditor_get_line_count(te))
95 {
96 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
97 if (NULL == gate_slotlist_add(&te->lines, &empty_line))
98 {
99 return NULL;
100 }
101 }
102 return (gate_strbuilder_t*)gate_slotlist_at(&te->lines, line);
103 }
104
105
106 gate_result_t gate_texteditor_fix_view(gate_texteditor_t* te)
107 {
108 gate_result_t ret = GATE_RESULT_OK;
109 gate_strbuilder_t dummy;
110 gate_strbuilder_t* line;
111 gate_size_t linelen;
112
113 gate_size_t lines_count = gate_texteditor_get_line_count(te);
114 if (lines_count == 0)
115 {
116 gate_strbuilder_create(&dummy, 0);
117 if (NULL == gate_slotlist_add(&te->lines, &dummy))
118 {
119 return GATE_RESULT_FAILED;
120 }
121 ++lines_count;
122 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
123 }
124 if (te->cur_row >= lines_count)
125 {
126 te->cur_row = lines_count - 1;
127 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
128 }
129 if (te->cur_row < te->window_start_row)
130 {
131 te->window_start_row = te->cur_row;
132 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
133 }
134 if (te->cur_row >= te->window_start_row + te->window_height)
135 {
136 te->window_start_row = te->cur_row + 1 - te->window_height;
137 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
138 }
139
140 line = gate_texteditor_get_line(te, te->cur_row);
141 if (!line)
142 {
143 return GATE_RESULT_FAILED;
144 }
145
146 linelen = gate_strbuilder_length(line);
147 if (te->cur_col > linelen)
148 {
149 te->cur_col = linelen;
150 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
151 }
152
153 if (te->cur_col < te->window_start_col)
154 {
155 te->window_start_col = te->cur_col;
156 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
157 }
158 if (te->cur_col >= te->window_start_col + te->window_width)
159 {
160 te->window_start_col = te->cur_col + 1 - te->window_width;
161 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
162 }
163 return ret;
164 }
165
166 gate_result_t gate_texteditor_insert_char(gate_texteditor_t* te, gate_char32_t chr)
167 {
168 gate_strbuilder_t* line;
169 gate_size_t len;
170 char txt[8];
171 gate_size_t txtlen = gate_char_write_utf8(chr, txt, sizeof(txt));
172
173 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_LINE_DIRTY);
174
175 line = gate_texteditor_get_line(te, te->cur_row);
176 if (!line)
177 {
178 return GATE_RESULT_FAILED;
179 }
180
181 len = gate_strbuilder_length(line);
182 if (te->cur_col < len)
183 {
184 txtlen = gate_strbuilder_insert(line, te->cur_col, txt, txtlen);
185 te->cur_col += txtlen;
186 }
187 else
188 {
189 gate_strbuilder_append_text(line, txt, txtlen);
190 te->cur_col = gate_strbuilder_length(line);
191 }
192 return gate_texteditor_fix_view(te);
193 }
194
195 gate_result_t gate_texteditor_insert(gate_texteditor_t* te, char const* data, gate_size_t len)
196 {
197 gate_strbuilder_t* line = gate_texteditor_get_line(te, te->cur_row);
198 if (NULL == line)
199 {
200 return GATE_RESULT_OUTOFBOUNDS;
201 }
202 while (te->cur_col > gate_strbuilder_length(line))
203 {
204 gate_strbuilder_append_text(line, " ", 1);
205 }
206 if (0 == gate_strbuilder_insert(line, te->cur_col, data, len))
207 {
208 return GATE_RESULT_OUTOFMEMORY;
209 }
210 return gate_texteditor_fix_view(te);
211 }
212
213 gate_result_t gate_texteditor_delete_back(gate_texteditor_t* te)
214 {
215 gate_result_t ret = gate_texteditor_move(te, GATE_TEXTEDITOR_MOVE_LEFT);
216 if (GATE_FAILED(ret))
217 {
218 return ret;
219 }
220 return gate_texteditor_delete_char(te);
221 }
222
223 gate_result_t gate_texteditor_delete_char(gate_texteditor_t* te)
224 {
225 gate_strbuilder_t* line = gate_texteditor_get_line(te, te->cur_row);
226 gate_size_t len;
227 gate_strbuilder_t* nextline;
228
229 if (line == NULL)
230 {
231 return GATE_RESULT_OUTOFMEMORY;
232 }
233 len = gate_strbuilder_length(line);
234 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_LINE_DIRTY);
235 if (te->cur_col >= len)
236 {
237 /* merge next line with current one */
238 te->cur_col = len;
239 nextline = gate_slotlist_at(&te->lines, te->cur_row + 1);
240 if (nextline == NULL)
241 {
242 /* at end -> no char to delete */
243 return GATE_RESULT_NOTAVAILABLE;
244 }
245 gate_strbuilder_append_text(line, nextline->ptr_data, nextline->length);
246 gate_slotlist_remove_at(&te->lines, te->cur_row + 1);
247 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
248 }
249 else
250 {
251 /* just delete char at cursor */
252 gate_strbuilder_remove(line, te->cur_col, 1);
253 }
254 return gate_texteditor_fix_view(te);
255 }
256
257 gate_result_t gate_texteditor_move(gate_texteditor_t* te, unsigned move_direction)
258 {
259 gate_result_t ret = GATE_RESULT_FAILED;
260 gate_strbuilder_t* line;
261 gate_size_t length;
262
263 switch (move_direction)
264 {
265 case GATE_TEXTEDITOR_MOVE_LEFT:
266 {
267 if (te->cur_col <= 0)
268 {
269 ret = GATE_RESULT_FAILED;
270 }
271 else
272 {
273 --te->cur_col;
274 ret = gate_texteditor_fix_view(te);
275 }
276 break;
277 }
278 case GATE_TEXTEDITOR_MOVE_RIGHT:
279 {
280 line = gate_slotlist_at(&te->lines, te->cur_row);
281 length = gate_strbuilder_length(line);
282 if (te->cur_col >= length)
283 {
284 ret = GATE_RESULT_FAILED;
285 }
286 else
287 {
288 ++te->cur_col;
289 ret = gate_texteditor_fix_view(te);
290 }
291 break;
292 }
293 case GATE_TEXTEDITOR_MOVE_UP:
294 {
295 if (te->cur_row <= 0)
296 {
297 ret = GATE_RESULT_FAILED;
298 }
299 else
300 {
301 --te->cur_row;
302 if (te->cur_row < te->window_start_row)
303 {
304 --te->window_start_row;
305 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
306 }
307 ret = gate_texteditor_fix_view(te);
308 }
309 break;
310 }
311 case GATE_TEXTEDITOR_MOVE_DOWN:
312 {
313 length = gate_slotlist_length(&te->lines);
314 if (te->cur_row + 1 >= length)
315 {
316 return GATE_RESULT_FAILED;
317 }
318 else
319 {
320 ++te->cur_row;
321
322 if (te->cur_row >= te->window_start_row + te->window_height)
323 {
324 te->window_start_row = te->cur_row - (te->window_height - 1);
325 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
326 }
327 ret = gate_texteditor_fix_view(te);
328 }
329 break;
330 }
331 case GATE_TEXTEDITOR_MOVE_HOME:
332 {
333 te->cur_col = 0;
334 ret = gate_texteditor_fix_view(te);
335 break;
336 }
337 case GATE_TEXTEDITOR_MOVE_END:
338 {
339 line = gate_slotlist_at(&te->lines, te->cur_row);
340 length = gate_strbuilder_length(line);
341 te->cur_col = length;
342 ret = gate_texteditor_fix_view(te);
343 break;
344 }
345 case GATE_TEXTEDITOR_MOVE_PGUP:
346 {
347 if (te->window_start_row >= te->window_height)
348 {
349 te->window_start_row -= te->window_height;
350 }
351 else
352 {
353 te->window_start_row = 0;
354 }
355 if (te->cur_row >= te->window_height)
356 {
357 te->cur_row -= te->window_height;
358 }
359 else
360 {
361 te->cur_row = 0;
362 }
363 ret = gate_texteditor_fix_view(te);
364 break;
365 }
366 case GATE_TEXTEDITOR_MOVE_PGDOWN:
367 {
368 length = gate_slotlist_length(&te->lines);
369 if (te->window_start_row + te->window_height < length)
370 {
371 te->window_start_row += te->window_height;
372 ret = gate_texteditor_fix_view(te);
373 }
374 break;
375 }
376 }
377 return ret;
378 }
379
380 gate_result_t gate_texteditor_new_line(gate_texteditor_t* te)
381 {
382 gate_result_t ret = GATE_RESULT_FAILED;
383 gate_strbuilder_t* line = gate_texteditor_get_line(te, te->cur_row);
384 gate_strbuilder_t new_line = GATE_INIT_EMPTY;
385 gate_strbuilder_t* next_line;
386 gate_size_t line_len;
387
388 do
389 {
390 if (!line)
391 {
392 ret = GATE_RESULT_FAILED;
393 break;
394 }
395 gate_strbuilder_create(&new_line, 0);
396 next_line = (gate_strbuilder_t*)gate_slotlist_insert_at(&te->lines, te->cur_row + 1, &new_line);
397 gate_strbuilder_release(&new_line);
398 if (NULL == next_line)
399 {
400 ret = GATE_RESULT_FAILED;
401 break;
402 }
403 line_len = gate_strbuilder_length(line);
404 if (te->cur_col < line_len)
405 {
406 gate_strbuilder_append_text(next_line, gate_strbuilder_ptr(line, te->cur_col), line_len - te->cur_col);
407 gate_strbuilder_remove(line, te->cur_col, line_len - te->cur_col);
408 }
409 te->cur_col = 0;
410 ++te->cur_row;
411 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
412 ret = GATE_RESULT_OK;
413 } while (0);
414
415 if (GATE_SUCCEEDED(ret))
416 {
417 return gate_texteditor_fix_view(te);
418 }
419 return ret;
420 }
421
422
423 gate_size_t gate_texteditor_get_line_count(gate_texteditor_t* te)
424 {
425 return gate_slotlist_length(&te->lines);
426 }
427
428 unsigned gate_texteditor_get_state(gate_texteditor_t* te)
429 {
430 return te->state;
431 }
432
433 void gate_texteditor_set_state(gate_texteditor_t* te, unsigned state)
434 {
435 te->state = state;
436 }
437
438 gate_size_t gate_texteditor_get_line_view(gate_texteditor_t* te, gate_size_t line, char const** ptr_text)
439 {
440 gate_strbuilder_t* ptr_line;
441
442 ptr_line = (gate_strbuilder_t*)gate_slotlist_at(&te->lines, line);
443 if (ptr_text)
444 {
445 *ptr_text = gate_strbuilder_ptr(ptr_line, 0);
446 }
447 return gate_strbuilder_length(ptr_line);
448 }
449