GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: gx_multi_line_text_view_text_draw.c Lines: 83 85 97.6 %
Date: 2026-08-28 02:37:13 Branches: 42 45 93.3 %

Line Branch Exec Source
1
/***************************************************************************
2
 * Copyright (c) 2024 Microsoft Corporation
3
 * Copyright (c) 2026 Eclipse ThreadX contributors
4
 *
5
 * This program and the accompanying materials are made available under the
6
 * terms of the MIT License which is available at
7
 * https://opensource.org/licenses/MIT.
8
 *
9
 * SPDX-License-Identifier: MIT
10
 **************************************************************************/
11
// Some portions generated by Copilot (Claude Sonnet 4.6).
12
// Some portions generated by Claude Code (Opus 5).
13
14
15
/**************************************************************************/
16
/**************************************************************************/
17
/**                                                                       */
18
/** GUIX Component                                                        */
19
/**                                                                       */
20
/**   Multi Line Text View Management (Multi Line Text View)              */
21
/**                                                                       */
22
/**************************************************************************/
23
24
#define GX_SOURCE_CODE
25
26
27
/* Include necessary system files.  */
28
29
#include "gx_api.h"
30
#include "gx_system.h"
31
#include "gx_canvas.h"
32
#include "gx_context.h"
33
#include "gx_multi_line_text_view.h"
34
#include "gx_utility.h"
35
#include "gx_widget.h"
36
#include "gx_window.h"
37
#include "gx_scrollbar.h"
38
39
/**************************************************************************/
40
/*                                                                        */
41
/*  FUNCTION                                               RELEASE        */
42
/*                                                                        */
43
/*    _gx_multi_line_text_view_draw                       PORTABLE C      */
44
/*                                                           6.1          */
45
/*  AUTHOR                                                                */
46
/*                                                                        */
47
/*    Kenneth Maxwell, Microsoft Corporation                              */
48
/*                                                                        */
49
/*  DESCRIPTION                                                           */
50
/*                                                                        */
51
/*    This function draws text for a multi-line-text-view widget.         */
52
/*                                                                        */
53
/*  INPUT                                                                 */
54
/*                                                                        */
55
/*    text_view                              Multi-line_text_view widget  */
56
/*                                             control block              */
57
/*    text_color                             ID of text color             */
58
/*                                                                        */
59
/*  OUTPUT                                                                */
60
/*                                                                        */
61
/*    None                                                                */
62
/*                                                                        */
63
/*  CALLS                                                                 */
64
/*                                                                        */
65
/*    _gx_context_line_color_set            Set the context color         */
66
/*    _gx_context_font_get                  Get font associated with the  */
67
/*                                            specified ID                */
68
/*    _gx_context_font_set                  Set the context font          */
69
/*    _gx_context_brush_width_set           Set the brush width           */
70
/*    _gx_multi_line_text_view_visible_rows_compute                       */
71
/*                                          Calculate visible rows        */
72
/*    _gx_multi_line_text_view_string_total_rows_compute                  */
73
/*                                          Calculate total rows for input*/
74
/*                                            string                      */
75
/*    _gx_utility_rectangle_resize          Offset rectangle by specified */
76
/*                                            value                       */
77
/*    _gx_utility_rectangle_overlap_detect  Detect rectangle overlaps     */
78
/*    _gx_utility_string_length_check       Test string length            */
79
/*    _gx_system_string_get                 Get string by specified id    */
80
/*    _gx_system_private_string_get         Get string pointer in         */
81
/*                                            dynamically copied string   */
82
/*                                            buffer                      */
83
/*    _gx_multi_line_text_view_paragraph_start_get                        */
84
/*                                          Get start index of a paragraph*/
85
/*    _gx_utility_bidi_paragraph_reorder    Reorder bidi text for display */
86
/*    _gx_system_string_width_get           Get string width              */
87
/*    _gx_canvas_text_draw                  Draw the text                 */
88
/*    _gx_canvas_drawing_initiate           Initiate a drawing context    */
89
/*    _gx_canvas_drawing_complete           Complete a drawing            */
90
/*                                                                        */
91
/*  CALLED BY                                                             */
92
/*                                                                        */
93
/*    Application Code                                                    */
94
/*    GUIX Internal Code                                                  */
95
/*                                                                        */
96
/**************************************************************************/
97
24141
VOID  _gx_multi_line_text_view_text_draw(GX_MULTI_LINE_TEXT_VIEW *text_view, GX_RESOURCE_ID text_color)
98
{
99
INT           index;
100
INT           line_height;
101
GX_STRING     string;
102
GX_STRING     line_string;
103
INT           x_pos;
104
INT           y_pos;
105
GX_RECTANGLE  client;
106
GX_RECTANGLE  draw_area;
107
GX_CANVAS    *canvas;
108
INT           first_visible_line;
109
INT           last_visible_line;
110
UINT          line_start_index;
111
UINT          line_end_index;
112
UINT          line_cache_start;
113
GX_VALUE      text_width;
114
GX_VALUE      space_width;
115
GX_VALUE      client_width;
116
GX_FONT      *font;
117
GX_SCROLLBAR *scroll;
118
UINT          status;
119
GX_DRAW_CONTEXT *context;
120
GX_RECTANGLE     saved_dirty;
121
122
#if defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT)
123
GX_BIDI_RESOLVED_TEXT_INFO *next = GX_NULL;
124
UINT                        bidi_text_line_index = 0;
125
#endif
126
127
24141
    _gx_context_line_color_set(text_color);
128
24141
    _gx_context_font_get(text_view -> gx_multi_line_text_view_font_id, &font);
129
24141
    _gx_context_font_set(text_view -> gx_multi_line_text_view_font_id);
130
24141
    _gx_context_brush_width_set(1);
131
132
24141
    _gx_window_scrollbar_find((GX_WINDOW *)text_view, GX_TYPE_VERTICAL_SCROLL, &scroll);
133
134
24141
    if (text_view -> gx_multi_line_text_view_line_index_old)
135
    {
136
137
        /* Get visible rows. */
138
565
        _gx_multi_line_text_view_visible_rows_compute(text_view);
139
140
        /* Calculate text total rows. */
141
565
        _gx_multi_line_text_view_string_total_rows_compute(text_view);
142
143
565
        if (scroll)
144
        {
145
            /* Reset scrollbar.  */
146
90
            _gx_scrollbar_reset(scroll, GX_NULL);
147
        }
148
        else
149
        {
150
475
            if (text_view -> gx_multi_line_text_view_text_total_rows >
151
475
                text_view -> gx_multi_line_text_view_cache_size)
152
            {
153
                /* Update line cache. */
154
4
                _gx_multi_line_text_view_line_cache_update(text_view);
155
            }
156
        }
157
    }
158
159
    /* Is there a string and font?  */
160
24141
    if ((text_view -> gx_multi_line_text_view_text.gx_string_length <= 0)  ||
161
18593
        font == GX_NULL)
162
    {
163
5588
        return;
164
    }
165
166
    /* Pickup text height. */
167
18571
    line_height = font -> gx_font_line_height + text_view -> gx_multi_line_text_view_line_space;
168
169
18571
    if (!line_height)
170
    {
171
18
        return;
172
    }
173
174
    /* pick up current canvas */
175
18553
    canvas = _gx_system_current_draw_context -> gx_draw_context_canvas;
176
177
    /* Pick up client area.  */
178
18553
    client = text_view -> gx_window_client;
179
180
    /* Offset client area by the size of whitespace.  */
181
18553
    _gx_utility_rectangle_resize(&client, (GX_VALUE)(-text_view -> gx_multi_line_text_view_whitespace));
182
183
    /* check for auto-scrolling vertically centered text */
184
18553
    if (text_view -> gx_widget_type == GX_TYPE_MULTI_LINE_TEXT_VIEW &&
185
9062
        scroll == GX_NULL &&
186
8246
        (text_view -> gx_widget_style & GX_STYLE_VALIGN_CENTER) == GX_STYLE_VALIGN_CENTER)
187
    {
188
4
        space_width = (GX_VALUE)(client.gx_rectangle_bottom - client.gx_rectangle_top);
189
4
        space_width = (GX_VALUE)((INT)space_width - (INT)((INT)text_view -> gx_multi_line_text_view_text_total_rows * line_height));
190
4
        text_view -> gx_multi_line_text_view_text_scroll_shift = (space_width >> 1);
191
    }
192
193
    /* Remember the current context and its clipping rectangle, in case the draw
194
       context stack turns out to be full.  */
195
18553
    context = _gx_system_current_draw_context;
196
18553
    saved_dirty = context -> gx_draw_context_dirty;
197
198
18553
    _gx_utility_rectangle_overlap_detect(&saved_dirty, &client, &draw_area);
199
18553
    status = _gx_canvas_drawing_initiate(canvas, (GX_WIDGET *)text_view, &draw_area);
200
201
18553
    if (status == GX_DRAW_NESTING_EXCEEDED)
202
    {
203
        /* The context stack is full, so no context was pushed. The context that
204
           would have been pushed is created for this same widget, so it differs
205
           from the current one only in its clipping rectangle. Narrow the current
206
           context's clipping rectangle instead and draw through it, rather than
207
           dropping the text. The rectangle is restored once the text is drawn.  */
208
2
        context -> gx_draw_context_dirty = draw_area;
209
    }
210
211

18553
    if ((status == GX_SUCCESS) || (status == GX_DRAW_NESTING_EXCEEDED))
212
    {
213
        /* Compute the start displaying position of pixels in x direction and y direction. */
214
18553
        y_pos = client.gx_rectangle_top;
215
18553
        y_pos += text_view -> gx_multi_line_text_view_text_scroll_shift;
216
18553
        y_pos += (text_view -> gx_multi_line_text_view_line_space >> 1);
217
218
18553
        line_string.gx_string_ptr = " ";
219
18553
        line_string.gx_string_length = 1;
220
221
18553
        _gx_system_string_width_get_ext(font, &line_string, &space_width);
222
223
18553
        first_visible_line = ((-text_view -> gx_multi_line_text_view_text_scroll_shift)) / line_height;
224
225
18553
        if (first_visible_line < 0)
226
        {
227
4
            first_visible_line = 0;
228
        }
229
230
18553
        last_visible_line = first_visible_line + (INT)(text_view -> gx_multi_line_text_view_text_visible_rows);
231
232
18553
        if (last_visible_line > (INT)(text_view -> gx_multi_line_text_view_text_total_rows - 1))
233
        {
234
14593
            last_visible_line = (INT)(text_view -> gx_multi_line_text_view_text_total_rows - 1);
235
        }
236
237
18553
        if (text_view -> gx_multi_line_text_view_text_id)
238
        {
239
2866
            _gx_widget_string_get_ext((GX_WIDGET *)text_view, text_view -> gx_multi_line_text_view_text_id, &string);
240
        }
241
        else
242
        {
243
15687
            _gx_system_private_string_get(&text_view -> gx_multi_line_text_view_text, &string, text_view -> gx_widget_style);
244
        }
245
246
18553
        y_pos += (INT)(first_visible_line * line_height);
247
248
115511
        for (index = first_visible_line; index <= last_visible_line; index++)
249
        {
250
#if defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT)
251
            if (_gx_system_bidi_text_enabled)
252
            {
253
                line_string.gx_string_ptr = GX_NULL;
254
                line_string.gx_string_length = 0;
255
256
                if (!next)
257
                {
258
                    next = text_view -> gx_multi_line_text_view_bidi_resolved_text_info;
259
                }
260
261
                while (next)
262
                {
263
                    if (bidi_text_line_index + next -> gx_bidi_resolved_text_info_total_lines > (UINT)index)
264
                    {
265
                        /* Get line string. */
266
                        if (next -> gx_bidi_resolved_text_info_text)
267
                        {
268
                            line_string = next -> gx_bidi_resolved_text_info_text[(UINT)index - bidi_text_line_index];
269
                        }
270
                        break;
271
                    }
272
273
                    bidi_text_line_index += next -> gx_bidi_resolved_text_info_total_lines;
274
                    next = next -> gx_bidi_resolved_text_info_next;
275
                }
276
            }
277
            else
278
            {
279
#endif /* defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT) */
280
96958
                line_cache_start = text_view -> gx_multi_line_text_view_first_cache_line;
281
96958
                line_start_index = text_view -> gx_multi_line_text_view_line_index[index - (INT)line_cache_start];
282
283
96958
                if ((INT)(index - (INT)line_cache_start) >= (INT)(text_view -> gx_multi_line_text_view_cache_size - 1))
284
                {
285
15124
                    line_end_index = text_view -> gx_multi_line_text_view_text.gx_string_length;
286
                }
287
                else
288
                {
289
81834
                    line_end_index = text_view -> gx_multi_line_text_view_line_index[index - (INT)(line_cache_start) + 1];
290
                }
291
292
                /* Get line string. */
293
96958
                line_string.gx_string_ptr = string.gx_string_ptr + line_start_index;
294
96958
                line_string.gx_string_length = line_end_index - line_start_index;
295
#if defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT)
296
            }
297
#endif /* defined(GX_DYNAMIC_BIDI_TEXT_SUPPORT) */
298
299
300
96958
            switch (text_view -> gx_widget_style & GX_STYLE_TEXT_ALIGNMENT_MASK)
301
            {
302
12114
            case GX_STYLE_TEXT_RIGHT:
303
12114
                _gx_system_string_width_get_ext(font, &line_string, &text_width);
304
13042
                while (text_width > (client.gx_rectangle_right - client.gx_rectangle_left - 2))
305
                {
306
928
                    text_width = (GX_VALUE)(text_width - space_width);
307
                }
308
12114
                x_pos = client.gx_rectangle_right - 1;
309
12114
                x_pos = (GX_VALUE)(x_pos - text_width);
310
12114
                break;
311
71511
            case GX_STYLE_TEXT_LEFT:
312
71511
                x_pos = client.gx_rectangle_left + 1;
313
71511
                break;
314
13333
            case GX_STYLE_TEXT_CENTER:
315
            default:
316
13333
                _gx_system_string_width_get_ext(font, &line_string, &text_width);
317
13333
                client_width = (GX_VALUE)(client.gx_rectangle_right - client.gx_rectangle_left + 1);
318
14173
                while (text_width > (client_width - 3))
319
                {
320
840
                    text_width = (GX_VALUE)(text_width - space_width);
321
                }
322
13333
                x_pos = (GX_VALUE)(client.gx_rectangle_left + ((client_width - text_width) / 2));
323
13333
                break;
324
            }
325
326
            /* Draw the text. */
327
96958
            _gx_canvas_text_draw_ext((GX_VALUE)x_pos, (GX_VALUE)y_pos, &line_string);
328
96958
            y_pos += line_height;
329
        }
330
331
18553
        if (status == GX_SUCCESS)
332
        {
333
18551
            _gx_canvas_drawing_complete(canvas, GX_FALSE);
334
        }
335
        else
336
        {
337
            /* Nothing was pushed, so _gx_canvas_drawing_complete() must not be
338
               called: it would pop the caller's context. Restore the clipping
339
               rectangle borrowed above instead.  */
340
2
            context -> gx_draw_context_dirty = saved_dirty;
341
        }
342
    }
343
    else if (status == GX_NO_VIEWS)
344
    {
345
        _gx_canvas_drawing_complete(canvas, GX_FALSE);
346
    }
347
    else
348
    {
349
        /* Any other status, such as GX_INVALID_MEMORY_SIZE when drawing into a
350
           partial frame buffer, is also returned before a context is pushed, so
351
           there is nothing to draw and nothing to complete.  */
352
    }
353
}
354