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 Input Management (Multi Line Text Input)            */
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_multi_line_text_view.h"
29 #include "gx_text_input_cursor.h"
30 #include "gx_multi_line_text_input.h"
31 #include "gx_context.h"
32 #include "gx_window.h"
33 #include "gx_widget.h"
34 #include "gx_utility.h"
35 #include "gx_canvas.h"
36 #include "gx_scrollbar.h"
37 
38 /**************************************************************************/
39 /*                                                                        */
40 /*  FUNCTION                                               RELEASE        */
41 /*                                                                        */
42 /*    _gx_multi_line_text_input_draw                      PORTABLE C      */
43 /*                                                           6.1          */
44 /*  AUTHOR                                                                */
45 /*                                                                        */
46 /*    Kenneth Maxwell, Microsoft Corporation                              */
47 /*                                                                        */
48 /*  DESCRIPTION                                                           */
49 /*                                                                        */
50 /*    This function draws the multi-line text input widget, which is a    */
51 /*    special type of widget.                                             */
52 /*                                                                        */
53 /*  INPUT                                                                 */
54 /*                                                                        */
55 /*    text_input                            Multi line text input         */
56 /*                                            control block               */
57 /*                                                                        */
58 /*  OUTPUT                                                                */
59 /*                                                                        */
60 /*    None                                                                */
61 /*                                                                        */
62 /*  CALLS                                                                 */
63 /*                                                                        */
64 /*    _gx_context_font_get                  Retrieve font                 */
65 /*    _gx_multi_line_text_view_draw         Draw the text view area       */
66 /*    _gx_text_input_cursor_draw            Draw the cursor               */
67 /*                                                                        */
68 /*  CALLED BY                                                             */
69 /*                                                                        */
70 /*    Application Code                                                    */
71 /*    GUIX Internal Code                                                  */
72 /*                                                                        */
73 /*  RELEASE HISTORY                                                       */
74 /*                                                                        */
75 /*    DATE              NAME                      DESCRIPTION             */
76 /*                                                                        */
77 /*  05-19-2020     Kenneth Maxwell          Initial Version 6.0           */
78 /*  09-30-2020     Kenneth Maxwell          Modified comment(s),          */
79 /*                                            resulting in version 6.1    */
80 /*                                                                        */
81 /**************************************************************************/
_gx_multi_line_text_input_draw(GX_MULTI_LINE_TEXT_INPUT * input)82 VOID  _gx_multi_line_text_input_draw(GX_MULTI_LINE_TEXT_INPUT *input)
83 {
84 GX_TEXT_INPUT_CURSOR *cursor_ptr = &input -> gx_multi_line_text_input_cursor_instance;
85 GX_VALUE              line_height = 0;
86 GX_FONT              *font;
87 GX_RESOURCE_ID        fill_color;
88 GX_RESOURCE_ID        text_color;
89 UINT                  line_start_mark;
90 UINT                  line_end_mark;
91 UINT                  end_mark;
92 
93 UINT                  index;
94 GX_STRING             private_string;
95 GX_STRING             draw_string;
96 INT                   x_pos;
97 INT                   y_pos;
98 GX_RECTANGLE          client;
99 GX_RECTANGLE          draw_area;
100 GX_CANVAS            *canvas;
101 UINT                  first_visible_line;
102 UINT                  last_visible_line;
103 UINT                  line_start_index;
104 UINT                  line_end_index;
105 UINT                  line_cache_start;
106 GX_VALUE              text_width;
107 GX_VALUE              client_width;
108 GX_SCROLLBAR         *scroll;
109 GX_VALUE              space_width;
110 
111     if (input -> gx_widget_style & GX_STYLE_ENABLED)
112     {
113         if (input -> gx_widget_style & GX_STYLE_TEXT_INPUT_READONLY)
114         {
115             fill_color = input -> gx_multi_line_text_input_readonly_fill_color;
116             text_color = input -> gx_multi_line_text_input_readonly_text_color;
117         }
118         else
119         {
120             fill_color = input -> gx_widget_normal_fill_color;
121             text_color = input -> gx_multi_line_text_view_normal_text_color;
122         }
123     }
124     else
125     {
126         fill_color = input -> gx_widget_disabled_fill_color;
127         text_color = input -> gx_multi_line_text_view_disabled_text_color;
128     }
129 
130     /* Draw background. */
131     _gx_window_border_draw((GX_WINDOW *)input, fill_color);
132 
133     if (input -> gx_multi_line_text_view_line_index_old)
134     {
135         /* Get visible rows. */
136         _gx_multi_line_text_view_visible_rows_compute((GX_MULTI_LINE_TEXT_VIEW *)input);
137 
138         /* Calculate text total rows. */
139         _gx_multi_line_text_view_string_total_rows_compute((GX_MULTI_LINE_TEXT_VIEW *)input);
140 
141         _gx_window_scrollbar_find((GX_WINDOW *)input, GX_TYPE_VERTICAL_SCROLL, &scroll);
142         if (scroll)
143         {
144             /* Reset scrollbar.  */
145             _gx_scrollbar_reset(scroll, GX_NULL);
146         }
147         else
148         {
149 
150             if (input -> gx_multi_line_text_view_text_total_rows >
151                 input -> gx_multi_line_text_view_cache_size)
152             {
153                 /* Update line cache. */
154                 _gx_multi_line_text_view_line_cache_update((GX_MULTI_LINE_TEXT_VIEW *)input);
155             }
156         }
157 
158         _gx_multi_line_text_input_cursor_pos_update(input, GX_TRUE);
159     }
160 
161     if ((input -> gx_multi_line_text_input_start_mark == input -> gx_multi_line_text_input_end_mark) ||
162         !(input -> gx_widget_style & GX_STYLE_ENABLED))
163     {
164         /* Draw text. */
165         _gx_multi_line_text_view_text_draw((GX_MULTI_LINE_TEXT_VIEW *)input, text_color);
166 
167         /* Draw the cursor. */
168         if ((input -> gx_widget_status & GX_STATUS_CURSOR_SHOW) &&
169             (input -> gx_widget_status & GX_STATUS_CURSOR_DRAW))
170         {
171             _gx_context_font_get(input -> gx_multi_line_text_view_font_id, &font);
172 
173             if (font)
174             {
175                 line_height = (GX_VALUE)(font -> gx_font_line_height +
176                                          input -> gx_multi_line_text_view_line_space);
177             }
178 
179             if (!(cursor_ptr -> gx_text_input_cursor_flags & GX_CURSOR_USE_CUSTOM_HEIGHT))
180             {
181                 cursor_ptr -> gx_text_input_cursor_height = line_height;
182             }
183 
184             _gx_text_input_cursor_draw(cursor_ptr);
185         }
186     }
187     else
188     {
189         line_start_mark = input -> gx_multi_line_text_input_start_mark;
190         end_mark = input -> gx_multi_line_text_input_end_mark;
191 
192         if (line_start_mark > end_mark)
193         {
194             GX_SWAP_VALS(line_start_mark, end_mark);
195         }
196 
197         _gx_context_font_get(input -> gx_multi_line_text_view_font_id, &font);
198         _gx_context_font_set(input -> gx_multi_line_text_view_font_id);
199         _gx_context_brush_width_set(0);
200 
201         /* Is there a string and font?  */
202         if ((input -> gx_multi_line_text_view_text.gx_string_length) && (font))
203         {
204             /* pick up current canvas */
205             canvas = _gx_system_current_draw_context -> gx_draw_context_canvas;
206 
207             /* Pick up client area.  */
208             client = input -> gx_window_client;
209 
210             /* Offset client area by the size of whitespace.  */
211             _gx_utility_rectangle_resize(&client, (GX_VALUE)(-input -> gx_multi_line_text_view_whitespace));
212 
213             _gx_utility_rectangle_overlap_detect(&_gx_system_current_draw_context -> gx_draw_context_dirty, &client, &draw_area);
214             _gx_canvas_drawing_initiate(canvas, (GX_WIDGET *)input, &draw_area);
215 
216             /* Pickup text height. */
217             line_height = (GX_VALUE)(font -> gx_font_line_height + input -> gx_multi_line_text_view_line_space);
218 
219             if (line_height)
220             {
221 
222                 first_visible_line = ((UINT)(-input -> gx_multi_line_text_view_text_scroll_shift)) / (UINT)line_height;
223                 last_visible_line = first_visible_line + input -> gx_multi_line_text_view_text_visible_rows;
224 
225                 if (last_visible_line > input -> gx_multi_line_text_view_text_total_rows - 1)
226                 {
227                     last_visible_line = input -> gx_multi_line_text_view_text_total_rows - 1;
228                 }
229 
230                 /* Compute the start displaying position of pixels in x direction and y direction. */
231                 y_pos = client.gx_rectangle_top;
232                 y_pos += input -> gx_multi_line_text_view_text_scroll_shift;
233                 y_pos += (input -> gx_multi_line_text_view_line_space >> 1);
234                 y_pos += (INT)first_visible_line * line_height;
235 
236                 _gx_system_private_string_get(&input -> gx_multi_line_text_view_text, &private_string, input -> gx_widget_style);
237 
238                 draw_string.gx_string_ptr = " ";
239                 draw_string.gx_string_length = 1;
240                 _gx_system_string_width_get_ext(font, &draw_string, &space_width);
241 
242                 for (index = first_visible_line; index <= last_visible_line; index++)
243                 {
244                     line_cache_start = input -> gx_multi_line_text_view_first_cache_line;
245 
246                     line_start_index = input -> gx_multi_line_text_view_line_index[index - line_cache_start];
247 
248                     if ((INT)(index - line_cache_start) >= (INT)(input -> gx_multi_line_text_view_cache_size - 1))
249                     {
250                         line_end_index = input -> gx_multi_line_text_view_text.gx_string_length;
251                     }
252                     else
253                     {
254                         line_end_index = input -> gx_multi_line_text_view_line_index[index - line_cache_start + 1];
255                     }
256 
257                     switch (input -> gx_widget_style & GX_STYLE_TEXT_ALIGNMENT_MASK)
258                     {
259                     case GX_STYLE_TEXT_RIGHT:
260                         draw_string.gx_string_ptr = private_string.gx_string_ptr + line_start_index;
261                         draw_string.gx_string_length = line_end_index - line_start_index;
262                         _gx_system_string_width_get_ext(font, &draw_string, &text_width);
263                         while (text_width > (client.gx_rectangle_right - client.gx_rectangle_left - 2))
264                         {
265                             text_width = (GX_VALUE)(text_width - space_width);
266                         }
267                         x_pos = client.gx_rectangle_right - 1;
268                         x_pos = (GX_VALUE)(x_pos - text_width);
269                         break;
270                     case GX_STYLE_TEXT_LEFT:
271                         x_pos = client.gx_rectangle_left + 1;
272                         break;
273                     case GX_STYLE_TEXT_CENTER:
274                     default:
275                         draw_string.gx_string_ptr = private_string.gx_string_ptr + line_start_index;
276                         draw_string.gx_string_length = line_end_index - line_start_index;
277                         _gx_system_string_width_get_ext(font, &draw_string, &text_width);
278                         client_width = (GX_VALUE)(client.gx_rectangle_right - client.gx_rectangle_left + 1);
279 
280                         while (text_width > (client_width - 3))
281                         {
282                             text_width = (GX_VALUE)(text_width - space_width);
283                         }
284 
285                         x_pos = (GX_VALUE)(client.gx_rectangle_left + ((client_width - text_width) / 2));
286                         break;
287                     }
288 
289                     if ((line_start_mark < line_end_index) && (end_mark > line_start_index))
290                     {
291                         if (line_start_mark < line_start_index)
292                         {
293                             line_start_mark = line_start_index;
294                         }
295 
296                         if (line_start_mark > line_start_index)
297                         {
298                             /* Draw text[line_start_index : start_mark - 1] with normal text color. */
299                             _gx_context_line_color_set(text_color);
300                             draw_string.gx_string_ptr = private_string.gx_string_ptr + line_start_index;
301                             draw_string.gx_string_length = line_start_mark - line_start_index;
302                             _gx_system_string_width_get_ext(font, &draw_string, &text_width);
303                             _gx_canvas_text_draw_ext((GX_VALUE)x_pos, (GX_VALUE)y_pos, &draw_string);
304                             x_pos += text_width;
305                         }
306 
307                         if (end_mark < line_end_index)
308                         {
309                             line_end_mark = end_mark;
310                         }
311                         else
312                         {
313                             line_end_mark = line_end_index;
314                         }
315 
316                         /* Draw text[start_mark: end_mark - 1] with hightlight text color. */
317                         _gx_context_line_color_set(input -> gx_multi_line_text_view_selected_text_color);
318                         _gx_context_fill_color_set(input -> gx_widget_selected_fill_color);
319 
320                         draw_string.gx_string_ptr = private_string.gx_string_ptr + line_start_mark;
321                         draw_string.gx_string_length = line_end_mark - line_start_mark;
322                         _gx_system_string_width_get_ext(font, &draw_string, &text_width);
323 
324                         draw_area.gx_rectangle_left = (GX_VALUE)x_pos;
325                         draw_area.gx_rectangle_right = (GX_VALUE)(x_pos + text_width - 1);
326                         draw_area.gx_rectangle_top = (GX_VALUE)(y_pos - (input -> gx_multi_line_text_view_line_space >> 1));
327                         draw_area.gx_rectangle_bottom = (GX_VALUE)(draw_area.gx_rectangle_top + line_height - 1);
328 
329                         while (draw_area.gx_rectangle_right > client.gx_rectangle_right - 1)
330                         {
331                             draw_area.gx_rectangle_right = (GX_VALUE)(draw_area.gx_rectangle_right - space_width);
332                         }
333 
334                         _gx_canvas_rectangle_draw(&draw_area);
335 
336                         draw_string.gx_string_ptr = private_string.gx_string_ptr + line_start_mark;
337                         draw_string.gx_string_length = line_end_mark - line_start_mark;
338                         _gx_canvas_text_draw_ext((GX_VALUE)x_pos, (GX_VALUE)y_pos, &draw_string);
339 
340                         x_pos += text_width;
341 
342                         /* Draw text[end_mark : line_end_index] width normal text color. */
343                         if (line_end_mark < line_end_index)
344                         {
345                             _gx_context_line_color_set(text_color);
346                             draw_string.gx_string_ptr = private_string.gx_string_ptr + line_end_mark;
347                             draw_string.gx_string_length = line_end_index - line_end_mark;
348                             _gx_canvas_text_draw_ext((GX_VALUE)x_pos, (GX_VALUE)y_pos, &draw_string);
349                         }
350 
351                         if (end_mark > line_end_index)
352                         {
353                             line_start_mark = line_end_index;
354                         }
355                     }
356                     else
357                     {
358                         _gx_context_line_color_set(text_color);
359 
360                         /* Draw the text. */
361                         draw_string.gx_string_ptr = private_string.gx_string_ptr + line_start_index;
362                         draw_string.gx_string_length = line_end_index - line_start_index;
363                         _gx_canvas_text_draw_ext((GX_VALUE)x_pos, (GX_VALUE)y_pos, &draw_string);
364                     }
365 
366                     y_pos += line_height;
367                 }
368             }
369 
370             _gx_canvas_drawing_complete(canvas, GX_FALSE);
371         }
372     }
373 
374     /* Draw widget's children.  */
375     _gx_widget_children_draw((GX_WIDGET *)input);
376 }
377 
378