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_system.h"
28 #include "gx_canvas.h"
29 #include "gx_context.h"
30 #include "gx_multi_line_text_view.h"
31 #include "gx_utility.h"
32 #include "gx_widget.h"
33 #include "gx_window.h"
34 #include "gx_scrollbar.h"
35 
36 /**************************************************************************/
37 /*                                                                        */
38 /*  FUNCTION                                               RELEASE        */
39 /*                                                                        */
40 /*    _gx_multi_line_text_view_draw                       PORTABLE C      */
41 /*                                                           6.1          */
42 /*  AUTHOR                                                                */
43 /*                                                                        */
44 /*    Kenneth Maxwell, Microsoft Corporation                              */
45 /*                                                                        */
46 /*  DESCRIPTION                                                           */
47 /*                                                                        */
48 /*    This function draws text for a multi-line-text-view widget.         */
49 /*                                                                        */
50 /*  INPUT                                                                 */
51 /*                                                                        */
52 /*    text_view                              Multi-line_text_view widget  */
53 /*                                             control block              */
54 /*    text_color                             ID of text color             */
55 /*                                                                        */
56 /*  OUTPUT                                                                */
57 /*                                                                        */
58 /*    None                                                                */
59 /*                                                                        */
60 /*  CALLS                                                                 */
61 /*                                                                        */
62 /*    _gx_context_line_color_set            Set the context color         */
63 /*    _gx_context_font_get                  Get font associated with the  */
64 /*                                            specified ID                */
65 /*    _gx_context_font_set                  Set the context font          */
66 /*    _gx_context_brush_width_set           Set the brush width           */
67 /*    _gx_multi_line_text_view_visible_rows_compute                       */
68 /*                                          Calculate visible rows        */
69 /*    _gx_multi_line_text_view_string_total_rows_compute                  */
70 /*                                          Calculate total rows for input*/
71 /*                                            string                      */
72 /*    _gx_utility_rectangle_resize          Offset rectangle by specified */
73 /*                                            value                       */
74 /*    _gx_utility_rectangle_overlap_detect  Detect rectangle overlaps     */
75 /*    _gx_utility_string_length_check       Test string length            */
76 /*    _gx_system_string_get                 Get string by specified id    */
77 /*    _gx_system_private_string_get         Get string pointer in         */
78 /*                                            dynamically copied string   */
79 /*                                            buffer                      */
80 /*    _gx_multi_line_text_view_paragraph_start_get                        */
81 /*                                          Get start index of a paragraph*/
82 /*    _gx_utility_bidi_paragraph_reorder    Reorder bidi text for display */
83 /*    _gx_system_string_width_get           Get string width              */
84 /*    _gx_canvas_text_draw                  Draw the text                 */
85 /*    _gx_canvas_drawing_initiate           Initiate a drawing context    */
86 /*    _gx_canvas_drawing_complete           Complete a drawing            */
87 /*                                                                        */
88 /*  CALLED BY                                                             */
89 /*                                                                        */
90 /*    Application Code                                                    */
91 /*    GUIX Internal Code                                                  */
92 /*                                                                        */
93 /*  RELEASE HISTORY                                                       */
94 /*                                                                        */
95 /*    DATE              NAME                      DESCRIPTION             */
96 /*                                                                        */
97 /*  05-19-2020     Kenneth Maxwell          Initial Version 6.0           */
98 /*  09-30-2020     Kenneth Maxwell          Modified comment(s),          */
99 /*                                            support runtime Arabic      */
100 /*                                            line breaking,              */
101 /*                                            modified logic of dynamic   */
102 /*                                            bidi text draw,             */
103 /*                                            resulting in version 6.1    */
104 /*                                                                        */
105 /**************************************************************************/
_gx_multi_line_text_view_text_draw(GX_MULTI_LINE_TEXT_VIEW * text_view,GX_RESOURCE_ID text_color)106 VOID  _gx_multi_line_text_view_text_draw(GX_MULTI_LINE_TEXT_VIEW *text_view, GX_RESOURCE_ID text_color)
107 {
108 INT           index;
109 INT           line_height;
110 GX_STRING     string;
111 GX_STRING     line_string;
112 INT           x_pos;
113 INT           y_pos;
114 GX_RECTANGLE  client;
115 GX_RECTANGLE  draw_area;
116 GX_CANVAS    *canvas;
117 INT           first_visible_line;
118 INT           last_visible_line;
119 UINT          line_start_index;
120 UINT          line_end_index;
121 UINT          line_cache_start;
122 GX_VALUE      text_width;
123 GX_VALUE      space_width;
124 GX_VALUE      client_width;
125 GX_FONT      *font;
126 GX_SCROLLBAR *scroll;
127 
128 #if defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT)
129 GX_BIDI_RESOLVED_TEXT_INFO *next = GX_NULL;
130 UINT                        bidi_text_line_index = 0;
131 #endif
132 
133     _gx_context_line_color_set(text_color);
134     _gx_context_font_get(text_view -> gx_multi_line_text_view_font_id, &font);
135     _gx_context_font_set(text_view -> gx_multi_line_text_view_font_id);
136     _gx_context_brush_width_set(1);
137 
138     _gx_window_scrollbar_find((GX_WINDOW *)text_view, GX_TYPE_VERTICAL_SCROLL, &scroll);
139 
140     if (text_view -> gx_multi_line_text_view_line_index_old)
141     {
142 
143         /* Get visible rows. */
144         _gx_multi_line_text_view_visible_rows_compute(text_view);
145 
146         /* Calculate text total rows. */
147         _gx_multi_line_text_view_string_total_rows_compute(text_view);
148 
149         if (scroll)
150         {
151             /* Reset scrollbar.  */
152             _gx_scrollbar_reset(scroll, GX_NULL);
153         }
154         else
155         {
156             if (text_view -> gx_multi_line_text_view_text_total_rows >
157                 text_view -> gx_multi_line_text_view_cache_size)
158             {
159                 /* Update line cache. */
160                 _gx_multi_line_text_view_line_cache_update(text_view);
161             }
162         }
163     }
164 
165     /* Is there a string and font?  */
166     if ((text_view -> gx_multi_line_text_view_text.gx_string_length <= 0)  ||
167         font == GX_NULL)
168     {
169         return;
170     }
171 
172     /* Pickup text height. */
173     line_height = font -> gx_font_line_height + text_view -> gx_multi_line_text_view_line_space;
174 
175     if (!line_height)
176     {
177         return;
178     }
179 
180     /* pick up current canvas */
181     canvas = _gx_system_current_draw_context -> gx_draw_context_canvas;
182 
183     /* Pick up client area.  */
184     client = text_view -> gx_window_client;
185 
186     /* Offset client area by the size of whitespace.  */
187     _gx_utility_rectangle_resize(&client, (GX_VALUE)(-text_view -> gx_multi_line_text_view_whitespace));
188 
189     /* check for auto-scrolling vertically centered text */
190     if (text_view -> gx_widget_type == GX_TYPE_MULTI_LINE_TEXT_VIEW &&
191         scroll == GX_NULL &&
192         (text_view -> gx_widget_style & GX_STYLE_VALIGN_CENTER) == GX_STYLE_VALIGN_CENTER)
193     {
194         space_width = (GX_VALUE)(client.gx_rectangle_bottom - client.gx_rectangle_top);
195         space_width = (GX_VALUE)((INT)space_width - (INT)((INT)text_view -> gx_multi_line_text_view_text_total_rows * line_height));
196         text_view -> gx_multi_line_text_view_text_scroll_shift = (space_width >> 1);
197     }
198 
199     _gx_utility_rectangle_overlap_detect(&_gx_system_current_draw_context -> gx_draw_context_dirty, &client, &draw_area);
200     _gx_canvas_drawing_initiate(canvas, (GX_WIDGET *)text_view, &draw_area);
201 
202     /* Compute the start displaying position of pixels in x direction and y direction. */
203     y_pos = client.gx_rectangle_top;
204     y_pos += text_view -> gx_multi_line_text_view_text_scroll_shift;
205     y_pos += (text_view -> gx_multi_line_text_view_line_space >> 1);
206 
207     line_string.gx_string_ptr = " ";
208     line_string.gx_string_length = 1;
209 
210     _gx_system_string_width_get_ext(font, &line_string, &space_width);
211 
212     first_visible_line = ((-text_view -> gx_multi_line_text_view_text_scroll_shift)) / line_height;
213 
214     if (first_visible_line < 0)
215     {
216         first_visible_line = 0;
217     }
218 
219     last_visible_line = first_visible_line + (INT)(text_view -> gx_multi_line_text_view_text_visible_rows);
220 
221     if (last_visible_line > (INT)(text_view -> gx_multi_line_text_view_text_total_rows - 1))
222     {
223         last_visible_line = (INT)(text_view -> gx_multi_line_text_view_text_total_rows - 1);
224     }
225 
226     if (text_view -> gx_multi_line_text_view_text_id)
227     {
228         _gx_widget_string_get_ext((GX_WIDGET *)text_view, text_view -> gx_multi_line_text_view_text_id, &string);
229     }
230     else
231     {
232         _gx_system_private_string_get(&text_view -> gx_multi_line_text_view_text, &string, text_view -> gx_widget_style);
233     }
234 
235     y_pos += (INT)(first_visible_line * line_height);
236 
237     for (index = first_visible_line; index <= last_visible_line; index++)
238     {
239 #if defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT)
240         if (_gx_system_bidi_text_enabled)
241         {
242             line_string.gx_string_ptr = GX_NULL;
243             line_string.gx_string_length = 0;
244 
245             if (!next)
246             {
247                 next = text_view -> gx_multi_line_text_view_bidi_resolved_text_info;
248             }
249 
250             while (next)
251             {
252                 if (bidi_text_line_index + next -> gx_bidi_resolved_text_info_total_lines > (UINT)index)
253                 {
254                     /* Get line string. */
255                     if (next -> gx_bidi_resolved_text_info_text)
256                     {
257                         line_string = next -> gx_bidi_resolved_text_info_text[(UINT)index - bidi_text_line_index];
258                     }
259                     break;
260                 }
261 
262                 bidi_text_line_index += next -> gx_bidi_resolved_text_info_total_lines;
263                 next = next -> gx_bidi_resolved_text_info_next;
264             }
265         }
266         else
267         {
268 #endif /* defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT) */
269             line_cache_start = text_view -> gx_multi_line_text_view_first_cache_line;
270             line_start_index = text_view -> gx_multi_line_text_view_line_index[index - (INT)line_cache_start];
271 
272             if ((INT)(index - (INT)line_cache_start) >= (INT)(text_view -> gx_multi_line_text_view_cache_size - 1))
273             {
274                 line_end_index = text_view -> gx_multi_line_text_view_text.gx_string_length;
275             }
276             else
277             {
278                 line_end_index = text_view -> gx_multi_line_text_view_line_index[index - (INT)(line_cache_start) + 1];
279             }
280 
281             /* Get line string. */
282             line_string.gx_string_ptr = string.gx_string_ptr + line_start_index;
283             line_string.gx_string_length = line_end_index - line_start_index;
284 #if defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT)
285         }
286 #endif /* defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT) */
287 
288 
289         switch (text_view -> gx_widget_style & GX_STYLE_TEXT_ALIGNMENT_MASK)
290         {
291         case GX_STYLE_TEXT_RIGHT:
292             _gx_system_string_width_get_ext(font, &line_string, &text_width);
293             while (text_width > (client.gx_rectangle_right - client.gx_rectangle_left - 2))
294             {
295                 text_width = (GX_VALUE)(text_width - space_width);
296             }
297             x_pos = client.gx_rectangle_right - 1;
298             x_pos = (GX_VALUE)(x_pos - text_width);
299             break;
300         case GX_STYLE_TEXT_LEFT:
301             x_pos = client.gx_rectangle_left + 1;
302             break;
303         case GX_STYLE_TEXT_CENTER:
304         default:
305             _gx_system_string_width_get_ext(font, &line_string, &text_width);
306             client_width = (GX_VALUE)(client.gx_rectangle_right - client.gx_rectangle_left + 1);
307             while (text_width > (client_width - 3))
308             {
309                 text_width = (GX_VALUE)(text_width - space_width);
310             }
311             x_pos = (GX_VALUE)(client.gx_rectangle_left + ((client_width - text_width) / 2));
312             break;
313         }
314 
315         /* Draw the text. */
316         _gx_canvas_text_draw_ext((GX_VALUE)x_pos, (GX_VALUE)y_pos, &line_string);
317         y_pos += line_height;
318     }
319 
320     _gx_canvas_drawing_complete(canvas, GX_FALSE);
321 }
322 
323