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 /**   Text Input Management (Single 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_single_line_text_input.h"
30 #include "gx_text_input_cursor.h"
31 
32 
33 /**************************************************************************/
34 /*                                                                        */
35 /*  FUNCTION                                               RELEASE        */
36 /*                                                                        */
37 /*    _gx_single_line_text_input_pen_down_process         PORTABLE C      */
38 /*                                                           6.1          */
39 /*  AUTHOR                                                                */
40 /*                                                                        */
41 /*    Kenneth Maxwell, Microsoft Corporation                              */
42 /*                                                                        */
43 /*  DESCRIPTION                                                           */
44 /*                                                                        */
45 /*    This function handles pen down event for single line text input     */
46 /*    widget.                                                             */
47 /*                                                                        */
48 /*  INPUT                                                                 */
49 /*                                                                        */
50 /*    text_input                            Single-line text input widget */
51 /*                                            control block               */
52 /*    event_ptr                             Pointer to GX_EVENT structure */
53 /*                                                                        */
54 /*  OUTPUT                                                                */
55 /*                                                                        */
56 /*    status                                Completion status             */
57 /*                                                                        */
58 /*  CALLS                                                                 */
59 /*                                                                        */
60 /*    _gx_single_line_text_input_text_rectangle_get                       */
61 /*                                          Retrieve rectangle from cursor*/
62 /*                                            to specified offset position*/
63 /*    _gx_single_line_text_input_position_get                             */
64 /*                                          Update text insert position   */
65 /*    _gx_system_dirty_partial_add          Mark the partial area of a    */
66 /*                                            widget as dirty             */
67 /*    _gx_widget_event_process              Default widget event process  */
68 /*                                                                        */
69 /*  CALLED BY                                                             */
70 /*                                                                        */
71 /*    _gx_single_line_text_input_event_process                            */
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_single_line_text_input_pen_down_process(GX_SINGLE_LINE_TEXT_INPUT * text_input,GX_EVENT * event_ptr)82 static UINT _gx_single_line_text_input_pen_down_process(GX_SINGLE_LINE_TEXT_INPUT *text_input, GX_EVENT *event_ptr)
83 {
84 GX_RECTANGLE dirty_area;
85 UINT         start_mark = text_input -> gx_single_line_text_input_start_mark;
86 UINT         end_mark = text_input -> gx_single_line_text_input_end_mark;
87 
88     _gx_system_input_capture((GX_WIDGET *)text_input);
89 
90     if (start_mark != end_mark)
91     {
92         /* Retrieve highlight text bounding rectangle. */
93         _gx_single_line_text_input_text_rectangle_get(text_input, (INT)(start_mark - end_mark), &dirty_area);
94 
95         /* Mark highlight area as dirty. */
96         _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &dirty_area);
97     }
98 
99     /* Calculate insert position according to click position. */
100     _gx_single_line_text_input_position_get(text_input, (UINT)((*event_ptr).gx_event_payload.gx_event_pointdata.gx_point_x));
101 
102     /* Set highlight start/end mark to insert position. */
103     text_input -> gx_single_line_text_input_start_mark = text_input -> gx_single_line_text_input_insert_pos;
104     text_input -> gx_single_line_text_input_end_mark = text_input -> gx_single_line_text_input_insert_pos;
105 
106     /* Call the widget default processing.  */
107     return _gx_widget_event_process((GX_WIDGET *)text_input, event_ptr);
108 }
109 
110 /**************************************************************************/
111 /*                                                                        */
112 /*  FUNCTION                                               RELEASE        */
113 /*                                                                        */
114 /*    _gx_single_line_text_input_pen_drag_process         PORTABLE C      */
115 /*                                                           6.1          */
116 /*  AUTHOR                                                                */
117 /*                                                                        */
118 /*    Kenneth Maxwell, Microsoft Corporation                              */
119 /*                                                                        */
120 /*  DESCRIPTION                                                           */
121 /*                                                                        */
122 /*    This function handles pen drag event for single line text input     */
123 /*    widget.                                                             */
124 /*                                                                        */
125 /*  INPUT                                                                 */
126 /*                                                                        */
127 /*    text_input                            Single-line text input widget */
128 /*                                            control block               */
129 /*    event_ptr                             Pointer to GX_EVENT structure */
130 /*                                                                        */
131 /*  OUTPUT                                                                */
132 /*                                                                        */
133 /*    status                                Completion status             */
134 /*                                                                        */
135 /*  CALLS                                                                 */
136 /*                                                                        */
137 /*    _gx_text_input_cursor_dirty_rectangle_get                           */
138 /*                                          Retrieve cursor rectangle     */
139 /*    _gx_single_line_text_input_text_rectangle_get                       */
140 /*                                          Retrieve rectangle from cursor*/
141 /*                                            to specified offset position*/
142 /*    _gx_single_line_text_input_position_get                             */
143 /*                                          Update text insert position   */
144 /*    _gx_system_dirty_partial_add          Mark the partial area of a    */
145 /*                                            widget as dirty             */
146 /*    _gx_widget_event_process              Default widget event process  */
147 /*                                                                        */
148 /*  CALLED BY                                                             */
149 /*                                                                        */
150 /*    _gx_single_line_text_input_event_process                            */
151 /*                                                                        */
152 /*  RELEASE HISTORY                                                       */
153 /*                                                                        */
154 /*    DATE              NAME                      DESCRIPTION             */
155 /*                                                                        */
156 /*  05-19-2020     Kenneth Maxwell          Initial Version 6.0           */
157 /*  09-30-2020     Kenneth Maxwell          Modified comment(s),          */
158 /*                                            resulting in version 6.1    */
159 /*                                                                        */
160 /**************************************************************************/
_gx_single_line_text_input_pen_drag_process(GX_SINGLE_LINE_TEXT_INPUT * text_input,GX_EVENT * event_ptr)161 static UINT _gx_single_line_text_input_pen_drag_process(GX_SINGLE_LINE_TEXT_INPUT *text_input, GX_EVENT *event_ptr)
162 {
163 GX_RECTANGLE dirty_area;
164 UINT         start_mark = text_input -> gx_single_line_text_input_start_mark;
165 UINT         end_mark = text_input -> gx_single_line_text_input_end_mark;
166 GX_VALUE     border_width;
167 GX_RECTANGLE client;
168 GX_VALUE     click_x;
169 
170     if (text_input -> gx_widget_status & GX_STATUS_OWNS_INPUT)
171     {
172         click_x = event_ptr -> gx_event_payload.gx_event_pointdata.gx_point_x;
173 
174         /* Calculate client rectangle. */
175         _gx_widget_border_width_get((GX_WIDGET *)text_input, &border_width);
176         _gx_widget_client_get((GX_WIDGET *)text_input, border_width, &client);
177 
178         /* Calculate insert position. */
179         _gx_single_line_text_input_position_get(text_input, (UINT)click_x);
180 
181         /* Update end mark. */
182         text_input -> gx_single_line_text_input_end_mark = text_input -> gx_single_line_text_input_insert_pos;
183 
184         if ((click_x < client.gx_rectangle_left) && (text_input -> gx_single_line_text_input_end_mark > 0))
185         {
186             if (!(text_input -> gx_widget_status & (GX_STATUS_MARK_NEXT | GX_STATUS_MARK_PREVIOUS)))
187             {
188                 /* Start a timer to move text right. */
189                 _gx_system_timer_start((GX_WIDGET *)text_input, GX_MARK_TIMER,
190                                        GX_MARK_INTERVAL, GX_MARK_INTERVAL);
191             }
192 
193             text_input -> gx_widget_status &= ~GX_STATUS_MARK_NEXT;
194             text_input -> gx_widget_status |= GX_STATUS_MARK_PREVIOUS;
195         }
196         else if ((click_x > client.gx_rectangle_right) &&
197                  (text_input -> gx_single_line_text_input_end_mark < text_input -> gx_single_line_text_input_string_size))
198         {
199             if (!(text_input -> gx_widget_status & (GX_STATUS_MARK_NEXT | GX_STATUS_MARK_PREVIOUS)))
200             {
201                 /* Start a timer to move text left. */
202                 _gx_system_timer_start((GX_WIDGET *)text_input, GX_MARK_TIMER,
203                                        GX_MARK_INTERVAL, GX_MARK_INTERVAL);
204             }
205 
206             text_input -> gx_widget_status &= ~GX_STATUS_MARK_PREVIOUS;
207             text_input -> gx_widget_status |= GX_STATUS_MARK_NEXT;
208         }
209         else
210         {
211             if (text_input -> gx_widget_status & (GX_STATUS_MARK_NEXT | GX_STATUS_MARK_PREVIOUS))
212             {
213                 _gx_system_timer_stop((GX_WIDGET *)text_input, GX_MARK_TIMER);
214                 text_input -> gx_widget_status &= ~(GX_STATUS_MARK_NEXT | GX_STATUS_MARK_PREVIOUS);
215             }
216         }
217 
218         if ((start_mark != text_input -> gx_single_line_text_input_end_mark) ||
219             (start_mark != end_mark))
220         {
221             /* Retrieve text bounding rectangle between old and new end mark. */
222             _gx_single_line_text_input_text_rectangle_get(text_input, (INT)(end_mark - text_input -> gx_single_line_text_input_end_mark), &dirty_area);
223 
224             _gx_system_dirty_partial_add((GX_WIDGET *)text_input, &dirty_area);
225         }
226     }
227     else
228     {
229         _gx_widget_event_process((GX_WIDGET *)text_input, event_ptr);
230     }
231 
232     return GX_SUCCESS;
233 }
234 
235 /**************************************************************************/
236 /*                                                                        */
237 /*  FUNCTION                                               RELEASE        */
238 /*                                                                        */
239 /*    _gx_single_line_text_input_event_process            PORTABLE C      */
240 /*                                                           6.4.0        */
241 /*  AUTHOR                                                                */
242 /*                                                                        */
243 /*    Kenneth Maxwell, Microsoft Corporation                              */
244 /*                                                                        */
245 /*  DESCRIPTION                                                           */
246 /*                                                                        */
247 /*    This service processes a single line text input event. This function*/
248 /*    is internally referenced by the gx_single_line_text_input_create    */
249 /*    function, but is exposed for use by the application in those cases  */
250 /*    where the application defines a custom single line text input event */
251 /*    processing function.                                                */
252 /*                                                                        */
253 /*  INPUT                                                                 */
254 /*                                                                        */
255 /*    text_input                            Single-line text input widget */
256 /*                                            control block               */
257 /*    event_ptr                             Pointer to GX_EVENT structure */
258 /*                                                                        */
259 /*  OUTPUT                                                                */
260 /*                                                                        */
261 /*    status                                Completion status             */
262 /*                                                                        */
263 /*  CALLS                                                                 */
264 /*                                                                        */
265 /*    _gx_widget_event_process              Default widget event process  */
266 /*    _gx_widget_event_generate             Create a event and send it to */
267 /*                                            parent                      */
268 /*    _gx_system_timer_start                Start the system timer        */
269 /*    _gx_system_timer_stop                 Stop the system timer         */
270 /*    _gx_system_dirty_partial_add          Mark the partial area of a    */
271 /*                                            widget as dirty             */
272 /*    _gx_text_input_cursor_dirty_rectangle_get                           */
273 /*                                          Get cursor rectangle          */
274 /*    _gx_single_line_text_input_keydown_process                          */
275 /*                                          Keydown event process function*/
276 /*    _gx_single_line_text_input_position_update                          */
277 /*                                          Update cursor position by     */
278 /*                                            insert position             */
279 /*                                                                        */
280 /*  CALLED BY                                                             */
281 /*                                                                        */
282 /*    Application Code                                                    */
283 /*    GUIX Internal Code                                                  */
284 /*                                                                        */
285 /*  RELEASE HISTORY                                                       */
286 /*                                                                        */
287 /*    DATE              NAME                      DESCRIPTION             */
288 /*                                                                        */
289 /*  05-19-2020     Kenneth Maxwell          Initial Version 6.0           */
290 /*  09-30-2020     Kenneth Maxwell          Modified comment(s),          */
291 /*                                            resulting in version 6.1    */
292 /*  12-31-2020     Kenneth Maxwell          Modified comment(s),          */
293 /*                                            added logic to release      */
294 /*                                            dynamic input buffer,       */
295 /*                                            resulting in version 6.1.3  */
296 /*  12-31-2023     Ting Zhu                 Modified comment(s),          */
297 /*                                            modified to always call     */
298 /*                                            default widget event        */
299 /*                                            process on a pen up event,  */
300 /*                                            resulting in version 6.4.0  */
301 /*                                                                        */
302 /**************************************************************************/
_gx_single_line_text_input_event_process(GX_SINGLE_LINE_TEXT_INPUT * text_input,GX_EVENT * event_ptr)303 UINT  _gx_single_line_text_input_event_process(GX_SINGLE_LINE_TEXT_INPUT *text_input, GX_EVENT *event_ptr)
304 {
305 UINT         status;
306 GX_WIDGET   *widget = (GX_WIDGET *)text_input;
307 GX_RECTANGLE dirty_area;
308 GX_VALUE     blink_interval;
309 ULONG        old_style;
310 
311     /* Default status to success.  */
312     status =  GX_SUCCESS;
313 
314     /* Process relative to the type of event.  */
315     switch (event_ptr -> gx_event_type)
316     {
317     case GX_EVENT_SHOW:
318         _gx_widget_event_process(widget, event_ptr);
319 
320         if ((text_input -> gx_widget_style & GX_STYLE_CURSOR_ALWAYS) &&
321             (text_input -> gx_widget_style & GX_STYLE_CURSOR_BLINK))
322         {
323             _gx_system_timer_start((GX_WIDGET *)text_input, ID_TEXT_INPUT_TIMER, GX_CURSOR_BLINK_INTERVAL, GX_CURSOR_BLINK_INTERVAL);
324         }
325 
326         /* Update cursor position. */
327         _gx_single_line_text_input_position_update(text_input);
328         break;
329 
330     case GX_EVENT_RESIZED:
331         _gx_single_line_text_input_position_update(text_input);
332         break;
333 
334     case GX_EVENT_STYLE_CHANGED:
335         if (widget -> gx_widget_status & GX_STATUS_VISIBLE)
336         {
337             old_style = event_ptr -> gx_event_payload.gx_event_ulongdata;
338             if ((old_style & (GX_STYLE_BORDER_MASK | GX_STYLE_TEXT_ALIGNMENT_MASK)) !=
339                 (widget -> gx_widget_style & (GX_STYLE_BORDER_MASK | GX_STYLE_TEXT_ALIGNMENT_MASK)))
340             {
341                 _gx_single_line_text_input_position_update(text_input);
342             }
343         }
344         break;
345 
346     case GX_EVENT_KEY_DOWN:
347         _gx_single_line_text_input_keydown_process(text_input, event_ptr);
348 
349         /*If input was done, send event to parent.  */
350         if (widget -> gx_widget_style & GX_STYLE_TEXT_INPUT_NOTIFY_ALL)
351         {
352             _gx_widget_event_generate(widget, GX_EVENT_TEXT_EDITED, 0);
353         }
354         break;
355 
356     case GX_EVENT_FOCUS_GAINED:
357         if (!(text_input -> gx_widget_style & GX_STYLE_CURSOR_ALWAYS))
358         {
359             text_input -> gx_widget_status |= (GX_STATUS_CURSOR_SHOW | GX_STATUS_CURSOR_DRAW);
360 
361             if (text_input -> gx_widget_style & GX_STYLE_CURSOR_BLINK)
362             {
363                 blink_interval = text_input -> gx_single_line_text_input_cursor_instance.gx_text_input_cursor_blink_interval;
364                 _gx_system_timer_start(widget, ID_TEXT_INPUT_TIMER, (UINT)blink_interval, (UINT)blink_interval);
365             }
366         }
367 
368         /* Select all text. */
369         _gx_single_line_text_input_text_select(text_input, 0, text_input -> gx_single_line_text_input_string_size - 1);
370 
371         /* this widget wants to be notified if it is moved or re-sized */
372         text_input -> gx_widget_status |= GX_STATUS_RESIZE_NOTIFY;
373 
374         /* Call the widget default processing.  */
375         status = _gx_widget_event_process(widget, event_ptr);
376         break;
377 
378     case GX_EVENT_PEN_DOWN:
379         _gx_single_line_text_input_pen_down_process(text_input, event_ptr);
380         break;
381 
382     case GX_EVENT_PEN_DRAG:
383         _gx_single_line_text_input_pen_drag_process(text_input, event_ptr);
384         break;
385 
386     case GX_EVENT_PEN_UP:
387         if (text_input -> gx_widget_status & GX_STATUS_OWNS_INPUT)
388         {
389             _gx_system_input_release((GX_WIDGET *)text_input);
390 
391             if (text_input -> gx_widget_status & (GX_STATUS_MARK_NEXT | GX_STATUS_MARK_PREVIOUS))
392             {
393                 _gx_system_timer_stop((GX_WIDGET *)text_input, GX_MARK_TIMER);
394                 text_input -> gx_widget_status &= ~(GX_STATUS_MARK_NEXT | GX_STATUS_MARK_PREVIOUS);
395             }
396         }
397 
398         _gx_widget_event_process(widget, event_ptr);
399         break;
400 
401     case GX_EVENT_TIMER:
402         if (event_ptr -> gx_event_payload.gx_event_timer_id == GX_MARK_TIMER)
403         {
404             if (text_input -> gx_widget_status & GX_STATUS_MARK_PREVIOUS)
405             {
406                 _gx_single_line_text_input_mark_previous(text_input);
407             }
408             else
409             {
410                 _gx_single_line_text_input_mark_next(text_input);
411             }
412         }
413         else if ((event_ptr -> gx_event_payload.gx_event_timer_id == ID_TEXT_INPUT_TIMER) &&
414                  (text_input -> gx_widget_status & GX_STATUS_CURSOR_SHOW))
415         {
416             if (text_input -> gx_widget_status & GX_STATUS_CURSOR_DRAW)
417             {
418                 text_input -> gx_widget_status &= (ULONG)(~GX_STATUS_CURSOR_DRAW);
419             }
420             else
421             {
422                 text_input -> gx_widget_status |= GX_STATUS_CURSOR_DRAW;
423             }
424 
425             _gx_text_input_cursor_dirty_rectangle_get(&text_input -> gx_single_line_text_input_cursor_instance, &dirty_area);
426             _gx_system_dirty_partial_add(widget, &dirty_area);
427         }
428         break;
429 
430     case GX_EVENT_FOCUS_LOST:
431         if (!(text_input -> gx_widget_style & GX_STYLE_CURSOR_ALWAYS))
432         {
433             if (text_input -> gx_widget_style & GX_STYLE_CURSOR_BLINK)
434             {
435                 _gx_system_timer_stop(widget, ID_TEXT_INPUT_TIMER);
436             }
437             text_input -> gx_widget_status &= (ULONG)(~GX_STATUS_CURSOR_SHOW);
438 
439             if (text_input -> gx_single_line_text_input_start_mark == text_input -> gx_single_line_text_input_end_mark)
440             {
441                 _gx_text_input_cursor_dirty_rectangle_get(&text_input -> gx_single_line_text_input_cursor_instance, &dirty_area);
442                 _gx_system_dirty_partial_add(widget, &dirty_area);
443             }
444 
445             /* If cursor always is not set, do-not receive resize notify when lose focus */
446             text_input -> gx_widget_status &= ~GX_STATUS_RESIZE_NOTIFY;
447         }
448 
449         if (text_input -> gx_single_line_text_input_start_mark != text_input -> gx_single_line_text_input_end_mark)
450         {
451             _gx_single_line_text_input_text_rectangle_get(text_input,
452                                                           (INT)(text_input -> gx_single_line_text_input_start_mark - text_input -> gx_single_line_text_input_end_mark), &dirty_area);
453 
454             text_input -> gx_single_line_text_input_start_mark = 0;
455             text_input -> gx_single_line_text_input_end_mark = 0;
456 
457             _gx_system_dirty_partial_add(widget, &dirty_area);
458         }
459 
460         /*If string was modified, send event to parent.  */
461         if (text_input -> gx_single_line_text_input_was_modified)
462         {
463             _gx_widget_event_generate(widget, GX_EVENT_TEXT_EDITED, 0);
464             text_input -> gx_single_line_text_input_was_modified = GX_FALSE;
465         }
466 
467         /* Call the widget default processing.  */
468         status = _gx_widget_event_process(widget, event_ptr);
469         break;
470 
471     case GX_EVENT_COPY:
472         _gx_single_line_text_input_copy(text_input);
473         break;
474 
475     case GX_EVENT_CUT:
476         _gx_single_line_text_input_cut(text_input);
477         break;
478 
479     case GX_EVENT_PASTE:
480         _gx_single_line_text_input_paste(text_input);
481         break;
482 
483     case GX_EVENT_MARK_NEXT:
484         _gx_single_line_text_input_mark_next(text_input);
485         break;
486 
487     case GX_EVENT_MARK_PREVIOUS:
488         _gx_single_line_text_input_mark_previous(text_input);
489         break;
490 
491     case GX_EVENT_MARK_HOME:
492         _gx_single_line_text_input_mark_home(text_input);
493         break;
494 
495     case GX_EVENT_MARK_END:
496         _gx_single_line_text_input_mark_end(text_input);
497         break;
498 
499     case GX_EVENT_DELETE:
500         if (text_input -> gx_widget_status & GX_STATUS_DYNAMIC_BUFFER)
501         {
502             if (!_gx_system_memory_free)
503             {
504                 return GX_SYSTEM_MEMORY_ERROR;
505             }
506 
507             _gx_system_memory_free(text_input -> gx_single_line_text_input_buffer);
508             text_input -> gx_single_line_text_input_buffer = GX_NULL;
509         }
510         break;
511 
512     default:
513         /* Call the widget default processing.  */
514         status = _gx_widget_event_process(widget, event_ptr);
515     }
516 
517     /* Return completion status.  */
518     return(status);
519 }
520 
521