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 /**   Vertical List (List)                                                */
18 /**                                                                       */
19 /**************************************************************************/
20 
21 #define GX_SOURCE_CODE
22 
23 
24 /* Include necessary system files.  */
25 
26 #include "gx_api.h"
27 #include "gx_widget.h"
28 #include "gx_window.h"
29 #include "gx_system.h"
30 #include "gx_scrollbar.h"
31 
32 /**************************************************************************/
33 /*                                                                        */
34 /*  FUNCTION                                               RELEASE        */
35 /*                                                                        */
36 /*    _gx_vertical_list_invisible_page_scroll             PORTABLE C      */
37 /*                                                           6.1.8        */
38 /*  AUTHOR                                                                */
39 /*                                                                        */
40 /*    Ting Zhu, Microsoft Corporation                                     */
41 /*                                                                        */
42 /*  DESCRIPTION                                                           */
43 /*                                                                        */
44 /*    This function moves up or down the vertical list by pages.          */
45 /*                                                                        */
46 /*  INPUT                                                                 */
47 /*                                                                        */
48 /*    list                                  Vertical list widget control  */
49 /*                                            block                       */
50 /*    num_pages                             The number of pages to scroll */
51 /*                                                                        */
52 /*  OUTPUT                                                                */
53 /*                                                                        */
54 /*    None                                                                */
55 /*                                                                        */
56 /*  CALLS                                                                 */
57 /*                                                                        */
58 /*    _gx_widget_first_client_child_get     Get first client child        */
59 /*    _gx_widget_next_client_child_get      Get next client child         */
60 /*                                                                        */
61 /*  CALLED BY                                                             */
62 /*                                                                        */
63 /*    _gx_vertical_list_scroll                                            */
64 /*                                                                        */
65 /*  RELEASE HISTORY                                                       */
66 /*                                                                        */
67 /*    DATE              NAME                      DESCRIPTION             */
68 /*                                                                        */
69 /*  08-02-2021     Ting Zhu                 Initial Version 6.1.8         */
70 /*                                                                        */
71 /**************************************************************************/
_gx_vertical_list_invisible_page_scroll(GX_VERTICAL_LIST * list,INT num_pages)72 static VOID _gx_vertical_list_invisible_page_scroll(GX_VERTICAL_LIST *list, INT num_pages)
73 {
74 INT maximum_scroll_pages;
75 
76     if (!(list -> gx_widget_style & GX_STYLE_WRAP))
77     {
78 
79         /* Wrap style is not applied, need to calculate scroll limit.  */
80         if (num_pages < 0)
81         {
82 
83             /* Calculate maximum up scroll num_pages.  */
84             maximum_scroll_pages = (list -> gx_vertical_list_total_rows - list -> gx_vertical_list_top_index - list -> gx_vertical_list_child_count);
85             maximum_scroll_pages /= list -> gx_vertical_list_child_count;
86 
87             /* Remain one page for normal scroll routine. */
88             if (maximum_scroll_pages >= 1)
89             {
90                 maximum_scroll_pages--;
91             }
92 
93             if (maximum_scroll_pages < (-num_pages))
94             {
95                 num_pages = (-maximum_scroll_pages);
96             }
97         }
98         else
99         {
100 
101             /* Calculate maximum down scroll num_pages.   */
102             maximum_scroll_pages = list -> gx_vertical_list_top_index / list -> gx_vertical_list_child_count;
103 
104             /* Remain one page for normal scroll routine. */
105             if (maximum_scroll_pages >= 1)
106             {
107                 maximum_scroll_pages--;
108             }
109 
110             if (maximum_scroll_pages < num_pages)
111             {
112                 num_pages = maximum_scroll_pages;
113             }
114         }
115     }
116 
117     if (num_pages)
118     {
119 
120         /* Update top index according to scroll num_pages.  */
121         list -> gx_vertical_list_top_index -= num_pages * list -> gx_vertical_list_child_count;
122 
123         if (list -> gx_widget_style & GX_STYLE_WRAP)
124         {
125 
126             /* Wrap page index.  */
127             if (num_pages < 0)
128             {
129                 while (list -> gx_vertical_list_top_index >= list -> gx_vertical_list_total_rows)
130                 {
131                     list -> gx_vertical_list_top_index -= list -> gx_vertical_list_total_rows;
132                 }
133             }
134             else
135             {
136                 while (list -> gx_vertical_list_top_index < 0)
137                 {
138                     list -> gx_vertical_list_top_index += list -> gx_vertical_list_total_rows;
139                 }
140             }
141         }
142     }
143 }
144 
145 /**************************************************************************/
146 /*                                                                        */
147 /*  FUNCTION                                               RELEASE        */
148 /*                                                                        */
149 /*    _gx_vertical_list_scroll                            PORTABLE C      */
150 /*                                                           6.1.8        */
151 /*  AUTHOR                                                                */
152 /*                                                                        */
153 /*    Kenneth Maxwell, Microsoft Corporation                              */
154 /*                                                                        */
155 /*  DESCRIPTION                                                           */
156 /*                                                                        */
157 /*    This function moves up or down the scrollbar.                       */
158 /*                                                                        */
159 /*  INPUT                                                                 */
160 /*                                                                        */
161 /*    vertical_list                         Vertical list widget control  */
162 /*                                            block                       */
163 /*    amount                                Shifting value                */
164 /*                                                                        */
165 /*  OUTPUT                                                                */
166 /*                                                                        */
167 /*    None                                                                */
168 /*                                                                        */
169 /*  CALLS                                                                 */
170 /*                                                                        */
171 /*    _gx_widget_scroll_shift               Shift a widget                */
172 /*    _gx_vertical_list_up_wrap             Scroll up the vertical list   */
173 /*    _gx_vertical_list_down_wrap           Scroll down the vertical list */
174 /*    _gx_widget_block_move                 Widget block move             */
175 /*                                                                        */
176 /*  CALLED BY                                                             */
177 /*                                                                        */
178 /*    _gx_vertical_list_event_process       Vertical list event process   */
179 /*                                                                        */
180 /*  RELEASE HISTORY                                                       */
181 /*                                                                        */
182 /*    DATE              NAME                      DESCRIPTION             */
183 /*                                                                        */
184 /*  05-19-2020     Kenneth Maxwell          Initial Version 6.0           */
185 /*  09-30-2020     Kenneth Maxwell          Modified comment(s),          */
186 /*                                            resulting in version 6.1    */
187 /*  08-02-2021     Ting Zhu                 Modified comment(s),          */
188 /*                                            fixed scroll overflow issue,*/
189 /*                                            resulting in version 6.1.8  */
190 /*                                                                        */
191 /**************************************************************************/
_gx_vertical_list_scroll(GX_VERTICAL_LIST * list,INT amount)192 VOID _gx_vertical_list_scroll(GX_VERTICAL_LIST *list, INT amount)
193 {
194 GX_WIDGET    *child;
195 GX_WIDGET    *last_child;
196 GX_RECTANGLE  block;
197 GX_SCROLLBAR *scroll;
198 GX_BOOL       do_block_move = GX_TRUE;
199 GX_BOOL       reuse_child_widgets = GX_FALSE;
200 INT           page_size;
201 INT           num_pages;
202 
203     if (!amount)
204     {
205         return;
206     }
207 
208     if ((list -> gx_vertical_list_callback != GX_NULL) &&
209         (list -> gx_vertical_list_visible_rows < list -> gx_vertical_list_total_rows) &&
210         ((list -> gx_vertical_list_child_count < list -> gx_vertical_list_total_rows) || (list -> gx_widget_style & GX_STYLE_WRAP)))
211     {
212         reuse_child_widgets = GX_TRUE;
213 
214         /* Calculate the page size.  */
215         page_size = (list -> gx_vertical_list_child_height * list -> gx_vertical_list_child_count);
216 
217         if (!page_size)
218         {
219             return;
220         }
221 
222         /* Calculate the number of pages to be scrolled.  */
223         if (amount < 0)
224         {
225             num_pages = (amount + page_size) / page_size;
226         }
227         else
228         {
229             num_pages = (amount - page_size) / page_size;
230         }
231 
232         if (num_pages)
233         {
234 
235             /* Calculate the remainning scroll amount.  */
236             amount -= (num_pages * page_size);
237 
238             /* Scroll pages.  */
239             _gx_vertical_list_invisible_page_scroll(list, num_pages);
240         }
241     }
242 
243     /* first shift my child widgets */
244     child = list -> gx_widget_first_child;
245 
246     while (child)
247     {
248         if (!(child -> gx_widget_status & GX_STATUS_NONCLIENT))
249         {
250             _gx_widget_scroll_shift(child, 0, amount, GX_TRUE);
251         }
252         child = child -> gx_widget_next;
253     }
254 
255     /* next check to see if we need to wrap any child widgets */
256 
257     if (reuse_child_widgets)
258     {
259         /* this means we have fewer children than list rows, so we
260            need to move and re-use the child widgets
261          */
262         if (amount < 0)
263         {
264             _gx_vertical_list_up_wrap(list);
265         }
266         else
267         {
268             _gx_vertical_list_down_wrap(list);
269         }
270     }
271 
272     _gx_window_scrollbar_find((GX_WINDOW *)list, GX_TYPE_VERTICAL_SCROLL, &scroll);
273 
274     if (scroll)
275     {
276         _gx_scrollbar_reset(scroll, GX_NULL);
277     }
278 
279     if (list -> gx_widget_status & GX_STATUS_VISIBLE)
280     {
281         block = list -> gx_window_client;
282 
283         if (!(list -> gx_widget_style & (GX_STYLE_WRAP | GX_STYLE_TRANSPARENT)) &&
284             (list -> gx_widget_style & GX_STYLE_BORDER_THIN))
285         {
286             child = _gx_widget_first_client_child_get((GX_WIDGET *)list);
287             last_child = _gx_widget_last_client_child_get((GX_WIDGET *)list);
288 
289             if (child)
290             {
291                 if ((child -> gx_widget_size.gx_rectangle_top > block.gx_rectangle_top ||
292                      last_child -> gx_widget_size.gx_rectangle_bottom < block.gx_rectangle_bottom))
293                 {
294                     /* If the widget has thin border, the round corder of the border
295                        will cover the client area, block move will cause trouble. */
296                     _gx_system_dirty_mark((GX_WIDGET *)list);
297                     do_block_move = GX_FALSE;
298                 }
299             }
300         }
301 
302         if (do_block_move)
303         {
304             _gx_widget_block_move((GX_WIDGET *)list, &block, 0, amount);
305         }
306     }
307 }
308 
309