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