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 /** Tree View Management (View) */
18 /** */
19 /**************************************************************************/
20
21 #define GX_SOURCE_CODE
22
23
24 /* Include necessary system files. */
25
26 #include "gx_api.h"
27 #include "gx_tree_view.h"
28 #include "gx_widget.h"
29 #include "gx_window.h"
30 #include "gx_system.h"
31 #include "gx_utility.h"
32
33 /**************************************************************************/
34 /* */
35 /* FUNCTION RELEASE */
36 /* */
37 /* _tree_view_selected_detect PORTABLE C */
38 /* 6.1 */
39 /* AUTHOR */
40 /* */
41 /* Kenneth Maxwell, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* Internal helper function to test if a menu contain selected item. */
46 /* */
47 /* INPUT */
48 /* */
49 /* tree Pointer to the tree view */
50 /* control block */
51 /* test_menu Menu for test */
52 /* */
53 /* OUTPUT */
54 /* */
55 /* status Completion status */
56 /* */
57 /* CALLS */
58 /* */
59 /* _tree_view_selected_detect Detect if a menu contain */
60 /* selected item */
61 /* */
62 /* CALLED BY */
63 /* */
64 /* _gx_tree_menu_pen_down_event_process */
65 /* */
66 /* RELEASE HISTORY */
67 /* */
68 /* DATE NAME DESCRIPTION */
69 /* */
70 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
71 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
72 /* resulting in version 6.1 */
73 /* */
74 /**************************************************************************/
_tree_view_selected_detect(GX_TREE_VIEW * tree,GX_MENU * test_menu)75 static GX_BOOL _tree_view_selected_detect(GX_TREE_VIEW *tree, GX_MENU *test_menu)
76 {
77 GX_MENU_LIST *list = &test_menu -> gx_menu_list;
78 GX_WIDGET *child;
79 GX_BOOL found = GX_FALSE;
80
81 if (!tree -> gx_tree_view_selected)
82 {
83 return found;
84 }
85 child = list -> gx_widget_first_child;
86
87 while (child)
88 {
89 if (child == tree -> gx_tree_view_selected)
90 {
91 found = GX_TRUE;
92 break;
93 }
94
95 if (child -> gx_widget_type == GX_TYPE_MENU)
96 {
97 found = _tree_view_selected_detect(tree, (GX_MENU *)child);
98
99 if (found)
100 {
101 break;
102 }
103 }
104 child = child -> gx_widget_next;
105 }
106
107 return found;
108 }
109
110 /**************************************************************************/
111 /* */
112 /* FUNCTION RELEASE */
113 /* */
114 /* _gx_tree_view_item_find PORTABLE C */
115 /* 6.1 */
116 /* AUTHOR */
117 /* */
118 /* Kenneth Maxwell, Microsoft Corporation */
119 /* */
120 /* DESCRIPTION */
121 /* */
122 /* Internal helper function to find a tree view item that is */
123 /* intersecting the specified horizontal line. */
124 /* */
125 /* INPUT */
126 /* */
127 /* start Widget to start searching */
128 /* line_y Y coordinate of the */
129 /* horizontal line */
130 /* returned_item Pointer to retrieved item */
131 /* */
132 /* OUTPUT */
133 /* */
134 /* status Completion status */
135 /* */
136 /* CALLS */
137 /* */
138 /* _gx_widget_first_client_child_get Get the first client child */
139 /* _gx_widget_next_client_child_get Get the next client child */
140 /* _gx_tree_view_item_find Find tree view item with test */
141 /* point */
142 /* */
143 /* CALLED BY */
144 /* */
145 /* _gx_tree_menu_pen_down_event_process */
146 /* */
147 /* RELEASE HISTORY */
148 /* */
149 /* DATE NAME DESCRIPTION */
150 /* */
151 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
152 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
153 /* resulting in version 6.1 */
154 /* */
155 /**************************************************************************/
_gx_tree_view_item_find(GX_WIDGET * start,GX_VALUE line_y,GX_WIDGET ** returned_item)156 static UINT _gx_tree_view_item_find(GX_WIDGET *start, GX_VALUE line_y, GX_WIDGET **returned_item)
157 {
158 UINT status = GX_SUCCESS;
159 GX_WIDGET *child;
160 GX_WIDGET *next;
161 GX_WIDGET *found = NULL;
162 GX_MENU_LIST *list;
163 GX_BOOL search_child;
164
165 child = _gx_widget_first_client_child_get((GX_WIDGET *)start);
166
167 while (child && child -> gx_widget_type == GX_TYPE_MENU_LIST)
168 {
169 child = _gx_widget_next_client_child_get(child);
170 }
171
172 while (child)
173 {
174 /* Pick up next client child. */
175 next = _gx_widget_next_client_child_get(child);
176
177 while (next && next -> gx_widget_type == GX_TYPE_MENU_LIST)
178 {
179 next = _gx_widget_next_client_child_get(next);
180 }
181
182 /* Check if the widget if intersecting the specified horizontal line. */
183 if ((line_y >= child -> gx_widget_size.gx_rectangle_top) &&
184 (line_y <= child -> gx_widget_size.gx_rectangle_bottom))
185 {
186 /* Line y is in current item. */
187 found = child;
188 break;
189 }
190
191 search_child = GX_FALSE;
192
193 if (next)
194 {
195 if (line_y < next -> gx_widget_size.gx_rectangle_top)
196 {
197 search_child = GX_TRUE;
198 }
199 else if (line_y <= next -> gx_widget_size.gx_rectangle_bottom)
200 {
201 /* Line y is inside next item. */
202 found = next;
203 break;
204 }
205 }
206 else
207 {
208 search_child = GX_TRUE;
209 }
210
211 if (search_child)
212 {
213 /* Line y is before next item. */
214 if (child -> gx_widget_type == GX_TYPE_MENU)
215 {
216 list = &((GX_MENU *)child) -> gx_menu_list;
217
218 if (list -> gx_widget_status & GX_STATUS_VISIBLE)
219 {
220 child = list -> gx_widget_first_child;
221 continue;
222 }
223 }
224 else
225 {
226 /* Not found. */
227 break;
228 }
229 }
230
231 child = next;
232 }
233
234 *returned_item = found;
235
236 return status;
237 }
238
239 /**************************************************************************/
240 /* */
241 /* FUNCTION RELEASE */
242 /* */
243 /* _gx_tree_view_root_icon_rect_get PORTABLE C */
244 /* 6.1 */
245 /* AUTHOR */
246 /* */
247 /* Kenneth Maxwell, Microsoft Corporation */
248 /* */
249 /* DESCRIPTION */
250 /* */
251 /* Internal helper function to get the root icon draw area of a tree */
252 /* view item. */
253 /* */
254 /* INPUT */
255 /* */
256 /* tree Pointer to the tree view */
257 /* control block */
258 /* item Pointer to the tree view item */
259 /* whose root icon draw area is*/
260 /* retrieved */
261 /* root_rect Retrieved root icon draw area */
262 /* */
263 /* OUTPUT */
264 /* */
265 /* status Completion status */
266 /* */
267 /* CALLS */
268 /* */
269 /* _gx_widget_pixelmap_get Get pixelmap by supplied ID */
270 /* */
271 /* CALLED BY */
272 /* */
273 /* _gx_tree_menu_pen_down_event_process */
274 /* */
275 /* RELEASE HISTORY */
276 /* */
277 /* DATE NAME DESCRIPTION */
278 /* */
279 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
280 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
281 /* resulting in version 6.1 */
282 /* */
283 /**************************************************************************/
_gx_tree_view_root_icon_rect_get(GX_TREE_VIEW * tree,GX_WIDGET * item,GX_RECTANGLE * root_rect)284 static UINT _gx_tree_view_root_icon_rect_get(GX_TREE_VIEW *tree, GX_WIDGET *item, GX_RECTANGLE *root_rect)
285 {
286 GX_PIXELMAP *map;
287
288 _gx_widget_pixelmap_get((GX_WIDGET *)tree, tree -> gx_tree_view_expand_pixelmap_id, &map);
289
290 if (map)
291 {
292 root_rect -> gx_rectangle_left = (GX_VALUE)(item -> gx_widget_size.gx_rectangle_left - (tree -> gx_tree_view_indentation >> 1));
293 root_rect -> gx_rectangle_left = (GX_VALUE)(root_rect -> gx_rectangle_left - (map -> gx_pixelmap_width >> 1));
294 root_rect -> gx_rectangle_top = (GX_VALUE)(item -> gx_widget_size.gx_rectangle_top + item -> gx_widget_size.gx_rectangle_bottom - 1);
295 root_rect -> gx_rectangle_top = (GX_VALUE)(root_rect -> gx_rectangle_top >> 1);
296 root_rect -> gx_rectangle_top = (GX_VALUE)(root_rect -> gx_rectangle_top - (map -> gx_pixelmap_height >> 1));
297 root_rect -> gx_rectangle_right = (GX_VALUE)((root_rect -> gx_rectangle_left + map -> gx_pixelmap_width - 1));
298 root_rect -> gx_rectangle_bottom = (GX_VALUE)((root_rect -> gx_rectangle_top + map -> gx_pixelmap_height - 1));
299 }
300 else
301 {
302 memset(root_rect, 0, sizeof(GX_RECTANGLE));
303 }
304
305 return GX_SUCCESS;
306 }
307
308 /**************************************************************************/
309 /* */
310 /* FUNCTION RELEASE */
311 /* */
312 /* _gx_tree_menu_pen_down_event_process PORTABLE C */
313 /* 6.1 */
314 /* AUTHOR */
315 /* */
316 /* Kenneth Maxwell, Microsoft Corporation */
317 /* */
318 /* DESCRIPTION */
319 /* */
320 /* Internal helper function to process pen down event for the tree */
321 /* view. */
322 /* */
323 /* INPUT */
324 /* */
325 /* tree Pointer to tree view control */
326 /* block */
327 /* event_ptr Pointer to event to process */
328 /* */
329 /* OUTPUT */
330 /* */
331 /* status Completion status */
332 /* */
333 /* CALLS */
334 /* */
335 /* _gx_tree_view_item_find Find tree view item with test */
336 /* point */
337 /* _gx_tree_view_root_icon_rect_get Retrieve root icon rectangle */
338 /* of specified tree view item */
339 /* _tree_view_selected_detect Detect if a menu contain */
340 /* selected item */
341 /* _gx_tree_view_selected_set Set new selected item */
342 /* _gx_tree_view_position Position a tree view */
343 /* _gx_system_dirty_partial_add Mark partial area of a widget */
344 /* as dirty */
345 /* _gx_system_top_widget_find Find top widget that contain */
346 /* test point */
347 /* _gx_utility_rectangle_point_detect Test if the point is resides */
348 /* in the rectangle. */
349 /* _gx_window_event_process Default window event process */
350 /* */
351 /* CALLED BY */
352 /* */
353 /* _gx_tree_view_event_process */
354 /* */
355 /* RELEASE HISTORY */
356 /* */
357 /* DATE NAME DESCRIPTION */
358 /* */
359 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
360 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
361 /* resulting in version 6.1 */
362 /* */
363 /**************************************************************************/
_gx_tree_menu_pen_down_event_process(GX_TREE_VIEW * tree,GX_EVENT * event_ptr)364 static VOID _gx_tree_menu_pen_down_event_process(GX_TREE_VIEW *tree, GX_EVENT *event_ptr)
365 {
366 GX_WIDGET *widget = (GX_WIDGET *)tree;
367 GX_WIDGET *child = GX_NULL;
368 GX_RECTANGLE root_rect;
369 GX_POINT *point;
370 GX_BOOL check_selection = GX_FALSE;
371
372 point = &event_ptr -> gx_event_payload.gx_event_pointdata;
373
374 /* Find tree view item that is intersecting the horizontal line of the click point. */
375 _gx_tree_view_item_find((GX_WIDGET *)tree, point -> gx_point_y, &child);
376
377 if (child && (child -> gx_widget_type == GX_TYPE_MENU) &&
378 ((GX_MENU *)child) -> gx_menu_list.gx_widget_first_child)
379 {
380 _gx_tree_view_root_icon_rect_get(tree, child, &root_rect);
381
382 if (_gx_utility_rectangle_point_detect(&root_rect, *point))
383 {
384 if (child -> gx_widget_style & GX_STYLE_MENU_EXPANDED)
385 {
386 child -> gx_widget_style &= (ULONG)(~GX_STYLE_MENU_EXPANDED);
387
388 if (_tree_view_selected_detect(tree, (GX_MENU *)child))
389 {
390 _gx_tree_view_selected_set(tree, child);
391 }
392 }
393 else
394 {
395 child -> gx_widget_style |= GX_STYLE_MENU_EXPANDED;
396 }
397
398 _gx_tree_view_position(tree);
399
400 root_rect = tree -> gx_window_client;
401 if (!(tree -> gx_widget_style & GX_STYLE_TREE_VIEW_SHOW_ROOT_LINES))
402 {
403 root_rect.gx_rectangle_top = child -> gx_widget_size.gx_rectangle_top;
404 }
405
406 _gx_system_dirty_partial_add((GX_WIDGET *)tree, &root_rect);
407 }
408 else
409 {
410 check_selection = GX_TRUE;
411 }
412 }
413 else
414 {
415 check_selection = GX_TRUE;
416 _gx_window_event_process((GX_WINDOW *)widget, event_ptr);
417 }
418
419 if (check_selection)
420 {
421 child = _gx_system_top_widget_find((GX_WIDGET *)tree, *point, GX_STATUS_SELECTABLE);
422
423 if (child)
424 {
425 if ((child -> gx_widget_type == GX_TYPE_MENU) ||
426 (child -> gx_widget_parent -> gx_widget_type == GX_TYPE_MENU_LIST))
427 {
428 _gx_tree_view_selected_set(tree, child);
429 }
430 }
431 }
432 }
433
434 /**************************************************************************/
435 /* */
436 /* FUNCTION RELEASE */
437 /* */
438 /* _gx_tree_view_event_process PORTABLE C */
439 /* 6.1 */
440 /* AUTHOR */
441 /* */
442 /* Kenneth Maxwell, Microsoft Corporation */
443 /* */
444 /* DESCRIPTION */
445 /* */
446 /* This function processes an event for the tree menu. */
447 /* */
448 /* INPUT */
449 /* */
450 /* tree Pointer to tree menu control */
451 /* block */
452 /* event_ptr Pointer to event to process */
453 /* */
454 /* OUTPUT */
455 /* */
456 /* status Completion status */
457 /* */
458 /* CALLS */
459 /* */
460 /* _gx_window_event_process Default window event process */
461 /* _gx_tree_view_position Position the children of the */
462 /* tree view */
463 /* _gx_tree_menu_pen_down_event_process Handle pen down event */
464 /* _gx_tree_view_scroll Scroll tree view client area */
465 /* */
466 /* CALLED BY */
467 /* */
468 /* Application Code */
469 /* GUIX Internal Code */
470 /* */
471 /* RELEASE HISTORY */
472 /* */
473 /* DATE NAME DESCRIPTION */
474 /* */
475 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
476 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
477 /* resulting in version 6.1 */
478 /* */
479 /**************************************************************************/
_gx_tree_view_event_process(GX_TREE_VIEW * tree,GX_EVENT * event_ptr)480 UINT _gx_tree_view_event_process(GX_TREE_VIEW *tree, GX_EVENT *event_ptr)
481 {
482 switch (event_ptr -> gx_event_type)
483 {
484 case GX_EVENT_SHOW:
485 _gx_window_event_process((GX_WINDOW *)tree, event_ptr);
486 if (tree -> gx_widget_parent && (tree -> gx_widget_parent -> gx_widget_type != GX_TYPE_MENU_LIST))
487 {
488 _gx_tree_view_position(tree);
489 }
490 break;
491
492 case GX_EVENT_PEN_DOWN:
493 _gx_tree_menu_pen_down_event_process(tree, event_ptr);
494 break;
495
496 case GX_EVENT_VERTICAL_SCROLL:
497 _gx_tree_view_scroll(tree, 0, (GX_VALUE)(event_ptr -> gx_event_payload.gx_event_intdata[1] - event_ptr -> gx_event_payload.gx_event_intdata[0]));
498 break;
499
500 case GX_EVENT_HORIZONTAL_SCROLL:
501 _gx_tree_view_scroll(tree, (GX_VALUE)(event_ptr -> gx_event_payload.gx_event_intdata[1] - event_ptr -> gx_event_payload.gx_event_intdata[0]), 0);
502 break;
503
504 default:
505 return _gx_window_event_process((GX_WINDOW *)tree, event_ptr);
506 }
507
508 return(GX_SUCCESS);
509 }
510
511