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 /**   Horizontal 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_horizontal_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 left or right the horizontal list by pages.     */
45 /*                                                                        */
46 /*  INPUT                                                                 */
47 /*                                                                        */
48 /*    list                                  Horizontal 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_horizontal_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_horizontal_list_invisible_page_scroll(GX_HORIZONTAL_LIST * list,INT num_pages)72 static VOID _gx_horizontal_list_invisible_page_scroll(GX_HORIZONTAL_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_horizontal_list_total_columns - list -> gx_horizontal_list_top_index - list -> gx_horizontal_list_child_count);
85             maximum_scroll_pages /= list -> gx_horizontal_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_horizontal_list_top_index / list -> gx_horizontal_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_horizontal_list_top_index -= num_pages * list -> gx_horizontal_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_horizontal_list_top_index >= list -> gx_horizontal_list_total_columns)
130                 {
131                     list -> gx_horizontal_list_top_index -= list -> gx_horizontal_list_total_columns;
132                 }
133             }
134             else
135             {
136                 while (list -> gx_horizontal_list_top_index < 0)
137                 {
138                     list -> gx_horizontal_list_top_index += list -> gx_horizontal_list_total_columns;
139                 }
140             }
141         }
142     }
143 }
144 
145 /**************************************************************************/
146 /*                                                                        */
147 /*  FUNCTION                                               RELEASE        */
148 /*                                                                        */
149 /*    _gx_horizontal_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 /*    list                                  Horizontal list widget control*/
162 /*                                            block                       */
163 /*    amount                                Shifting value                */
164 /*                                                                        */
165 /*  OUTPUT                                                                */
166 /*                                                                        */
167 /*    None                                                                */
168 /*                                                                        */
169 /*  CALLS                                                                 */
170 /*                                                                        */
171 /*    _gx_window_client_scroll              Scroll the window client      */
172 /*    _gx_horizontal_list_left_wrap         Scroll the horizontal list    */
173 /*                                            to the left                 */
174 /*    _gx_horizontal_list_right_wrap        Scroll the horizontal list    */
175 /*                                            to the right                */
176 /*                                                                        */
177 /*  CALLED BY                                                             */
178 /*                                                                        */
179 /*    _gx_horizontal_list_event_process                                   */
180 /*                                                                        */
181 /*  RELEASE HISTORY                                                       */
182 /*                                                                        */
183 /*    DATE              NAME                      DESCRIPTION             */
184 /*                                                                        */
185 /*  05-19-2020     Kenneth Maxwell          Initial Version 6.0           */
186 /*  09-30-2020     Kenneth Maxwell          Modified comment(s),          */
187 /*                                            resulting in version 6.1    */
188 /*  08-02-2021     Ting Zhu                 Modified comment(s),          */
189 /*                                            fixed scroll overflow issue,*/
190 /*                                            resulting in version 6.1.8  */
191 /*                                                                        */
192 /**************************************************************************/
_gx_horizontal_list_scroll(GX_HORIZONTAL_LIST * list,INT amount)193 VOID _gx_horizontal_list_scroll(GX_HORIZONTAL_LIST *list, INT amount)
194 {
195 GX_WIDGET    *child;
196 GX_WIDGET    *last_child;
197 GX_RECTANGLE  block;
198 GX_SCROLLBAR *scroll;
199 GX_BOOL       do_block_move = GX_TRUE;
200 GX_BOOL       reuse_child_widgets = GX_FALSE;
201 INT           page_size;
202 INT           num_pages;
203 
204     if (!amount)
205     {
206         return;
207     }
208 
209     if ((list -> gx_horizontal_list_callback != GX_NULL) &&
210         (list -> gx_horizontal_list_visible_columns < list -> gx_horizontal_list_total_columns) &&
211         ((list -> gx_horizontal_list_child_count < list -> gx_horizontal_list_total_columns) || (list -> gx_widget_style & GX_STYLE_WRAP)))
212     {
213         reuse_child_widgets = GX_TRUE;
214 
215         /* Calculate the page size.  */
216         page_size = (list -> gx_horizontal_list_child_width * list -> gx_horizontal_list_child_count);
217 
218         if (!page_size)
219         {
220             return;
221         }
222 
223         /* Calculate the number of pages to be scrolled.  */
224         if (amount < 0)
225         {
226             num_pages = (amount + page_size) / page_size;
227         }
228         else
229         {
230             num_pages = (amount - page_size) / page_size;
231         }
232 
233         if (num_pages)
234         {
235 
236             /* Calculate the remainning scroll amount.  */
237             amount -= (num_pages * page_size);
238 
239             /* Scroll pages.  */
240             _gx_horizontal_list_invisible_page_scroll(list, num_pages);
241         }
242     }
243 
244     /* first shift my child widgets */
245     child = list -> gx_widget_first_child;
246 
247     while (child)
248     {
249         if (!(child -> gx_widget_status & GX_STATUS_NONCLIENT))
250         {
251             _gx_widget_scroll_shift(child, amount, 0, GX_TRUE);
252         }
253         child = child -> gx_widget_next;
254     }
255 
256 
257     /* now check to see if we need to wrap any child widgets */
258 
259     if (reuse_child_widgets)
260     {
261         /* this means we have fewer children than list rows, so we
262            need to move and re-use the child widgets
263          */
264         if (amount < 0)
265         {
266             _gx_horizontal_list_left_wrap(list);
267         }
268         else
269         {
270             _gx_horizontal_list_right_wrap(list);
271         }
272     }
273 
274     _gx_window_scrollbar_find((GX_WINDOW *)list, GX_TYPE_HORIZONTAL_SCROLL, &scroll);
275 
276     if (scroll)
277     {
278         _gx_scrollbar_reset(scroll, GX_NULL);
279     }
280 
281     if (list -> gx_widget_status & GX_STATUS_VISIBLE)
282     {
283         block = list -> gx_window_client;
284 
285         if (!(list -> gx_widget_style & (GX_STYLE_WRAP | GX_STYLE_TRANSPARENT)) &&
286             (list -> gx_widget_style & GX_STYLE_BORDER_THIN))
287         {
288             child = _gx_widget_first_client_child_get((GX_WIDGET *)list);
289             last_child = _gx_widget_last_client_child_get((GX_WIDGET *)list);
290 
291             if (child)
292             {
293                 if ((child -> gx_widget_size.gx_rectangle_left > block.gx_rectangle_left) ||
294                     (last_child -> gx_widget_size.gx_rectangle_right < block.gx_rectangle_right))
295                 {
296                     /* If the widget has thin border, the round corder of the border
297                        will cover the client area, block move will cause trouble. */
298                     _gx_system_dirty_mark((GX_WIDGET *)list);
299                     do_block_move = GX_FALSE;
300                 }
301             }
302         }
303 
304         if (do_block_move)
305         {
306             _gx_widget_block_move((GX_WIDGET *)list, &block, amount, 0);
307         }
308     }
309 }
310 
311