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