1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** GUIX Component */
16 /** */
17 /** Multi Line Text View Management (Multi Line Text View) */
18 /** */
19 /**************************************************************************/
20
21 #define GX_SOURCE_CODE
22
23
24 /* Include necessary system files. */
25
26 #include "gx_api.h"
27 #include "gx_widget.h"
28 #include "gx_system.h"
29 #include "gx_window.h"
30 #include "gx_multi_line_text_view.h"
31
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _gx_multi_line_text_view_line_index_find PORTABLE C */
37 /* 6.1 */
38 /* AUTHOR */
39 /* */
40 /* Kenneth Maxwell, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* Internal helper function to find the first byte index of the */
45 /* sepcified line from the specified position. */
46 /* */
47 /* INPUT */
48 /* */
49 /* text_view Multi line text view */
50 /* control block */
51 /* start_line Line index to search from */
52 /* start_index Byte index to search from */
53 /* search_line Index of the line to search */
54 /* return_index Retrieved byte index of the */
55 /* line */
56 /* */
57 /* OUTPUT */
58 /* */
59 /* None */
60 /* */
61 /* CALLS */
62 /* */
63 /* _gx_window_client_width_get Get window client width */
64 /* _gx_multi_line_text_view_display_info_get */
65 /* Get the number of character */
66 /* bytes a line can display */
67 /* */
68 /* CALLED BY */
69 /* */
70 /* _gx_multi_line_text_view_line_start_cache_update */
71 /* */
72 /* RELEASE HISTORY */
73 /* */
74 /* DATE NAME DESCRIPTION */
75 /* */
76 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
77 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
78 /* resulting in version 6.1 */
79 /* */
80 /**************************************************************************/
_gx_multi_line_text_view_line_index_find(GX_MULTI_LINE_TEXT_VIEW * view,UINT start_line,UINT start_index,UINT search_line,UINT * return_index)81 static VOID _gx_multi_line_text_view_line_index_find(GX_MULTI_LINE_TEXT_VIEW *view, UINT start_line, UINT start_index, UINT search_line, UINT *return_index)
82 {
83 GX_VALUE client_width;
84 GX_MULTI_LINE_TEXT_INFO text_info;
85
86 /* Pickup widget width. */
87 _gx_window_client_width_get((GX_WINDOW *)view, &client_width);
88
89 /* Calcualte client width. */
90 client_width = (GX_VALUE)(client_width - (view -> gx_multi_line_text_view_whitespace << 1));
91 client_width = (GX_VALUE)(client_width - 2);
92
93 /* Calcualte from the start of the text. */
94 while (start_line < search_line)
95 {
96 /* Caculate maximum byte number a line can display in forward direction. */
97 _gx_multi_line_text_view_display_info_get(view, start_index, view -> gx_multi_line_text_view_text.gx_string_length, &text_info, client_width);
98
99 start_index += text_info.gx_text_display_number;
100 start_line++;
101 }
102
103 *return_index = start_index;
104 }
105
106 /**************************************************************************/
107 /* */
108 /* FUNCTION RELEASE */
109 /* */
110 /* _gx_multi_line_text_view_line_start_cache_create PORTABLE C */
111 /* 6.1 */
112 /* AUTHOR */
113 /* */
114 /* Kenneth Maxwell, Microsoft Corporation */
115 /* */
116 /* DESCRIPTION */
117 /* */
118 /* Internal helper function to create line index cache for specified */
119 /* line range. */
120 /* */
121 /* INPUT */
122 /* */
123 /* text_view Multi line text view */
124 /* control block */
125 /* first_line The line to start cache */
126 /* updated_size The numer of lines to cache */
127 /* */
128 /* OUTPUT */
129 /* */
130 /* None */
131 /* */
132 /* CALLS */
133 /* */
134 /* _gx_window_client_width_get Get window client width */
135 /* _gx_multi_line_text_view_line_index_find */
136 /* Get index of the line */
137 /* _gx_multi_line_text_view_display_info_get */
138 /* Get the number of character */
139 /* bytes a line can display */
140 /* */
141 /* CALLED BY */
142 /* */
143 /* _gx_multi_line_text_view_line_start_cache_update */
144 /* */
145 /* RELEASE HISTORY */
146 /* */
147 /* DATE NAME DESCRIPTION */
148 /* */
149 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
150 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
151 /* resulting in version 6.1 */
152 /* */
153 /**************************************************************************/
_gx_multi_line_text_view_line_start_cache_create(GX_MULTI_LINE_TEXT_VIEW * view,UINT first_line,UINT updated_size)154 static VOID _gx_multi_line_text_view_line_start_cache_create(GX_MULTI_LINE_TEXT_VIEW *view, UINT first_line, UINT updated_size)
155 {
156 GX_VALUE client_width;
157 GX_MULTI_LINE_TEXT_INFO text_info;
158 UINT index;
159 UINT loop;
160
161 /* Pickup widget width. */
162 _gx_window_client_width_get((GX_WINDOW *)view, &client_width);
163
164 /* Calcualte client width. */
165 client_width = (GX_VALUE)(client_width - (view -> gx_multi_line_text_view_whitespace << 1));
166 client_width = (GX_VALUE)(client_width - 2);
167
168 /* Find index of first line. */
169 _gx_multi_line_text_view_line_index_find(view, 0, 0, first_line, &index);
170
171 view -> gx_multi_line_text_view_line_index[0] = index;
172
173 for (loop = 1; loop < updated_size; loop++)
174 {
175 /* Caculate maximum byte number a line can display in forward direction. */
176 _gx_multi_line_text_view_display_info_get(view, index, view -> gx_multi_line_text_view_text.gx_string_length, &text_info, client_width);
177
178 index += text_info.gx_text_display_number;
179 view -> gx_multi_line_text_view_line_index[loop] = index;
180 }
181 }
182
183 /**************************************************************************/
184 /* */
185 /* FUNCTION RELEASE */
186 /* */
187 /* _gx_multi_line_text_view_line_tail_cache_create PORTABLE C */
188 /* 6.1 */
189 /* AUTHOR */
190 /* */
191 /* Kenneth Maxwell, Microsoft Corporation */
192 /* */
193 /* DESCRIPTION */
194 /* */
195 /* Internal helper function to create line index cache for specified */
196 /* line range with specified byte index and line index to start */
197 /* calculating needed line index. */
198 /* */
199 /* INPUT */
200 /* */
201 /* view Multi line text view */
202 /* control block */
203 /* start_index The first byte index of the */
204 /* line to search from */
205 /* start_line The line to search from */
206 /* first_line The line to start cache */
207 /* updated_size The number of lines to cache */
208 /* */
209 /* OUTPUT */
210 /* */
211 /* None */
212 /* */
213 /* CALLS */
214 /* */
215 /* _gx_window_client_width_get Get window client width */
216 /* _gx_multi_line_text_view_line_index_find */
217 /* Search byte index of specified*/
218 /* line */
219 /* _gx_multi_line_text_view_display_info_get */
220 /* Calculate the number of bytes */
221 /* that a line can display */
222 /* _gx_system_string_get Retrieve string by ID */
223 /* _gx_system_private_string_get Retrieve string pointer in a */
224 /* dynamically copied string */
225 /* buffer */
226 /* */
227 /* CALLED BY */
228 /* */
229 /* _gx_multi_line_text_view_line_tail_cache_update */
230 /* */
231 /* RELEASE HISTORY */
232 /* */
233 /* DATE NAME DESCRIPTION */
234 /* */
235 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
236 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
237 /* resulting in version 6.1 */
238 /* */
239 /**************************************************************************/
_gx_multi_line_text_view_line_tail_cache_create(GX_MULTI_LINE_TEXT_VIEW * view,UINT start_index,UINT start_line,UINT first_line,UINT updated_size)240 static VOID _gx_multi_line_text_view_line_tail_cache_create(GX_MULTI_LINE_TEXT_VIEW *view, UINT start_index, UINT start_line, UINT first_line, UINT updated_size)
241 {
242 GX_VALUE client_width;
243 GX_MULTI_LINE_TEXT_INFO text_info;
244 UINT index;
245 UINT loop;
246
247 /* Pickup widget width. */
248 _gx_window_client_width_get((GX_WINDOW *)view, &client_width);
249
250 /* Calcualte client width. */
251 client_width = (GX_VALUE)(client_width - (view -> gx_multi_line_text_view_whitespace << 1));
252 client_width = (GX_VALUE)(client_width - 2);
253
254 /* Find index of first line. */
255 _gx_multi_line_text_view_line_index_find(view, start_line, start_index, first_line, &index);
256
257 view -> gx_multi_line_text_view_line_index[GX_MULTI_LINE_INDEX_CACHE_SIZE - updated_size] = index;
258
259 for (loop = GX_MULTI_LINE_INDEX_CACHE_SIZE - updated_size + 1; loop < GX_MULTI_LINE_INDEX_CACHE_SIZE; loop++)
260 {
261 /* Calculate maximum byte number a line can display in forward direction. */
262 _gx_multi_line_text_view_display_info_get(view, (UINT)index, view -> gx_multi_line_text_view_text.gx_string_length, &text_info, client_width);
263
264 index += text_info.gx_text_display_number;
265 view -> gx_multi_line_text_view_line_index[loop] = index;
266 }
267 }
268
269 /**************************************************************************/
270 /* */
271 /* FUNCTION RELEASE */
272 /* */
273 /* _gx_multi_line_text_view_line_start_cache_update PORTABLE C */
274 /* 6.1 */
275 /* AUTHOR */
276 /* */
277 /* Kenneth Maxwell, Microsoft Corporation */
278 /* */
279 /* DESCRIPTION */
280 /* */
281 /* Internal helper function to update line index cache to cover */
282 /* previous lines. */
283 /* */
284 /* INPUT */
285 /* */
286 /* view Multi line text view */
287 /* control block */
288 /* new_first_cache_line The new line index that cache */
289 /* start */
290 /* */
291 /* OUTPUT */
292 /* */
293 /* None */
294 /* */
295 /* CALLS */
296 /* */
297 /* _gx_multi_line_text_view_line_start_cache_create */
298 /* Create line index cache for */
299 /* specified range */
300 /* */
301 /* CALLED BY */
302 /* */
303 /* _gx_multi_line_text_view_line_cache_update */
304 /* */
305 /* RELEASE HISTORY */
306 /* */
307 /* DATE NAME DESCRIPTION */
308 /* */
309 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
310 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
311 /* resulting in version 6.1 */
312 /* */
313 /**************************************************************************/
_gx_multi_line_text_view_line_start_cache_update(GX_MULTI_LINE_TEXT_VIEW * view,UINT new_first_cache_line)314 static VOID _gx_multi_line_text_view_line_start_cache_update(GX_MULTI_LINE_TEXT_VIEW *view, UINT new_first_cache_line)
315 {
316 UINT first_cache_line;
317 UINT updated_size;
318
319 /* Pickup first cache line. */
320 first_cache_line = view -> gx_multi_line_text_view_first_cache_line;
321
322 updated_size = first_cache_line - new_first_cache_line;
323
324 if (updated_size < GX_MULTI_LINE_INDEX_CACHE_SIZE)
325 {
326 /* Move line index value in [0, index_cache_size - updated_size] to [updated_size, index_cache_size]. */
327 memmove(view -> gx_multi_line_text_view_line_index + updated_size, view -> gx_multi_line_text_view_line_index,
328 sizeof(UINT) * (GX_MULTI_LINE_INDEX_CACHE_SIZE - updated_size));
329 }
330 else
331 {
332 updated_size = GX_MULTI_LINE_INDEX_CACHE_SIZE;
333 }
334
335 _gx_multi_line_text_view_line_start_cache_create(view, new_first_cache_line, updated_size);
336
337 /* Update cache size. */
338 view -> gx_multi_line_text_view_cache_size = (GX_UBYTE)(view -> gx_multi_line_text_view_cache_size + updated_size);
339
340 if (view -> gx_multi_line_text_view_cache_size > GX_MULTI_LINE_INDEX_CACHE_SIZE)
341 {
342 view -> gx_multi_line_text_view_cache_size = GX_MULTI_LINE_INDEX_CACHE_SIZE;
343 }
344
345 /* Update first cache line. */
346 view -> gx_multi_line_text_view_first_cache_line = new_first_cache_line;
347 }
348
349 /**************************************************************************/
350 /* */
351 /* FUNCTION RELEASE */
352 /* */
353 /* _gx_multi_line_text_view_line_tail_cache_update PORTABLE C */
354 /* 6.1 */
355 /* AUTHOR */
356 /* */
357 /* Kenneth Maxwell, Microsoft Corporation */
358 /* */
359 /* DESCRIPTION */
360 /* */
361 /* Internal helper function to update line index cache to cover */
362 /* following lines. */
363 /* */
364 /* INPUT */
365 /* */
366 /* view Multi line text view */
367 /* control block */
368 /* new_first_cache_line The new line index that cache */
369 /* start */
370 /* */
371 /* OUTPUT */
372 /* */
373 /* None */
374 /* */
375 /* CALLS */
376 /* */
377 /* _gx_multi_line_text_view_line_tail_cache_create */
378 /* Create line index cache for */
379 /* specified range */
380 /* */
381 /* CALLED BY */
382 /* */
383 /* _gx_multi_line_text_view_line_cache_update */
384 /* */
385 /* RELEASE HISTORY */
386 /* */
387 /* DATE NAME DESCRIPTION */
388 /* */
389 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
390 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
391 /* resulting in version 6.1 */
392 /* */
393 /**************************************************************************/
_gx_multi_line_text_view_line_tail_cache_update(GX_MULTI_LINE_TEXT_VIEW * view,UINT new_first_cache_line)394 static VOID _gx_multi_line_text_view_line_tail_cache_update(GX_MULTI_LINE_TEXT_VIEW *view, UINT new_first_cache_line)
395 {
396 UINT first_cache_line;
397 UINT updated_size;
398 UINT last_cache_index;
399 UINT last_cache_line;
400
401 /* Pickup first cache line. */
402 first_cache_line = view -> gx_multi_line_text_view_first_cache_line;
403
404 updated_size = new_first_cache_line - first_cache_line;
405
406 last_cache_index = view -> gx_multi_line_text_view_line_index[GX_MULTI_LINE_INDEX_CACHE_SIZE - 1];
407 last_cache_line = view -> gx_multi_line_text_view_first_cache_line + GX_MULTI_LINE_INDEX_CACHE_SIZE - 1;
408
409 if (updated_size < GX_MULTI_LINE_INDEX_CACHE_SIZE)
410 {
411 /* Move line index value in [updated_size, index_cache_size - 1] to [0, index_cache_size - updated_size]. */
412 memmove(view -> gx_multi_line_text_view_line_index, view -> gx_multi_line_text_view_line_index + updated_size,
413 sizeof(UINT) * (GX_MULTI_LINE_INDEX_CACHE_SIZE - updated_size));
414 }
415 else
416 {
417 updated_size = GX_MULTI_LINE_INDEX_CACHE_SIZE;
418 }
419
420 _gx_multi_line_text_view_line_tail_cache_create(view, last_cache_index, last_cache_line,
421 new_first_cache_line + GX_MULTI_LINE_INDEX_CACHE_SIZE - updated_size, updated_size);
422
423 /* Update cache size. */
424 view -> gx_multi_line_text_view_cache_size = GX_MULTI_LINE_INDEX_CACHE_SIZE;
425
426 /* Update first cache line. */
427 view -> gx_multi_line_text_view_first_cache_line = new_first_cache_line;
428 }
429
430 /**************************************************************************/
431 /* */
432 /* FUNCTION RELEASE */
433 /* */
434 /* _gx_multi_line_text_view_line_cache_update PORTABLE C */
435 /* 6.1.5 */
436 /* AUTHOR */
437 /* */
438 /* Kenneth Maxwell, Microsoft Corporation */
439 /* */
440 /* DESCRIPTION */
441 /* */
442 /* This function updates the line index cache of the displayed text. */
443 /* */
444 /* INPUT */
445 /* */
446 /* text_view Multi line text view */
447 /* control block */
448 /* OUTPUT */
449 /* */
450 /* Completion Status */
451 /* */
452 /* CALLS */
453 /* */
454 /* _gx_widget_font_get Get font by resource ID */
455 /* _gx_multi_line_text_view_line_start_cache_update */
456 /* Update line index cache to */
457 /* cover previous lines */
458 /* _gx_multi_line_text_view_line_tail_cache_update */
459 /* Update line index cache to */
460 /* cover following lines */
461 /* */
462 /* CALLED BY */
463 /* */
464 /* GUIX Internal Code */
465 /* */
466 /* RELEASE HISTORY */
467 /* */
468 /* DATE NAME DESCRIPTION */
469 /* */
470 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
471 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
472 /* added logic to disable line */
473 /* cache process while dynamic */
474 /* bidi text is enabled, */
475 /* resulting in version 6.1 */
476 /* 03-02-2021 Ting Zhu Modified comment(s), fixed */
477 /* line height calculation, */
478 /* resulting in version 6.1.5 */
479 /* */
480 /**************************************************************************/
_gx_multi_line_text_view_line_cache_update(GX_MULTI_LINE_TEXT_VIEW * view)481 UINT _gx_multi_line_text_view_line_cache_update(GX_MULTI_LINE_TEXT_VIEW *view)
482 {
483 UINT first_visible_line;
484 UINT last_visible_line;
485 UINT first_cache_line;
486 INT new_first_cache_line;
487 INT line_height;
488 GX_FONT *font;
489
490 #if defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT)
491 if (_gx_system_bidi_text_enabled)
492 {
493 /* No need to update line index cache when dynamic bidi text option is on. */
494 return GX_SUCCESS;
495 }
496 #endif
497
498 _gx_widget_font_get((GX_WIDGET *)view, view -> gx_multi_line_text_view_font_id, &font);
499
500 if (font)
501 {
502 /* Pickup line height. */
503 line_height = font -> gx_font_line_height + view -> gx_multi_line_text_view_line_space;
504
505 if (line_height <= 0)
506 {
507 return GX_FAILURE;
508 }
509
510 /* Calcualte first visible line. */
511 if (view -> gx_multi_line_text_view_text_scroll_shift < 0)
512 {
513 first_visible_line = ((UINT)-view -> gx_multi_line_text_view_text_scroll_shift) / (UINT)line_height;
514 }
515 else
516 {
517 first_visible_line = 0;
518 }
519
520 /* Calculate last visible line. */
521 last_visible_line = first_visible_line + view -> gx_multi_line_text_view_text_visible_rows;
522
523 /* Pickuo first cache line. */
524 first_cache_line = view -> gx_multi_line_text_view_first_cache_line;
525
526 /* Calculat new first cache line. */
527 new_first_cache_line = GX_MULTI_LINE_INDEX_CACHE_SIZE;
528 new_first_cache_line -= (INT)(last_visible_line - first_visible_line + 1);
529 new_first_cache_line /= 2;
530 new_first_cache_line = (INT)first_visible_line - new_first_cache_line;
531
532 if ((UINT)new_first_cache_line + GX_MULTI_LINE_INDEX_CACHE_SIZE > view -> gx_multi_line_text_view_text_total_rows)
533 {
534 new_first_cache_line = (INT)(view -> gx_multi_line_text_view_text_total_rows - GX_MULTI_LINE_INDEX_CACHE_SIZE);
535 }
536
537 if (first_visible_line <= first_cache_line)
538 {
539 if (new_first_cache_line < 0)
540 {
541 new_first_cache_line = 0;
542 }
543
544 if (new_first_cache_line < (INT)first_cache_line)
545 {
546 /* Update line start cache. */
547 _gx_multi_line_text_view_line_start_cache_update(view, (UINT)new_first_cache_line);
548 }
549 }
550 else if (last_visible_line >= first_cache_line + view -> gx_multi_line_text_view_cache_size - 1)
551 {
552 if (new_first_cache_line > (INT)first_cache_line)
553 {
554 /* Update line tail cache. */
555 _gx_multi_line_text_view_line_tail_cache_update(view, (UINT)new_first_cache_line);
556 }
557 }
558 }
559 return GX_SUCCESS;
560 }
561
562