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_widget.h"
29 #include "gx_window.h"
30 #include "gx_scrollbar.h"
31 #include "gx_utility.h"
32 #include "gx_multi_line_text_view.h"
33 #include "gx_multi_line_text_input.h"
34 
35 /**************************************************************************/
36 /*                                                                        */
37 /*  FUNCTION                                               RELEASE        */
38 /*                                                                        */
39 /*    _gx_multi_line_text_input_cursor_pos_update         PORTABLE C      */
40 /*                                                           6.1          */
41 /*  AUTHOR                                                                */
42 /*                                                                        */
43 /*    Kenneth Maxwell, Microsoft Corporation                              */
44 /*                                                                        */
45 /*  DESCRIPTION                                                           */
46 /*                                                                        */
47 /*    This function gets the position of the cursor according to insert   */
48 /*      index.                                                            */
49 /*                                                                        */
50 /*  INPUT                                                                 */
51 /*                                                                        */
52 /*    text_input                            Multi line text input         */
53 /*                                            control block               */
54 /*                                                                        */
55 /*  OUTPUT                                                                */
56 /*                                                                        */
57 /*    None                                                                */
58 /*                                                                        */
59 /*  CALLS                                                                 */
60 /*                                                                        */
61 /*    _gx_widget_font_get                   Retrieve font                 */
62 /*    _gx_window_scrollbar_find             Find scrollbar for a window   */
63 /*    _gx_scrollbar_reset                   Reset scrollbar information   */
64 /*    _gx_system_string_width_get           Get the width of a string     */
65 /*    _gx_utility_rectangle_resize          Increase/Shrink rectangle by  */
66 /*                                            specified value             */
67 /*                                                                        */
68 /*  CALLED BY                                                             */
69 /*                                                                        */
70 /*    GUIX Intercal Code                                                  */
71 /*                                                                        */
72 /*  RELEASE HISTORY                                                       */
73 /*                                                                        */
74 /*    DATE              NAME                      DESCRIPTION             */
75 /*                                                                        */
76 /*  05-19-2020     Kenneth Maxwell          Initial Version 6.0           */
77 /*  09-30-2020     Kenneth Maxwell          Modified comment(s),          */
78 /*                                            resulting in version 6.1    */
79 /*                                                                        */
80 /**************************************************************************/
81 
_gx_multi_line_text_input_cursor_pos_update(GX_MULTI_LINE_TEXT_INPUT * text_input,GX_BOOL make_cursor_visible)82 UINT _gx_multi_line_text_input_cursor_pos_update(GX_MULTI_LINE_TEXT_INPUT *text_input, GX_BOOL make_cursor_visible)
83 {
84 GX_TEXT_INPUT_CURSOR    *cursor_ptr = &text_input -> gx_multi_line_text_input_cursor_instance;
85 GX_MULTI_LINE_TEXT_VIEW *view = (GX_MULTI_LINE_TEXT_VIEW *)text_input;
86 GX_FONT                 *font;
87 GX_VALUE                 line_height;
88 UINT                     insert_pos;
89 UINT                     cur_line;
90 USHORT                   cache_index;
91 GX_CHAR                 *string_buffer;
92 GX_POINT                 cur_pos;
93 GX_RECTANGLE             client;
94 INT                      y_pos;
95 GX_VALUE                 text_width;
96 GX_VALUE                 space_width;
97 GX_VALUE                 client_width;
98 UINT                     start_index;
99 UINT                     end_index;
100 GX_STRING                string;
101 GX_MULTI_LINE_TEXT_INFO  text_info;
102 
103     _gx_widget_font_get((GX_WIDGET *)text_input, text_input -> gx_multi_line_text_view_font_id, &font);
104 
105     if (!font)
106     {
107         return GX_FAILURE;
108     }
109 
110     line_height = (GX_VALUE)(font -> gx_font_line_height +
111                              text_input -> gx_multi_line_text_view_line_space);
112     text_width = 0;
113 
114     if (!line_height)
115     {
116         return GX_FAILURE;
117     }
118 
119     /* Get the y position where the text displayed from. */
120     client = text_input -> gx_window_client;
121 
122     if (text_input -> gx_multi_line_text_view_whitespace)
123     {
124         /* Offset client bounding box.  */
125         _gx_utility_rectangle_resize(&client, (GX_VALUE)(-text_input -> gx_multi_line_text_view_whitespace));
126     }
127 
128     client_width = (GX_VALUE)(client.gx_rectangle_right - client.gx_rectangle_left + 1);
129 
130     string_buffer = (GX_CHAR *)text_input -> gx_multi_line_text_view_text.gx_string_ptr;
131 
132     insert_pos = text_input -> gx_multi_line_text_input_text_insert_position;
133 
134     cache_index = 0;
135     cur_line = 0;
136 
137     if ((cache_index < view -> gx_multi_line_text_view_cache_size) &&
138         (view -> gx_multi_line_text_view_line_index[0] > insert_pos))
139     {
140         /* Insert position is before cached lines. Search from start. */
141         start_index = 0;
142         end_index = text_input -> gx_multi_line_text_view_text.gx_string_length;
143         cur_line = 1;
144 
145         while (1)
146         {
147             /* Calculate some information used to draw the text. */
148             _gx_multi_line_text_view_display_info_get((GX_MULTI_LINE_TEXT_VIEW *)text_input, start_index, end_index, &text_info, (GX_VALUE)(client_width - 2));
149 
150             start_index += text_info.gx_text_display_number;
151 
152             if (start_index >= insert_pos)
153             {
154                 end_index = start_index;
155                 start_index -= text_info.gx_text_display_number;
156                 break;
157             }
158 
159             cur_line++;
160         }
161 
162         if (insert_pos)
163         {
164             if ((string_buffer[insert_pos - 1] == GX_KEY_CARRIAGE_RETURN) ||
165                 (string_buffer[insert_pos - 1] == GX_KEY_LINE_FEED))
166             {
167                 cur_line++;
168             }
169         }
170     }
171     else
172     {
173         /* Find the cursor line. */
174         while (cache_index < view -> gx_multi_line_text_view_cache_size)
175         {
176             if (view -> gx_multi_line_text_view_line_index[cache_index] >= insert_pos)
177             {
178                 break;
179             }
180             cache_index++;
181         }
182 
183         if (cache_index < view -> gx_multi_line_text_view_cache_size)
184         {
185             /* Found insert position. */
186             if (insert_pos &&
187                 (string_buffer[insert_pos - 1] != GX_KEY_CARRIAGE_RETURN) &&
188                 (string_buffer[insert_pos - 1] != GX_KEY_LINE_FEED) && cache_index)
189             {
190                 cache_index--;
191             }
192 
193             /* cursor line is started from 1. */
194             cur_line = cache_index + view -> gx_multi_line_text_view_first_cache_line + 1;
195 
196             /* get x coordinate of cursor postition */
197             start_index = text_input -> gx_multi_line_text_view_line_index[cache_index];
198 
199             if (cache_index >= view -> gx_multi_line_text_view_cache_size - 1)
200             {
201                 end_index = text_input -> gx_multi_line_text_view_text.gx_string_length;
202             }
203             else
204             {
205                 end_index = text_input -> gx_multi_line_text_view_line_index[cache_index + 1];
206             }
207         }
208         else if (view -> gx_multi_line_text_view_cache_size)
209         {
210             /* Insert position is after cached lines, search from last cached line. */
211             cache_index = (USHORT)(view -> gx_multi_line_text_view_cache_size - 1);
212             cur_line = cache_index + view -> gx_multi_line_text_view_first_cache_line + 1;
213             start_index = text_input -> gx_multi_line_text_view_line_index[cache_index];
214             end_index = text_input -> gx_multi_line_text_view_text.gx_string_length;
215 
216             while (start_index < end_index)
217             {
218                 /* Calculate some information used to draw the text. */
219                 _gx_multi_line_text_view_display_info_get((GX_MULTI_LINE_TEXT_VIEW *)text_input, start_index, end_index, &text_info, (GX_VALUE)(client_width - 2));
220 
221                 start_index += text_info.gx_text_display_number;
222 
223                 if (start_index >= insert_pos)
224                 {
225                     end_index = start_index;
226                     start_index -= text_info.gx_text_display_number;
227                     break;
228                 }
229 
230                 cur_line++;
231             }
232 
233             if ((string_buffer[insert_pos - 1] == GX_KEY_CARRIAGE_RETURN) ||
234                 (string_buffer[insert_pos - 1] == GX_KEY_LINE_FEED))
235             {
236                 cur_line++;
237             }
238         }
239         else
240         {
241             /* No character. */
242             start_index = 0;
243             end_index = 0;
244             cur_line = 1;
245         }
246     }
247 
248     string.gx_string_ptr = " ";
249     string.gx_string_length = 1;
250     _gx_system_string_width_get_ext(font, &string, &space_width);
251 
252     switch (text_input -> gx_widget_style & GX_STYLE_TEXT_ALIGNMENT_MASK)
253     {
254     case GX_STYLE_TEXT_RIGHT:
255         string.gx_string_ptr = (GX_CHAR *)text_input -> gx_multi_line_text_view_text.gx_string_ptr + start_index;
256         string.gx_string_length = end_index - start_index;
257         _gx_system_string_width_get_ext(font, &string, &text_width);
258         while (text_width > (client.gx_rectangle_right - client.gx_rectangle_left - 2))
259         {
260             text_width = (GX_VALUE)(text_width - space_width);
261         }
262         cur_pos.gx_point_x = (GX_VALUE)(client.gx_rectangle_right - 1);
263         cur_pos.gx_point_x = (GX_VALUE)(cur_pos.gx_point_x - text_width);
264         break;
265 
266     case GX_STYLE_TEXT_LEFT:
267         cur_pos.gx_point_x = (GX_VALUE)(client.gx_rectangle_left + 1);
268         break;
269 
270     case GX_STYLE_TEXT_CENTER:
271     default:
272         string.gx_string_ptr = (GX_CHAR *)text_input -> gx_multi_line_text_view_text.gx_string_ptr + start_index;
273         string.gx_string_length = end_index - start_index;
274         _gx_system_string_width_get_ext(font, &string, &text_width);
275         while (text_width > (client.gx_rectangle_right - client.gx_rectangle_left - 2))
276         {
277             text_width = (GX_VALUE)(text_width - space_width);
278         }
279 
280         cur_pos.gx_point_x = (GX_VALUE)(client.gx_rectangle_left + ((client_width - text_width) / 2));
281         break;
282     }
283 
284     if (insert_pos &&
285         (string_buffer[insert_pos - 1] != GX_KEY_CARRIAGE_RETURN) &&
286         (string_buffer[insert_pos - 1] != GX_KEY_LINE_FEED))
287     {
288         string.gx_string_ptr = string_buffer + start_index;
289         string.gx_string_length = insert_pos - start_index;
290         _gx_system_string_width_get_ext(font, &string, &text_width);
291         cur_pos.gx_point_x = (GX_VALUE)(cur_pos.gx_point_x + text_width);
292     }
293 
294     /* make cursor position inside client area. */
295     while (cur_pos.gx_point_x > client.gx_rectangle_right - 1)
296     {
297         cur_pos.gx_point_x = (GX_VALUE)(cur_pos.gx_point_x - space_width);
298     }
299 
300     /* Update cursor line. */
301     text_input -> gx_multi_line_text_input_text_cursor_line = cur_line;
302 
303     /* get y coordinate of cursor postition */
304     y_pos = (INT)client.gx_rectangle_top + text_input -> gx_multi_line_text_view_text_scroll_shift;
305 
306     cur_pos.gx_point_y = (GX_VALUE)(y_pos + (INT)(cur_line - 1) * line_height);
307     cur_pos.gx_point_y = (GX_VALUE)(cur_pos.gx_point_y + (line_height >> 1));
308 
309     cursor_ptr -> gx_text_input_cursor_pos = cur_pos;
310 
311     if (make_cursor_visible)
312     {
313         _gx_multi_line_text_input_cursor_visible(text_input);
314     }
315     return GX_SUCCESS;
316 }
317 
318