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