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_utility.h"
29 #include "gx_scrollbar.h"
30 #include "gx_multi_line_text_input.h"
31 #include "gx_multi_line_text_view.h"
32 #include "gx_window.h"
33 #include "gx_widget.h"
34 
35 /**************************************************************************/
36 /*                                                                        */
37 /*  FUNCTION                                               RELEASE        */
38 /*                                                                        */
39 /*    _gx_multi_line_text_input_char_insert               PORTABLE C      */
40 /*                                                           6.1          */
41 /*  AUTHOR                                                                */
42 /*                                                                        */
43 /*    Kenneth Maxwell, Microsoft Corporation                              */
44 /*                                                                        */
45 /*  DESCRIPTION (deprecated)                                              */
46 /*                                                                        */
47 /*    This function inserts a character into the input buffer.            */
48 /*                                                                        */
49 /*  INPUT                                                                 */
50 /*                                                                        */
51 /*    text_input                            Multi line text input widget  */
52 /*                                            control block               */
53 /*    str                                   Utf8 string to be inserted    */
54 /*    str_size                              Inserted string size in bytes */
55 /*                                                                        */
56 /*  OUTPUT                                                                */
57 /*                                                                        */
58 /*    None                                                                */
59 /*                                                                        */
60 /*  CALLS                                                                 */
61 /*                                                                        */
62 /*    _gx_multi_line_text_input_char_insert_ext                           */
63 /*                                                                        */
64 /*  CALLED BY                                                             */
65 /*                                                                        */
66 /*    _gx_multi_line_text_input_keydown_process                           */
67 /*                                                                        */
68 /*  RELEASE HISTORY                                                       */
69 /*                                                                        */
70 /*    DATE              NAME                      DESCRIPTION             */
71 /*                                                                        */
72 /*  05-19-2020     Kenneth Maxwell          Initial Version 6.0           */
73 /*  09-30-2020     Kenneth Maxwell          Modified comment(s),          */
74 /*                                            resulting in version 6.1    */
75 /*                                                                        */
76 /**************************************************************************/
77 #if defined(GX_ENABLE_DEPRECATED_STRING_API)
_gx_multi_line_text_input_char_insert(GX_MULTI_LINE_TEXT_INPUT * text_input,GX_UBYTE * str,UINT str_size)78 UINT _gx_multi_line_text_input_char_insert(GX_MULTI_LINE_TEXT_INPUT *text_input, GX_UBYTE *str, UINT str_size)
79 {
80 UINT      status;
81 GX_STRING string;
82 
83     string.gx_string_ptr = (GX_CHAR *)str;
84     string.gx_string_length = str_size;
85     status = _gx_multi_line_text_input_char_insert_ext(text_input, &string);
86 
87     return status;
88 }
89 #endif
90 
91 /**************************************************************************/
92 /*                                                                        */
93 /*  FUNCTION                                               RELEASE        */
94 /*                                                                        */
95 /*    _gx_multi_line_text_input_char_insert_ext           PORTABLE C      */
96 /*                                                           6.1          */
97 /*  AUTHOR                                                                */
98 /*                                                                        */
99 /*    Kenneth Maxwell, Microsoft Corporation                              */
100 /*                                                                        */
101 /*  DESCRIPTION                                                           */
102 /*                                                                        */
103 /*    This function inserts a character into the input buffer.            */
104 /*                                                                        */
105 /*  INPUT                                                                 */
106 /*                                                                        */
107 /*    text_input                            Multi line text input widget  */
108 /*                                            control block               */
109 /*    str                                   Utf8 string to be inserted    */
110 /*    str_size                              Inserted string size in bytes */
111 /*                                                                        */
112 /*  OUTPUT                                                                */
113 /*                                                                        */
114 /*    None                                                                */
115 /*                                                                        */
116 /*  CALLS                                                                 */
117 /*                                                                        */
118 /*    memmove                               Move block of memory          */
119 /*    _gx_widget_font_get                   Retrieve font                 */
120 /*    _gx_window_scrollbar_find             Find scrollbar for a window   */
121 /*    _gx_scrollbar_reset                   Reset scrollbar information   */
122 /*    _gx_multi_line_text_view_string_total_rows_compute                  */
123 /*                                          Calculate total rows          */
124 /*    _gx_multi_line_text_view_line_cache_update                          */
125 /*                                          Update line cache             */
126 /*    _gx_multi_line_text_input_cursor_pos_update                         */
127 /*                                          Update cursor position        */
128 /*                                            according to insert index   */
129 /*    _gx_utility_rectangle_resize          Increase/Shrink rectangle by  */
130 /*                                            specified value             */
131 /*    _gx_utility_string_length_check       Test string length            */
132 /*    _gx_system_dirty_partial_add          Add one dirty area to dirty   */
133 /*                                            list                        */
134 /*                                                                        */
135 /*  CALLED BY                                                             */
136 /*                                                                        */
137 /*    _gx_multi_line_text_input_keydown_process                           */
138 /*                                                                        */
139 /*  RELEASE HISTORY                                                       */
140 /*                                                                        */
141 /*    DATE              NAME                      DESCRIPTION             */
142 /*                                                                        */
143 /*  05-19-2020     Kenneth Maxwell          Initial Version 6.0           */
144 /*  09-30-2020     Kenneth Maxwell          Modified comment(s),          */
145 /*                                            resulting in version 6.1    */
146 /*                                                                        */
147 /**************************************************************************/
_gx_multi_line_text_input_char_insert_ext(GX_MULTI_LINE_TEXT_INPUT * text_input,GX_CONST GX_STRING * str)148 UINT _gx_multi_line_text_input_char_insert_ext(GX_MULTI_LINE_TEXT_INPUT *text_input, GX_CONST GX_STRING *str)
149 {
150 GX_TEXT_INPUT_CURSOR *cursor_ptr = &text_input -> gx_multi_line_text_input_cursor_instance;
151 UINT                  string_size;
152 UINT                  buffer_size;
153 GX_CHAR              *input_buffer;
154 GX_UBYTE             *insert_char;
155 UINT                  index;
156 INT                   shift;
157 INT                   last_visible_line;
158 GX_RECTANGLE          client;
159 GX_RECTANGLE          dirty_rect;
160 GX_FONT              *font;
161 GX_VALUE              line_height;
162 GX_VALUE              old_cur_y;
163 GX_SCROLLBAR         *scroll;
164 UINT                  text_rows;
165 UINT                  glyph_len;
166 UINT                  handled_size;
167 GX_STRING             insert_str;
168 
169     if (text_input -> gx_multi_line_text_input_start_mark != text_input -> gx_multi_line_text_input_end_mark)
170     {
171         if (text_input -> gx_multi_line_text_input_end_mark < text_input -> gx_multi_line_text_input_start_mark)
172         {
173             _gx_multi_line_text_input_delete(text_input);
174         }
175         else
176         {
177             _gx_multi_line_text_input_backspace(text_input);
178         }
179     }
180 
181     string_size = text_input -> gx_multi_line_text_view_text.gx_string_length;
182     buffer_size = text_input -> gx_multi_line_text_input_buffer_size;
183     insert_str = *str;
184 
185     _gx_widget_font_get((GX_WIDGET *)text_input, text_input -> gx_multi_line_text_view_font_id, &font);
186 
187     if (!font)
188     {
189         return GX_FAILURE;
190     }
191 
192     /* Check if the insert string size is bigger than the remaining buffer size.  */
193     if (buffer_size < string_size + insert_str.gx_string_length + 1)
194     {
195         return GX_FAILURE;
196     }
197 
198     /* Insert a character. */
199 
200     input_buffer = (GX_CHAR *)text_input -> gx_multi_line_text_view_text.gx_string_ptr;
201     glyph_len = 1;
202 
203     while (insert_str.gx_string_length)
204     {
205         if (insert_str.gx_string_ptr[0] == GX_KEY_CARRIAGE_RETURN || insert_str.gx_string_ptr[0] == GX_KEY_LINE_FEED)
206         {
207             /* If the inserted character is a new line character,
208                exchange it with current used new line character. */
209             insert_char = text_input -> gx_multi_line_text_input_new_line_character;
210             glyph_len = text_input -> gx_multi_line_text_input_new_line_character_size;
211 
212             if (insert_str.gx_string_ptr[0] == GX_KEY_CARRIAGE_RETURN)
213             {
214                 if ((insert_str.gx_string_length > 1) && insert_str.gx_string_ptr[1] == GX_KEY_LINE_FEED)
215                 {
216                     handled_size = 2;
217                 }
218                 else
219                 {
220                     handled_size = 1;
221                 }
222             }
223             else
224             {
225                 handled_size = 1;
226             }
227 
228             insert_str.gx_string_ptr += handled_size;
229             insert_str.gx_string_length -= handled_size;
230         }
231         else
232         {
233             insert_char = (GX_UBYTE *)insert_str.gx_string_ptr;
234 #if defined GX_UTF8_SUPPORT
235             _gx_utility_utf8_string_character_get(&insert_str, GX_NULL, &glyph_len);
236 #else
237             glyph_len = 1;
238             insert_str.gx_string_ptr++;
239             insert_str.gx_string_length--;
240 #endif
241         }
242 
243         string_size = text_input -> gx_multi_line_text_view_text.gx_string_length;
244 
245         if (buffer_size < string_size + glyph_len + 1)
246         {
247             break;
248         }
249         index = text_input -> gx_multi_line_text_input_text_insert_position;
250         memmove(input_buffer + index + glyph_len, input_buffer + index, string_size - index + 1);
251         memmove(input_buffer + index, insert_char, glyph_len);
252 
253         /* Update insert position and string size. */
254         text_input -> gx_multi_line_text_input_text_insert_position += glyph_len;
255         text_input -> gx_multi_line_text_view_text.gx_string_length += glyph_len;
256     }
257 
258     /* Save old text rows.  */
259     text_rows = text_input -> gx_multi_line_text_view_text_total_rows;
260 
261     /* Calculate new text rows. */
262     _gx_multi_line_text_view_string_total_rows_compute((GX_MULTI_LINE_TEXT_VIEW *)text_input);
263 
264     if (text_rows != text_input -> gx_multi_line_text_view_text_total_rows)
265     {
266         _gx_window_scrollbar_find((GX_WINDOW *)text_input, GX_TYPE_VERTICAL_SCROLL, &scroll);
267         if (scroll)
268         {
269             /* Reset scrollbar.  */
270             _gx_scrollbar_reset(scroll, GX_NULL);
271         }
272         else
273         {
274 
275             if (text_input -> gx_multi_line_text_view_text_total_rows >
276                 text_input -> gx_multi_line_text_view_cache_size)
277             {
278                 _gx_multi_line_text_view_line_cache_update((GX_MULTI_LINE_TEXT_VIEW *)text_input);
279             }
280         }
281     }
282 
283     /* Save old shift value. */
284     shift = text_input -> gx_multi_line_text_view_text_scroll_shift;
285 
286     /* Record old y coordinate of cursor position. */
287     old_cur_y = cursor_ptr -> gx_text_input_cursor_pos.gx_point_y;
288 
289     /* Update cursor position. */
290     _gx_multi_line_text_input_cursor_pos_update(text_input, GX_TRUE);
291 
292     /* Set the text modified flag to GX_TRUE. */
293     text_input -> gx_multi_line_text_input_text_was_modified = GX_TRUE;
294 
295     if (shift == text_input -> gx_multi_line_text_view_text_scroll_shift)
296     {
297         line_height = (GX_VALUE)(font -> gx_font_line_height +
298                                  text_input -> gx_multi_line_text_view_line_space);
299 
300         if (line_height)
301         {
302             /* Calculate dirty area. */
303             client = text_input -> gx_window_client;
304 
305             /* Offset client area by length of whitespace.  */
306             _gx_utility_rectangle_resize(&client, (GX_VALUE)(-text_input -> gx_multi_line_text_view_whitespace));
307 
308             dirty_rect = client;
309 
310             /* Calculate dirty area. */
311             dirty_rect.gx_rectangle_top = (GX_VALUE)(old_cur_y - line_height - (line_height >> 1));
312 
313             /* Calculate last visible line. */
314             last_visible_line = (-text_input -> gx_multi_line_text_view_text_scroll_shift) / line_height;
315             last_visible_line = last_visible_line + (INT)text_input -> gx_multi_line_text_view_text_visible_rows + 1;
316 
317             if (last_visible_line > (INT)text_input -> gx_multi_line_text_view_text_total_rows)
318             {
319                 last_visible_line = (INT)text_input -> gx_multi_line_text_view_text_total_rows;
320             }
321 
322             shift = last_visible_line * line_height + shift;
323             shift += client.gx_rectangle_top;
324 
325             if (shift < dirty_rect.gx_rectangle_bottom)
326             {
327                 dirty_rect.gx_rectangle_bottom = (GX_VALUE)shift;
328             }
329 
330             _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &dirty_rect);
331         }
332     }
333     else
334     {
335         _gx_system_dirty_mark((GX_WIDGET *)text_input);
336     }
337 
338     return GX_SUCCESS;
339 }
340 
341