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_input.h"
29 #include "gx_multi_line_text_view.h"
30 #include "gx_window.h"
31 #include "gx_widget.h"
32 #include "gx_utility.h"
33 #include "gx_scrollbar.h"
34 
35 /**************************************************************************/
36 /*                                                                        */
37 /*  FUNCTION                                               RELEASE        */
38 /*                                                                        */
39 /*    _gx_multi_line_text_input_backspace                 PORTABLE C      */
40 /*                                                           6.1.7        */
41 /*  AUTHOR                                                                */
42 /*                                                                        */
43 /*    Kenneth Maxwell, Microsoft Corporation                              */
44 /*                                                                        */
45 /*  DESCRIPTION                                                           */
46 /*                                                                        */
47 /*    This function deletes a character before the cursor.                */
48 /*                                                                        */
49 /*  INPUT                                                                 */
50 /*                                                                        */
51 /*    text_input                            Multi line text input widget  */
52 /*                                            control block               */
53 /*                                                                        */
54 /*  OUTPUT                                                                */
55 /*                                                                        */
56 /*    None                                                                */
57 /*                                                                        */
58 /*  CALLS                                                                 */
59 /*                                                                        */
60 /*    _gx_widget_font_get                   Retrieve font                 */
61 /*    _gx_utility_rectangle_resize          Increase/Shrink rectangle with*/
62 /*                                            specified value             */
63 /*    _gx_utility_utf8_string_character_get Parses utf8 string to         */
64 /*                                            multi-byte glyph            */
65 /*    _gx_utility_string_length_check       Test string length            */
66 /*    _gx_window_scrollbar_find             Find scrollbar for a window   */
67 /*    _gx_window_client_width_get           Get client width              */
68 /*    _gx_scrollbar_reset                   Reset scrollbar information   */
69 /*    _gx_multi_line_text_view_line_cache_update                          */
70 /*                                          Update line cache             */
71 /*    _gx_multi_line_text_view_display_info_get                           */
72 /*                                          Get the number of characters  */
73 /*                                            that a line can display     */
74 /*    _gx_multi_line_text_input_char_remove Remove a character            */
75 /*    _gx_multi_line_text_input_cursor_pos_update                         */
76 /*                                          Update cursor position        */
77 /*                                            according to insert index   */
78 /*    _gx_multi_line_text_view_string_total_rows_compute                  */
79 /*    _gx_system_string_width_get           Get string width              */
80 /*    _gx_system_dirty_partial_add          Add one dirty area to dirty   */
81 /*                                            list                        */
82 /*                                                                        */
83 /*                                                                        */
84 /*  CALLED BY                                                             */
85 /*                                                                        */
86 /*    _gx_multi_line_text_input_keydown_process                           */
87 /*                                                                        */
88 /*  RELEASE HISTORY                                                       */
89 /*                                                                        */
90 /*    DATE              NAME                      DESCRIPTION             */
91 /*                                                                        */
92 /*  05-19-2020     Kenneth Maxwell          Initial Version 6.0           */
93 /*  09-30-2020     Kenneth Maxwell          Modified comment(s),          */
94 /*                                            resulting in version 6.1    */
95 /*  06-02-2021     Kenneth Maxwell          Modified comment(s),          */
96 /*                                            removed unused variable     */
97 /*                                            assignment,                 */
98 /*                                            resulting in version 6.1.7  */
99 /*                                                                        */
100 /**************************************************************************/
_gx_multi_line_text_input_backspace(GX_MULTI_LINE_TEXT_INPUT * text_input)101 UINT _gx_multi_line_text_input_backspace(GX_MULTI_LINE_TEXT_INPUT *text_input)
102 {
103 GX_TEXT_INPUT_CURSOR    *cursor_ptr = &text_input -> gx_multi_line_text_input_cursor_instance;
104 GX_MULTI_LINE_TEXT_VIEW *view = (GX_MULTI_LINE_TEXT_VIEW *)text_input;
105 GX_FONT                 *font;
106 GX_RECTANGLE             client;
107 GX_RECTANGLE             dirty_rect;
108 UINT                     insert_pos;
109 INT                      shift;
110 INT                      last_visible_line;
111 GX_VALUE                 line_height;
112 GX_VALUE                 delete_width;
113 UINT                     old_text_rows;
114 UINT                     glyph_len = 1;
115 GX_STRING                string;
116 GX_SCROLLBAR            *scroll;
117 UINT                     start_mark = text_input -> gx_multi_line_text_input_start_mark;
118 UINT                     end_mark = text_input -> gx_multi_line_text_input_end_mark;
119 
120     if (end_mark < start_mark)
121     {
122         return _gx_multi_line_text_input_delete(text_input);
123     }
124 
125     insert_pos = text_input -> gx_multi_line_text_input_text_insert_position;
126 
127     if (insert_pos > 0)
128     {
129         _gx_widget_font_get((GX_WIDGET *)text_input, text_input -> gx_multi_line_text_view_font_id, &font);
130 
131         if (!font)
132         {
133             return GX_FAILURE;
134         }
135 
136         if (start_mark != end_mark)
137         {
138             glyph_len = (UINT)GX_ABS((INT)start_mark - (INT)end_mark);
139             text_input -> gx_multi_line_text_input_start_mark = 0;
140             text_input -> gx_multi_line_text_input_end_mark = 0;
141 
142             string.gx_string_ptr = &text_input -> gx_multi_line_text_view_text.gx_string_ptr[insert_pos - glyph_len];
143         }
144         else
145         {
146             _gx_multi_line_text_input_cursor_visible(text_input);
147 
148 #ifdef GX_UTF8_SUPPORT
149             _gx_utility_utf8_string_backward_character_length_get(&text_input -> gx_multi_line_text_view_text,
150                                                                   (INT)(insert_pos - 1), &glyph_len);
151 #endif
152 
153             /* Get the character to be deleted.  */
154             string.gx_string_ptr = &text_input -> gx_multi_line_text_view_text.gx_string_ptr[insert_pos - glyph_len];
155 
156             if (string.gx_string_ptr[0] == GX_KEY_CARRIAGE_RETURN || string.gx_string_ptr[0] == GX_KEY_LINE_FEED)
157             {
158                 glyph_len = text_input -> gx_multi_line_text_input_new_line_character_size;
159 
160                 string.gx_string_ptr = &text_input -> gx_multi_line_text_view_text.gx_string_ptr[insert_pos - glyph_len];
161             }
162         }
163 
164         string.gx_string_length = glyph_len;
165 
166         /* Get the width of the character to be deleted. */
167         _gx_system_string_width_get_ext(font, &string, &delete_width);
168 
169         /* Record old shift value.  */
170         shift = text_input -> gx_multi_line_text_view_text_scroll_shift;
171 
172         /* Remove character before cursor. */
173         _gx_multi_line_text_input_char_remove(text_input, insert_pos - glyph_len, glyph_len);
174 
175         /* Save old text rows.  */
176         old_text_rows = text_input -> gx_multi_line_text_view_text_total_rows;
177 
178         /* Calculate new text rows.  */
179         _gx_multi_line_text_view_string_total_rows_compute((GX_MULTI_LINE_TEXT_VIEW *)text_input);
180 
181         if (old_text_rows != text_input -> gx_multi_line_text_view_text_total_rows)
182         {
183             _gx_window_scrollbar_find((GX_WINDOW *)text_input, GX_TYPE_VERTICAL_SCROLL, &scroll);
184             if (scroll)
185             {
186                 /* Reset scrollbar.  */
187                 _gx_scrollbar_reset(scroll, GX_NULL);
188             }
189             else
190             {
191                 if (text_input -> gx_multi_line_text_view_text_total_rows >
192                     view -> gx_multi_line_text_view_cache_size)
193                 {
194                     _gx_multi_line_text_view_line_cache_update((GX_MULTI_LINE_TEXT_VIEW *)text_input);
195                 }
196             }
197         }
198 
199         /* Update text insert position.  */
200         text_input -> gx_multi_line_text_input_text_insert_position -= glyph_len;
201 
202         client = text_input -> gx_window_client;
203 
204         if (text_input -> gx_multi_line_text_view_whitespace)
205         {
206             /* Offset client bounding box.  */
207             _gx_utility_rectangle_resize(&client, (GX_VALUE)(-text_input -> gx_multi_line_text_view_whitespace));
208         }
209 
210         /* Recalculate cursor position. */
211         _gx_multi_line_text_input_cursor_pos_update(text_input, GX_TRUE);
212 
213         if (shift == text_input -> gx_multi_line_text_view_text_scroll_shift)
214         {
215 
216             line_height = (GX_VALUE)(font -> gx_font_line_height + text_input -> gx_multi_line_text_view_line_space);
217 
218             if (line_height)
219             {
220                 /* Calculate dirty area. */
221                 dirty_rect = client;
222 
223                 dirty_rect.gx_rectangle_top = (GX_VALUE)(cursor_ptr -> gx_text_input_cursor_pos.gx_point_y - (line_height << 1));
224 
225                 /* Calculate last visible line. */
226                 last_visible_line = (-text_input -> gx_multi_line_text_view_text_scroll_shift) / line_height;
227                 last_visible_line = last_visible_line + (INT)view -> gx_multi_line_text_view_text_visible_rows + 1;
228 
229                 if (last_visible_line > (INT)text_input -> gx_multi_line_text_view_text_total_rows)
230                 {
231                     last_visible_line = (INT)text_input -> gx_multi_line_text_view_text_total_rows;
232                 }
233 
234                 shift = (last_visible_line + (INT)old_text_rows - (INT)text_input -> gx_multi_line_text_view_text_total_rows) * line_height + shift;
235                 shift += client.gx_rectangle_top;
236 
237                 if (shift < dirty_rect.gx_rectangle_bottom)
238                 {
239                     dirty_rect.gx_rectangle_bottom = (GX_VALUE)shift;
240                 }
241 
242                 _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &dirty_rect);
243             }
244         }
245         else
246         {
247             _gx_system_dirty_mark((GX_WIDGET *)text_input);
248         }
249     }
250 
251     return GX_SUCCESS;
252 }
253 
254