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 /**   Rich Text View Management (Rich 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_rich_text_view.h"
30 #include "gx_window.h"
31 #include "gx_utility.h"
32 #include "gx_widget.h"
33 #include "gx_context.h"
34 #include "gx_scrollbar.h"
35 
36 /**************************************************************************/
37 /*                                                                        */
38 /*  FUNCTION                                               RELEASE        */
39 /*                                                                        */
40 /*    _gx_rich_text_view_single_line_draw                 PORTABLE C      */
41 /*                                                           6.1.5        */
42 /*  AUTHOR                                                                */
43 /*                                                                        */
44 /*    Kenneth Maxwell, Microsoft Corporation                              */
45 /*                                                                        */
46 /*  DESCRIPTION                                                           */
47 /*                                                                        */
48 /*    This function draws a single line text for a rich text view widget. */
49 /*                                                                        */
50 /*  INPUT                                                                 */
51 /*                                                                        */
52 /*    text_view                              Rich text view control block */
53 /*    ypos                                   Draw start y position        */
54 /*    line_info                              Text information for drawing */
55 /*                                                                        */
56 /*  OUTPUT                                                                */
57 /*                                                                        */
58 /*    None                                                                */
59 /*                                                                        */
60 /*  CALLS                                                                 */
61 /*                                                                        */
62 /*    _gx_rich_text_view_tag_enter          Detect rich text enter tag    */
63 /*    _gx_system_rich_text_format_pop       Pop rich text format          */
64 /*    _gx_system_rich_text_format_push      Push rich text format         */
65 /*    _gx_widget_font_get                   Retrieve font by font id      */
66 /*    _gx_system_string_width_get           Get string width              */
67 /*    _gx_canvas_text_draw                  Draw text                     */
68 /*    _gx_canvas_line_draw                  Draw line                     */
69 /*                                                                        */
70 /*  CALLED BY                                                             */
71 /*                                                                        */
72 /*    _gx_rich_text_view_draw                                             */
73 /*                                                                        */
74 /*  RELEASE HISTORY                                                       */
75 /*                                                                        */
76 /*    DATE              NAME                      DESCRIPTION             */
77 /*                                                                        */
78 /*  09-30-2020     Kenneth Maxwell          Initial Version 6.1           */
79 /*  03-02-2021     Ting Zhu                 Modified comment(s),          */
80 /*                                            removed unreachable code,   */
81 /*                                            resulting in version 6.1.5  */
82 /*                                                                        */
83 /**************************************************************************/
_gx_rich_text_view_single_line_draw(GX_RICH_TEXT_VIEW * text_view,GX_VALUE ypos,GX_RICH_TEXT_LINE_INFO * line_info)84 static VOID _gx_rich_text_view_single_line_draw(GX_RICH_TEXT_VIEW *text_view, GX_VALUE ypos, GX_RICH_TEXT_LINE_INFO *line_info)
85 {
86 GX_STRING           text = line_info -> gx_rich_text_line_info_text;
87 GX_STRING           draw_text;
88 GX_UBYTE            processed_count;
89 GX_FONT            *font;
90 GX_BOOL             draw_start = GX_FALSE;
91 GX_VALUE            text_width;
92 GX_VALUE            xpos;
93 GX_VALUE            y;
94 GX_RICH_TEXT_FORMAT start_format;
95 GX_RICH_TEXT_FORMAT format = line_info -> gx_rich_text_line_info_start_format;
96 GX_RECTANGLE        client = text_view -> gx_window_client;
97 GX_VALUE            client_width;
98 GX_RECTANGLE        draw_area;
99 GX_BOOL             escape = GX_FALSE;
100 
101     /* Offset client area by the size of whitespace.  */
102     _gx_utility_rectangle_resize(&client, (GX_VALUE)(-text_view -> gx_multi_line_text_view_whitespace));
103 
104     /* Calculate client width. */
105     client_width = (GX_VALUE)(client.gx_rectangle_right - client.gx_rectangle_left);
106 
107     /* Process proceding styles. */
108     while (text.gx_string_ptr[0] == '<')
109     {
110         if ((_gx_rich_text_view_tag_enter(text_view, &text, &format, &processed_count) == GX_SUCCESS))
111         {
112             text.gx_string_ptr += processed_count;
113             text.gx_string_length -= processed_count;
114         }
115         else
116         {
117             break;
118         }
119     }
120 
121     start_format = format;
122 
123     /* Calculate draw start x coordinate. */
124     switch (format.gx_rich_text_flags & GX_RICH_TEXT_ALIGN_MASK)
125     {
126     case GX_RICH_TEXT_RIGHT:
127         xpos = (GX_VALUE)(client.gx_rectangle_right - 1);
128         xpos = (GX_VALUE)(xpos - (INT)line_info -> gx_rich_text_line_info_text_width);
129         break;
130 
131     case GX_RICH_TEXT_CENTER:
132         xpos = (GX_VALUE)((client_width - (INT)line_info -> gx_rich_text_line_info_text_width) / 2);
133         xpos = (GX_VALUE)(xpos + client.gx_rectangle_left);
134         break;
135 
136     case GX_RICH_TEXT_LEFT:
137     default:
138         xpos = (GX_VALUE)(client.gx_rectangle_left + 1);
139         break;
140     }
141 
142     draw_text.gx_string_ptr = text.gx_string_ptr;
143     draw_text.gx_string_length = 0;
144 
145     /* Calculate the total rows of text view string. */
146     while (text.gx_string_length > 0)
147     {
148         processed_count = 1;
149 
150         if ((!escape) && (text.gx_string_ptr[0] == '\\'))
151         {
152             escape = GX_TRUE;
153 
154             if (draw_text.gx_string_length == 0)
155             {
156                 draw_text.gx_string_ptr++;
157             }
158             else
159             {
160                 draw_start = GX_TRUE;
161             }
162         }
163         else
164         {
165             /* Test tags. */
166             if ((!escape) &&
167                 (text.gx_string_ptr[0] == '<') &&
168                 (_gx_rich_text_view_tag_enter(text_view, &text, &format, &processed_count) == GX_SUCCESS))
169             {
170                 if (draw_text.gx_string_length == 0)
171                 {
172                     /* Skip tags. */
173                     draw_text.gx_string_ptr += processed_count;
174 
175                     /* Set text draw style to current draw style. */
176                     start_format = format;
177                 }
178                 else
179                 {
180                     /* Draw text before current tag. */
181                     draw_start = GX_TRUE;
182                 }
183             }
184             else
185             {
186                 draw_text.gx_string_length += processed_count;
187             }
188 
189             escape = GX_FALSE;
190         }
191 
192         text.gx_string_ptr += processed_count;
193         text.gx_string_length -= processed_count;
194 
195         /* If we come the the last character, or draw_text falg is true, process drawing. */
196         if ((text.gx_string_length == 0) || draw_start)
197         {
198             /* Pick up text font. */
199             _gx_context_font_get(start_format.gx_rich_text_font_id, &font);
200 
201             /* Pict up text width. */
202             _gx_system_string_width_get_ext(font, &draw_text, &text_width);
203 
204             /* Set text color. */
205             _gx_context_line_color_set(start_format.gx_rich_text_color);
206 
207             /* Set font. */
208             _gx_context_font_set(start_format.gx_rich_text_font_id);
209 
210             if (start_format.gx_rich_text_highlight_color != text_view -> gx_widget_normal_fill_color)
211             {
212                 _gx_context_brush_width_set(0);
213 
214                 /* Draw highlight background. */
215                 _gx_context_fill_color_set(start_format.gx_rich_text_highlight_color);
216 
217                 draw_area.gx_rectangle_left = (GX_VALUE)xpos;
218                 draw_area.gx_rectangle_right = (GX_VALUE)(xpos + text_width - 1);
219                 draw_area.gx_rectangle_top = (GX_VALUE)(ypos - (text_view -> gx_multi_line_text_view_line_space >> 1));
220                 draw_area.gx_rectangle_bottom = (GX_VALUE)(draw_area.gx_rectangle_top + line_info -> gx_rich_text_line_info_line_height - 1);
221 
222                 _gx_canvas_rectangle_draw(&draw_area);
223             }
224 
225             y = (GX_VALUE)(ypos + line_info -> gx_rich_text_line_info_baseline - font -> gx_font_baseline);
226 
227             /* Draw text. */
228             _gx_canvas_text_draw_ext(xpos, y, &draw_text);
229 
230             /* Test italic flag is valid. */
231             if (start_format.gx_rich_text_flags & GX_RICH_TEXT_UNDERLINE)
232             {
233                 y = (GX_VALUE)(ypos + line_info -> gx_rich_text_line_info_baseline + 1);
234 
235                 _gx_context_brush_width_set(1);
236 
237                 /* Draw underline. */
238                 _gx_canvas_line_draw(xpos, y, (GX_VALUE)(xpos + text_width - 1), y);
239             }
240 
241             /* Advance draw start x position by text width. */
242             xpos = (GX_VALUE)(xpos + text_width);
243 
244             draw_text.gx_string_ptr = text.gx_string_ptr;
245             draw_text.gx_string_length = 0;
246 
247             /* Set draw text flag to false. */
248             draw_start = GX_FALSE;
249             start_format = format;
250         }
251     }
252 
253     line_info -> gx_rich_text_line_info_end_format = format;
254 }
255 
256 
257 /**************************************************************************/
258 /*                                                                        */
259 /*  FUNCTION                                               RELEASE        */
260 /*                                                                        */
261 /*    _gx_rich_text_view_text_draw                        PORTABLE C      */
262 /*                                                           6.1          */
263 /*  AUTHOR                                                                */
264 /*                                                                        */
265 /*    Kenneth Maxwell, Microsoft Corporation                              */
266 /*                                                                        */
267 /*  DESCRIPTION                                                           */
268 /*                                                                        */
269 /*    This function draws text for a rich text view widget.               */
270 /*                                                                        */
271 /*  INPUT                                                                 */
272 /*                                                                        */
273 /*    text_view                              Rich text view control block */
274 /*                                                                        */
275 /*  OUTPUT                                                                */
276 /*                                                                        */
277 /*    None                                                                */
278 /*                                                                        */
279 /*  CALLS                                                                 */
280 /*                                                                        */
281 /*    _gx_window_scrollbar_find             Find scroll bar for specified */
282 /*                                          window.                       */
283 /*    _gx_rich_text_view_text_total_height_calculate                      */
284 /*                                          Calculate text total height   */
285 /*    _gx_scrollbar_reset                   Reset scrollbar               */
286 /*    _gx_utility_rectangle_resize          Resize rectangle              */
287 /*    _gx_widget_string_get_ext             Retrieve text by id           */
288 /*    _gx_widget_font_get                   Retireve font by id           */
289 /*    _gx_system_private_string_get         Retreive string pointer       */
290 /*    _gx_system_rich_text_format_stack_clear                             */
291 /*                                          Clear rich text format stack  */
292 /*    _gx_system_rich_text_format_stack_switch                            */
293 /*                                          Switch rich text format stack */
294 /*    _gx_rich_text_view_line_info_get      Get one line text for drawing */
295 /*    _gx_rich_text_view_single_line_text_draw                            */
296 /*                                          Draw a single line            */
297 /*                                                                        */
298 /*  CALLED BY                                                             */
299 /*                                                                        */
300 /*    GUIX Internal Code                                                  */
301 /*    Application Code                                                    */
302 /*                                                                        */
303 /*  RELEASE HISTORY                                                       */
304 /*                                                                        */
305 /*    DATE              NAME                      DESCRIPTION             */
306 /*                                                                        */
307 /*  09-30-2020     Kenneth Maxwell          Initial Version 6.1           */
308 /*                                                                        */
309 /**************************************************************************/
_gx_rich_text_view_text_draw(GX_RICH_TEXT_VIEW * text_view)310 VOID  _gx_rich_text_view_text_draw(GX_RICH_TEXT_VIEW *text_view)
311 {
312 GX_STRING              text;
313 GX_FONT               *font;
314 GX_SCROLLBAR          *scroll;
315 GX_RICH_TEXT_LINE_INFO line_info;
316 GX_RICH_TEXT_FORMAT    format;
317 GX_VALUE               client_width;
318 GX_RECTANGLE           client;
319 GX_VALUE               ypos;
320 GX_CANVAS             *canvas;
321 GX_RECTANGLE           draw_area;
322 
323     if (!text_view -> gx_multi_line_text_view_text.gx_string_length)
324     {
325         /* Nothing to draw. */
326         return;
327     }
328 
329     if (text_view -> gx_multi_line_text_view_line_index_old)
330     {
331 
332         /* Rich text layout changes, we might need to reset scrollbar. */
333         _gx_window_scrollbar_find((GX_WINDOW *)text_view, GX_TYPE_VERTICAL_SCROLL, &scroll);
334 
335         if (scroll)
336         {
337             /* Update rich text view text total height.  */
338             _gx_rich_text_view_text_total_height_calculate(text_view);
339 
340             /* Reset scrollbar.  */
341             _gx_scrollbar_reset(scroll, GX_NULL);
342         }
343     }
344 
345     /* Pick up client retangle. */
346     client = text_view -> gx_window_client;
347 
348     /* Offset client area by the size of whitespace.  */
349     _gx_utility_rectangle_resize(&client, (GX_VALUE)(-text_view -> gx_multi_line_text_view_whitespace));
350 
351     /* Calculate text display width. */
352     client_width = (GX_VALUE)(client.gx_rectangle_right - client.gx_rectangle_left - 1);
353 
354     /* Set default draw style. */
355     format.gx_rich_text_color = text_view -> gx_multi_line_text_view_normal_text_color;
356     format.gx_rich_text_highlight_color = text_view -> gx_widget_normal_fill_color;
357     format.gx_rich_text_font_id = text_view -> gx_rich_text_view_fonts.gx_rich_text_fonts_normal_id;
358     format.gx_rich_text_flags = 0;
359 
360     switch (text_view -> gx_widget_style & GX_STYLE_TEXT_ALIGNMENT_MASK)
361     {
362     case GX_STYLE_TEXT_CENTER:
363         format.gx_rich_text_flags |= GX_RICH_TEXT_CENTER;
364         break;
365 
366     case GX_STYLE_TEXT_RIGHT:
367         format.gx_rich_text_flags |= GX_RICH_TEXT_RIGHT;
368         break;
369 
370     default:
371         /* Do nothing. */
372         break;
373     }
374 
375     /* Pick up text for drawing. */
376     if (text_view -> gx_multi_line_text_view_text_id)
377     {
378         _gx_widget_string_get_ext((GX_WIDGET *)text_view, text_view -> gx_multi_line_text_view_text_id, &text);
379     }
380     else
381     {
382         _gx_system_private_string_get(&text_view -> gx_multi_line_text_view_text, &text, text_view -> gx_widget_style);
383     }
384 
385     ypos = client.gx_rectangle_top;
386     ypos = (GX_VALUE)(ypos + text_view -> gx_multi_line_text_view_text_scroll_shift);
387     ypos = (GX_VALUE)(ypos + (text_view -> gx_multi_line_text_view_line_space >> 1));
388 
389     /* pick up current canvas */
390     canvas = _gx_system_current_draw_context -> gx_draw_context_canvas;
391 
392     _gx_utility_rectangle_overlap_detect(&_gx_system_current_draw_context -> gx_draw_context_dirty, &client, &draw_area);
393     _gx_canvas_drawing_initiate(canvas, (GX_WIDGET *)text_view, &draw_area);
394 
395     /* Calculate the total rows of text view string. */
396     while (text.gx_string_length > 0)
397     {
398         /* Pickup draw font. */
399         _gx_widget_font_get((GX_WIDGET *)text_view, format.gx_rich_text_font_id, &font);
400 
401         if (!font)
402         {
403             break;
404         }
405 
406         line_info.gx_rich_text_line_info_text.gx_string_ptr = text.gx_string_ptr;
407         line_info.gx_rich_text_line_info_text.gx_string_length = 0;
408         line_info.gx_rich_text_line_info_start_format = format;
409         line_info.gx_rich_text_line_info_end_format = format;
410         line_info.gx_rich_text_line_info_line_height = font -> gx_font_line_height;
411         line_info.gx_rich_text_line_info_baseline = font -> gx_font_baseline;
412         line_info.gx_rich_text_line_info_text_width = 0;
413 
414         _gx_rich_text_view_context_save();
415 
416         if (_gx_rich_text_view_line_info_get(text_view, text, &line_info, client_width) != GX_SUCCESS)
417         {
418             break;
419         }
420 
421         format = line_info.gx_rich_text_line_info_end_format;
422 
423         /* Draw text. */
424         if ((GX_VALUE)(ypos + line_info.gx_rich_text_line_info_line_height) > client.gx_rectangle_top)
425         {
426             if ((GX_VALUE)(ypos) < client.gx_rectangle_bottom)
427             {
428                 _gx_rich_text_view_context_restore();
429                 _gx_rich_text_view_single_line_draw(text_view, ypos, &line_info);
430             }
431             else
432             {
433                 break;
434             }
435         }
436 
437         ypos = (GX_VALUE)(ypos + line_info.gx_rich_text_line_info_line_height);
438         ypos = (GX_VALUE)(ypos + text_view -> gx_multi_line_text_view_line_space);
439 
440         text.gx_string_ptr += line_info.gx_rich_text_line_info_text.gx_string_length;
441         text.gx_string_length -= line_info.gx_rich_text_line_info_text.gx_string_length;
442     }
443     _gx_canvas_drawing_complete(canvas, GX_FALSE);
444 
445     _gx_rich_text_view_context_reset();
446 }
447 
448