Ensure new children of a group are disabled if their parent is.
[AROS.git] / workbench / libs / muimaster / classes / group.c
blob9ddccf91aa97b2c8b55f503e51326e920e85e292
1 /*
2 Copyright 1999, David Le Corfec.
3 Copyright 2002-2015, The AROS Development Team.
4 All rights reserved.
6 $Id$
7 */
9 #include <exec/types.h>
11 #include <clib/alib_protos.h>
12 #include <proto/exec.h>
13 #include <proto/intuition.h>
14 #include <proto/utility.h>
15 #include <proto/graphics.h>
16 #include <proto/muimaster.h>
18 extern struct Library *MUIMasterBase;
20 #include "muimaster_intern.h"
21 #include "mui.h"
22 #include "support.h"
23 #include "prefs.h"
25 /* #define MYDEBUG 1 */
26 #include "debug.h"
28 #define ROUND(x) ((int)(x + 0.5))
29 #define IS_HIDDEN(obj) (! (_flags(obj) & MADF_SHOWME) \
30 || (_flags(obj) & MADF_BORDERGADGET))
32 /* Attributes filtered out in OM_SET, before OM_SET gets passed to children.
33 Tested with MUI under UAE/AOS.
35 notifyclass:
37 MUIA_HelpLine
38 MUIA_HelpNode
39 MUIA_ObjectID
40 MUIA_UserData
42 areaclass:
44 MUIA_ContextMenu
45 MUIA_ContextMenuTrigger
46 MUIA_ControlChar
47 MUIA_CycleChain
48 MUIA_Draggable
49 MUIA_FillArea
50 MUIA_Frame
51 MUIA_FrameTitle
52 MUIA_HorizWeight
53 MUIA_Pressed
54 MUIA_Selected
55 MUIA_ShortHelp
56 MUIA_ShowMe
57 MUIA_VertWeight
58 MUIA_Weight
62 /* Private attribute/method definitions */
63 #define MUIM_Group_Insert (MUIB_MUI|0x00424d34) /* MUI: V20 */
64 struct MUIP_Group_Insert
66 STACKED ULONG MethodID;
67 STACKED Object *obj;
68 STACKED Object *pred;
71 #define MUIA_Group_ChildCount 0x80420322 /* MUI: V20 isg LONG */
73 struct layout2d_elem
75 WORD min;
76 WORD max;
77 WORD dim;
78 ULONG weight;
82 struct MUI_GroupData
84 Object *family;
85 struct Hook *layout_hook;
86 ULONG flags;
87 ULONG columns;
88 ULONG rows;
89 struct layout2d_elem *row_infos;
90 struct layout2d_elem *col_infos;
91 LONG active_page;
92 ULONG horiz_spacing;
93 ULONG vert_spacing;
94 ULONG num_children;
95 ULONG num_visible_children; /* for horiz/vert group only */
96 ULONG horiz_weight_sum;
97 ULONG vert_weight_sum;
98 ULONG samesize_maxmin_horiz;
99 ULONG samesize_maxmin_vert;
100 ULONG update; /* for MUI_Redraw() 1 - do not redraw the frame
101 * 2 - the virtual pos has changed */
102 struct MUI_EventHandlerNode ehn;
103 LONG virt_offx, virt_offy; /* diplay offsets */
104 LONG old_virt_offx, old_virt_offy; /* Saved virtual positions,
105 * used for update == 2 */
106 LONG virt_mwidth, virt_mheight; /* The complete width */
107 LONG saved_minwidth, saved_minheight;
108 LONG dont_forward_get; /* Set temporarily to 1 so that the get method
109 * is not forwarded */
110 LONG dont_forward_methods; /* Set temporarily to 1, meaning that the
111 * methods are not forwarded to the group's
112 * children */
113 /* MUI4 group with tabs */
114 Object *titlegroup;
117 /* Note:
118 * The MUI4 feature of group with tabs is implemented based on behaviour of
119 * one application. What this application codes suggest it seems that passing
120 * MUIV_Frame_Register together with MUIA_Group_PageMode, TRUE activates this
121 * mode.
122 * In such mode, the first passed group is used to register tab "titles" and
123 * is always visible. The selection of object in this group selects the
124 * matching (by position) group to be displayed
127 #define GROUP_HORIZ (1<<1)
128 #define GROUP_SAME_WIDTH (1<<2)
129 #define GROUP_SAME_HEIGHT (1<<3)
130 #define GROUP_CHANGING (1<<4)
131 #define GROUP_PAGEMODE (1<<5)
132 #define GROUP_VIRTUAL (1<<6)
133 #define GROUP_HSPACING (1<<7)
134 #define GROUP_VSPACING (1<<8)
135 #define GROUP_CHANGED (1<<9)
138 /* During minmax calculations objects with a weight of 0 shall
139 be treated like they had identical min/def/max size, ie. fixed size.
141 During layout objects with 0 weight must be treated like fixed-sized
142 too, but for hgroups only in x direction, and for vgroups only in
143 y direction. I think ... */
145 #define w0_defwidth(x) (_hweight(x) ? _defwidth(x) : _minwidth(x))
146 #define w0_maxwidth(x) (_hweight(x) ? _maxwidth(x) : _minwidth(x))
148 #define w0_defheight(x) (_vweight(x) ? _defheight(x) : _minheight(x))
149 #define w0_maxheight(x) (_vweight(x) ? _maxheight(x) : _minheight(x))
151 static const int __version = 1;
152 static const int __revision = 1;
154 IPTR Group__MUIM_Show(struct IClass *cl, Object *obj,
155 struct MUIP_Show *msg);
156 IPTR Group__MUIM_Hide(struct IClass *cl, Object *obj,
157 struct MUIP_Hide *msg);
159 /*****************************************************************************/
160 /*****************************************************************************/
163 static ULONG Group_DispatchMsg(struct IClass *cl, Object *obj, Msg msg);
165 static void change_active_page(struct IClass *cl, Object *obj, LONG page)
167 struct MUI_GroupData *data = INST_DATA(cl, obj);
168 LONG newpage, num_children = data->num_children;
170 if (!(data->flags & GROUP_PAGEMODE))
171 return;
173 if (data->titlegroup != NULL)
174 num_children--;
176 switch (page)
178 case MUIV_Group_ActivePage_First:
179 newpage = 0;
180 break;
181 case MUIV_Group_ActivePage_Last:
182 newpage = num_children - 1;
183 break;
184 case MUIV_Group_ActivePage_Prev:
185 newpage = data->active_page - 1;
186 if (newpage == -1)
187 newpage = num_children - 1;
188 break;
189 case MUIV_Group_ActivePage_Next:
190 case MUIV_Group_ActivePage_Advance:
191 newpage = (data->active_page + 1) % num_children;
192 break;
193 default:
194 newpage = page;
195 break;
198 if (newpage != data->active_page)
200 if (_flags(obj) & MADF_CANDRAW)
201 Group__MUIM_Hide(cl, obj, NULL);
203 data->active_page = newpage;
205 if (_flags(obj) & MADF_CANDRAW)
207 DoMethod(obj, MUIM_Layout);
208 Group__MUIM_Show(cl, obj, NULL);
209 data->update = 1;
210 MUI_Redraw(obj, MADF_DRAWUPDATE);
213 if (data->titlegroup)
214 set(data->titlegroup, MUIA_Group_ActivePage, newpage);
218 /**************************************************************************
219 Returns the number of visible children. Visible children are all children
220 that have MADF_SHOWME and not MADF_BORDERGADGET set.
221 **************************************************************************/
222 static int Group_GetNumVisibleChildren(struct MUI_GroupData *data,
223 struct MinList *children)
225 int num_visible_children = data->num_children;
226 Object *cstate;
227 Object *child;
229 /* As there can be invisible children we have to subtract those from
230 * the total number of children */
231 cstate = (Object *) children->mlh_Head;
232 while ((child = NextObject(&cstate)))
234 if (IS_HIDDEN(child))
235 num_visible_children--;
237 return num_visible_children;
240 /**************************************************************************
241 Handles insertion of objects - works based on fact that IDs of all methods
242 that use it are the same for Group and Family and that struct share obj at
243 same offset.
244 **************************************************************************/
245 struct MUIP_StructWithObj
247 STACKED ULONG MethodID;
248 STACKED Object *obj;
251 static IPTR Group__MUIM_AddObject(struct IClass *cl, Object *obj, Msg msg)
253 struct MUI_GroupData *data = INST_DATA(cl, obj);
254 struct MUIP_StructWithObj *msgint = (struct MUIP_StructWithObj *)msg;
256 DoMethodA(data->family, (Msg) msg);
257 data->num_children++;
258 if ((data->flags & GROUP_CHANGING) != 0)
259 data->flags |= GROUP_CHANGED;
261 /* if we are in an application tree, propagate pointers */
262 if (muiNotifyData(obj)->mnd_GlobalInfo)
264 /* Only children of groups can have parents */
266 if ((_flags(obj) & MADF_INVIRTUALGROUP)
267 || (data->flags & GROUP_VIRTUAL))
269 _flags(msgint->obj) |= MADF_INVIRTUALGROUP;
272 DoMethod(msgint->obj, MUIM_ConnectParent, (IPTR) obj);
275 /* Ensure new children are disabled if their parent is */
276 if (XGET(obj, MUIA_Disabled))
277 nnset(obj, MUIA_Disabled, TRUE);
279 /* Some apps (Odyssey) expect _parent() will work before group tree is added to application tree */
280 muiNotifyData(msgint->obj)->mnd_ParentObject = obj;
282 if (_flags(obj) & MADF_SETUP)
284 DoSetupMethod(msgint->obj, muiRenderInfo(obj));
286 /* if (_flags(obj) & MADF_CANDRAW) */
287 /* DoShowMethod(msg->opam_Object); */
289 return TRUE;
292 /**************************************************************************
293 OM_NEW - Constructor
294 **************************************************************************/
295 IPTR Group__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
297 struct MUI_GroupData *data;
298 struct TagItem *tags, *tag;
299 BOOL bad_children = FALSE;
300 IPTR disabled = FALSE;
301 IPTR frame = MUIV_Frame_None;
303 D(bug("[group.mui] OM_NEW, object 0x%p\n", obj));
305 obj = (Object *) DoSuperMethodA(cl, obj, (Msg) msg);
306 if (!obj)
307 return 0;
309 /* Initial local instance data */
310 data = INST_DATA(cl, obj);
312 data->family = MUI_NewObjectA(MUIC_Family, NULL);
313 if (!data->family)
315 CoerceMethod(cl, obj, OM_DISPOSE);
316 return 0;
319 data->horiz_spacing = -1;
320 data->vert_spacing = -1;
321 data->columns = 1;
322 data->rows = 1;
323 data->active_page = 0;
324 get(obj, MUIA_Frame, &frame);
326 /* parse initial taglist */
327 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
329 switch (tag->ti_Tag)
331 case MUIA_Group_Child:
332 D(bug("[group.mui] Adding child 0x%p\n", tag->ti_Data));
333 if (tag->ti_Data)
335 DoMethod(obj, OM_ADDMEMBER, tag->ti_Data);
336 /* Set first child as group title */
337 if ((frame == MUIV_Frame_Register)
338 && (data->titlegroup == NULL))
339 data->titlegroup = (Object *) tag->ti_Data;
341 else
342 bad_children = TRUE;
343 break;
345 case MUIA_Group_ActivePage:
346 change_active_page(cl, obj, (LONG) tag->ti_Data);
347 break;
349 case MUIA_Group_Columns:
350 data->columns = (tag->ti_Data) > 1 ? tag->ti_Data : 1;
351 data->rows = 0;
352 break;
354 case MUIA_Group_Horiz:
355 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_HORIZ);
356 break;
358 case MUIA_Group_HorizSpacing:
359 data->flags |= GROUP_HSPACING;
360 data->horiz_spacing = tag->ti_Data;
361 break;
363 case MUIA_Group_LayoutHook:
364 data->layout_hook = (struct Hook *)tag->ti_Data;
365 break;
367 case MUIA_Group_PageMode:
368 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_PAGEMODE);
369 break;
371 case MUIA_Group_Rows:
372 data->rows = MAX((ULONG) tag->ti_Data, 1);
373 data->columns = 0;
374 break;
376 case MUIA_Group_SameHeight:
377 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_HEIGHT);
378 break;
380 case MUIA_Group_SameSize:
381 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_HEIGHT);
382 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_WIDTH);
383 break;
385 case MUIA_Group_SameWidth:
386 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_WIDTH);
387 break;
389 case MUIA_Group_Spacing:
390 data->flags |= (GROUP_HSPACING | GROUP_VSPACING);
391 data->horiz_spacing = tag->ti_Data;
392 data->vert_spacing = tag->ti_Data;
393 break;
395 case MUIA_Group_VertSpacing:
396 data->flags |= GROUP_VSPACING;
397 data->vert_spacing = tag->ti_Data;
398 break;
400 case MUIA_Group_Virtual:
401 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_VIRTUAL);
402 break;
406 if (bad_children)
408 CoerceMethod(cl, obj, OM_DISPOSE);
409 return 0;
412 /* D(bug("Group_New(0x%lx)\n",obj)); */
414 if (data->flags & GROUP_VIRTUAL)
416 /* This is used by MUI_Render() to determine if group is virtual.
417 * It then installs a clip region.
418 * Also MUI_Layout() uses this. Probably for speed up reason */
419 _flags(obj) |= MADF_ISVIRTUALGROUP;
422 /* will forward MUIA_Disabled to children */
423 get(obj, MUIA_Disabled, &disabled);
424 if (disabled)
426 set(obj, MUIA_Disabled, TRUE);
429 /* This is only used for virtual groups */
430 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS; /* Will be filled on demand */
431 data->ehn.ehn_Priority = 10; /* Will hear the click before all
432 * other normal objects */
433 data->ehn.ehn_Flags = 0;
434 data->ehn.ehn_Object = obj;
435 data->ehn.ehn_Class = cl;
436 return (IPTR) obj;
439 /**************************************************************************
440 OM_DISPOSE
441 **************************************************************************/
442 IPTR Group__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
444 struct MUI_GroupData *data = INST_DATA(cl, obj);
446 if (data->row_infos != NULL)
447 mui_free(data->row_infos);
448 if (data->col_infos != NULL)
449 mui_free(data->col_infos);
450 if (data->family != NULL)
451 MUI_DisposeObject(data->family);
452 return DoSuperMethodA(cl, obj, msg);
455 /**************************************************************************
456 OM_SET
457 **************************************************************************/
458 IPTR Group__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
460 struct MUI_GroupData *data = INST_DATA(cl, obj);
461 struct TagItem *tags = msg->ops_AttrList;
462 struct TagItem *tag;
463 BOOL forward = TRUE;
464 BOOL need_recalc = FALSE;
465 IPTR retval;
467 int virt_offx = data->virt_offx, virt_offy = data->virt_offy;
469 /* There are many ways to find out what tag items provided by set()
470 ** we do know. The best way should be using NextTagItem() and simply
471 ** browsing through the list.
474 /* Parse group attributes before calling DoSuperMethodA(),
475 ** otherwise if an app for example sets up a notification
476 ** on MUIA_Group_ActivePage which calls a hook, and then
477 ** the hook function calls get(obj, MUIA_Group_ActivePage),
478 ** it would get returned the old active page instead of the new
479 ** active page
482 while ((tag = NextTagItem(&tags)) != NULL)
484 switch (tag->ti_Tag)
486 case MUIA_Group_Columns:
487 data->columns = MAX((ULONG) tag->ti_Data, 1);
488 data->rows = 0;
489 need_recalc = TRUE;
490 break;
491 case MUIA_Group_ActivePage:
492 change_active_page(cl, obj, (LONG) tag->ti_Data);
493 break;
494 case MUIA_Group_Forward:
495 forward = tag->ti_Data;
496 break;
497 case MUIA_Group_HorizSpacing:
498 data->flags |= GROUP_HSPACING;
499 data->horiz_spacing = tag->ti_Data;
500 break;
501 case MUIA_Group_Rows:
502 data->rows = MAX((ULONG) tag->ti_Data, 1);
503 data->columns = 0;
504 need_recalc = TRUE;
505 break;
506 case MUIA_Group_Spacing:
507 data->flags |= (GROUP_HSPACING | GROUP_VSPACING);
508 data->horiz_spacing = tag->ti_Data;
509 data->vert_spacing = tag->ti_Data;
510 break;
511 case MUIA_Group_VertSpacing:
512 data->flags |= GROUP_VSPACING;
513 data->vert_spacing = tag->ti_Data;
514 break;
515 case MUIA_Virtgroup_Left:
516 //kprintf("set virtgroup_left: %d\n", tag->ti_Data);
517 virt_offx = tag->ti_Data;
518 break;
520 case MUIA_Group_LayoutHook:
522 [ach] Seems like MUI supports setting this attribute after
523 initialization, even though the documentation states
524 otherwise. At least some programs use it...
526 data->layout_hook = (struct Hook *)tag->ti_Data;
527 break;
529 case MUIA_Virtgroup_Top:
530 //kprintf("set virtgroup_top: %d\n", tag->ti_Data);
531 virt_offy = tag->ti_Data;
532 break;
537 if (muiRenderInfo(obj) && need_recalc)
538 DoMethod(_win(obj), MUIM_Window_RecalcDisplay, (IPTR) obj);
540 retval = DoSuperMethodA(cl, obj, (Msg) msg);
542 /* seems to be the documented behaviour, however it should be slow! */
544 if (forward)
546 /* Attributes which are to be filtered out, so that they are ignored
547 when OM_SET is passed to group's children */
549 tags = msg->ops_AttrList;
550 while ((tag = NextTagItem(&tags)) != NULL)
552 switch (tag->ti_Tag)
554 case MUIA_HelpLine:
555 case MUIA_HelpNode:
556 case MUIA_ObjectID:
557 case MUIA_UserData:
559 case MUIA_ContextMenu:
560 case MUIA_ContextMenuTrigger:
561 case MUIA_ControlChar:
562 case MUIA_CycleChain:
563 case MUIA_Draggable:
564 case MUIA_FillArea:
565 case MUIA_Group_ActivePage:
566 case MUIA_Frame:
567 case MUIA_FrameTitle:
568 case MUIA_HorizWeight:
569 case MUIA_Pressed:
570 case MUIA_ShortHelp:
571 case MUIA_ShowMe:
572 case MUIA_VertWeight:
573 case MUIA_Weight:
574 case MUIA_Virtgroup_Left:
575 case MUIA_Virtgroup_Top:
576 case MUIA_AppMessage:
577 tag->ti_Tag = TAG_IGNORE;
578 break;
579 case MUIA_Selected:
580 /* D(bug("Group_Set(%p) MUIA_Selected forwarded\n", obj)); */
581 /* tag->ti_Tag = TAG_IGNORE; */
582 break;
586 Group_DispatchMsg(cl, obj, (Msg) msg);
590 if (virt_offx != data->virt_offx || virt_offy != data->virt_offy)
592 if (_flags(obj) & MADF_CANDRAW)
593 Group__MUIM_Hide(cl, obj, NULL);
594 data->virt_offx = virt_offx;
595 data->virt_offy = virt_offy;
596 /* Relayout ourself, this will also relayout all the children */
597 DoMethod(obj, MUIM_Layout);
598 if (_flags(obj) & MADF_CANDRAW)
599 Group__MUIM_Show(cl, obj, NULL);
600 data->update = 2;
601 MUI_Redraw(obj, MADF_DRAWUPDATE);
604 return retval;
608 /**************************************************************************
609 OM_GET
610 **************************************************************************/
611 IPTR Group__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
613 /* small macro to simplify return value storage */
614 #define STORE *(msg->opg_Storage)
616 struct MUI_GroupData *data = INST_DATA(cl, obj);
618 switch (msg->opg_AttrID)
620 case MUIA_Version:
621 STORE = __version;
622 return 1;
623 case MUIA_Revision:
624 STORE = __revision;
625 return 1;
626 case MUIA_Group_ActivePage:
627 STORE = data->active_page;
628 return 1;
629 case MUIA_Group_ChildList:
630 return GetAttr(MUIA_Family_List, data->family, msg->opg_Storage);
631 case MUIA_Group_Horiz:
632 STORE = (data->flags & GROUP_HORIZ);
633 return 1;
634 case MUIA_Group_HorizSpacing:
635 STORE = data->horiz_spacing;
636 return 1;
637 case MUIA_Group_VertSpacing:
638 STORE = data->vert_spacing;
639 return 1;
640 case MUIA_Group_ChildCount:
641 STORE = data->num_children;
642 return 1;
643 case MUIA_Virtgroup_Left:
644 STORE = data->virt_offx;
645 return 1;
646 case MUIA_Virtgroup_Top:
647 STORE = data->virt_offy;
648 return 1;
649 case MUIA_Virtgroup_Width:
650 STORE = data->virt_mwidth;
651 return 1;
652 case MUIA_Virtgroup_Height:
653 STORE = data->virt_mheight;
654 return 1;
655 case MUIA_Virtgroup_MinWidth:
656 STORE = data->saved_minwidth;
657 return 1;
658 case MUIA_Virtgroup_MinHeight:
659 STORE = data->saved_minheight;
660 return 1;
663 /* our handler didn't understand the attribute, we simply pass
664 ** it to our superclass now
666 if (DoSuperMethodA(cl, obj, (Msg) msg))
667 return 1;
669 /* seems to be the documented behaviour, however it should be slow! */
670 if (!data->dont_forward_get && !data->dont_forward_methods)
672 Object *cstate;
673 Object *child;
674 struct MinList *ChildList = NULL;
676 get(data->family, MUIA_Family_List, &(ChildList));
677 cstate = (Object *) ChildList->mlh_Head;
678 while ((child = NextObject(&cstate)))
679 if (DoMethodA(child, (Msg) msg))
680 return 1;
682 return 0;
683 #undef STORE
687 /**************************************************************************
688 MUIM_AddTail
689 **************************************************************************/
690 IPTR Group__MUIM_AddTail(struct IClass *cl, Object *obj,
691 struct MUIP_Group_AddTail *msg)
693 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
696 /**************************************************************************
697 MUIM_AddHead
698 **************************************************************************/
699 IPTR Group__MUIM_AddHead(struct IClass *cl, Object *obj,
700 struct MUIP_Group_AddHead *msg)
702 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
705 /**************************************************************************
706 MUIM_Insert
707 **************************************************************************/
708 IPTR Group__MUIM_Insert(struct IClass *cl, Object *obj,
709 struct MUIP_Group_Insert *msg)
711 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
714 /**************************************************************************
715 MUIM_Remove
716 **************************************************************************/
717 IPTR Group__MUIM_Remove(struct IClass *cl, Object *obj,
718 struct MUIP_Group_Remove *msg)
720 struct MUI_GroupData *data = INST_DATA(cl, obj);
722 if (_flags(obj) & MADF_CANDRAW)
723 DoHideMethod(msg->obj);
724 if (_flags(obj) & MADF_SETUP)
725 DoMethod(msg->obj, MUIM_Cleanup);
726 if (muiNotifyData(obj)->mnd_GlobalInfo)
728 DoMethod(msg->obj, MUIM_DisconnectParent);
729 muiNotifyData(msg->obj)->mnd_ParentObject = NULL;
731 _flags(msg->obj) &= ~MADF_INVIRTUALGROUP;
734 if ((data->flags & GROUP_CHANGING) != 0)
735 data->flags |= GROUP_CHANGED;
736 data->num_children--;
737 DoMethodA(data->family, (Msg) msg);
739 return TRUE;
743 /**************************************************************************
744 MUIM_ConnectParent
745 **************************************************************************/
746 IPTR Group__MUIM_ConnectParent(struct IClass *cl, Object *obj,
747 struct MUIP_ConnectParent *msg)
749 struct MUI_GroupData *data = INST_DATA(cl, obj);
750 Object *cstate;
751 Object *child;
752 struct MinList *ChildList = NULL;
754 DoSuperMethodA(cl, obj, (Msg) msg);
756 get(data->family, MUIA_Family_List, &(ChildList));
757 cstate = (Object *) ChildList->mlh_Head;
758 while ((child = NextObject(&cstate)))
760 if ((_flags(obj) & MADF_INVIRTUALGROUP)
761 || (data->flags & GROUP_VIRTUAL))
763 _flags(child) |= MADF_INVIRTUALGROUP;
766 /* Only children of groups can have parents */
767 muiNotifyData(child)->mnd_ParentObject = obj;
769 DoMethod(child, MUIM_ConnectParent, (IPTR) obj);
771 return TRUE;
774 /**************************************************************************
775 MUIM_DisconnectParent
776 **************************************************************************/
777 IPTR Group__MUIM_DisconnectParent(struct IClass *cl, Object *obj,
778 struct MUIP_ConnectParent *msg)
780 struct MUI_GroupData *data = INST_DATA(cl, obj);
781 Object *cstate;
782 Object *child;
783 struct MinList *ChildList = NULL;
785 get(data->family, MUIA_Family_List, &(ChildList));
786 cstate = (Object *) ChildList->mlh_Head;
787 while ((child = NextObject(&cstate)))
789 DoMethodA(child, (Msg) msg);
790 muiNotifyData(child)->mnd_ParentObject = NULL;
791 _flags(child) &= ~MADF_INVIRTUALGROUP;
793 DoSuperMethodA(cl, obj, (Msg) msg);
794 return TRUE;
798 * Put group in exchange state
800 IPTR Group__MUIM_InitChange(struct IClass *cl, Object *obj,
801 struct MUIP_Group_InitChange *msg)
803 struct MUI_GroupData *data = INST_DATA(cl, obj);
805 data->flags &= ~GROUP_CHANGED;
806 data->flags |= GROUP_CHANGING;
807 return TRUE;
812 * Will recalculate display after dynamic adding/removing
814 IPTR Group__MUIM_ExitChange(struct IClass *cl, Object *obj,
815 struct MUIP_Group_ExitChange *msg)
817 struct MUI_GroupData *data = INST_DATA(cl, obj);
819 data->flags &= ~GROUP_CHANGING;
821 if (data->flags & GROUP_CHANGED)
823 data->flags &= ~GROUP_CHANGED;
825 if ((_flags(obj) & MADF_SETUP) && _win(obj))
827 Object *win = _win(obj);
828 Object *parent = obj;
830 /* CHECKME: Don't call RecalcDisplay if one of our parents is
831 in GROUP_CHANGING state to prevent crash with Zune prefs
832 program NListtree page because NList/NListtree when
833 killing tree images in MUIM_Cleanup uses InitChange/
834 ExitChange. Zune prefs program uses InitChange/ExitChange
835 when switching page -> nesting -> mess. */
837 while ((parent = _parent(parent)))
839 struct MUI_GroupData *pdata = INST_DATA(cl, parent);
841 if (parent == win)
842 break;
844 if (pdata->flags & GROUP_CHANGING)
846 return TRUE;
851 DoMethod(win, MUIM_Window_RecalcDisplay, (IPTR) obj);
855 return TRUE;
860 * Will recalculate display after dynamic adding/removing
862 IPTR Group__MUIM_ExitChange2(struct IClass *cl, Object *obj,
863 struct MUIP_Group_ExitChange2 *msg)
865 struct MUI_GroupData *data = INST_DATA(cl, obj);
867 if (data->flags & GROUP_CHANGING)
869 data->flags &= ~(GROUP_CHANGING | GROUP_CHANGED);
871 if ((_flags(obj) & MADF_SETUP) && _win(obj))
873 Object *win = _win(obj);
874 Object *parent = obj;
876 /* CHECKME: Don't call RecalcDisplay if one of our parents is
877 in GROUP_CHANGING state to prevent crash with Zune prefs
878 program NListtree page because NList/NListtree when
879 killing tree images in MUIM_Cleanup uses InitChange/
880 ExitChange. Zune prefs program uses InitChange/ExitChange
881 when switching page -> nesting -> mess. */
883 while ((parent = _parent(parent)))
885 struct MUI_GroupData *pdata = INST_DATA(cl, parent);
887 if (parent == win)
888 break;
890 if (pdata->flags & GROUP_CHANGING)
892 return TRUE;
897 DoMethod(win, MUIM_Window_RecalcDisplay, (IPTR) obj);
901 return TRUE;
906 * Sort the family
908 IPTR Group__MUIM_Sort(struct IClass *cl, Object *obj,
909 struct MUIP_Group_Sort *msg)
911 struct MUI_GroupData *data = INST_DATA(cl, obj);
913 /* modify message */
914 msg->MethodID = MUIM_Family_Sort;
916 DoMethodA(data->family, (APTR) msg);
918 /* restore original message */
919 msg->MethodID = MUIM_Group_Sort;
920 return TRUE;
923 /**************************************************************************
924 MUIM_Group_DoMethodNoForward
926 Executes the given method but does not forward it to the children
927 **************************************************************************/
928 IPTR Group__MUIM_DoMethodNoForward(struct IClass *cl, Object *obj,
929 struct MUIP_Group_DoMethodNoForward *msg)
931 struct MUI_GroupData *data = INST_DATA(cl, obj);
932 IPTR rc;
933 data->dont_forward_methods = 1; /* disable forwarding */
934 rc = DoMethodA(obj, (Msg) & msg->DoMethodID);
935 /* Probably doesn't work correctly on AROS? */
937 data->dont_forward_methods = 0;
938 return rc;
942 * Propagate a method to group children.
944 static ULONG Group_DispatchMsg(struct IClass *cl, Object *obj, Msg msg)
946 struct MUI_GroupData *data = INST_DATA(cl, obj);
947 Object *cstate;
948 Object *child;
949 struct MinList *ChildList = NULL;
951 if (data->dont_forward_methods)
952 return TRUE;
954 get(data->family, MUIA_Family_List, &(ChildList));
955 cstate = (Object *) ChildList->mlh_Head;
956 while ((child = NextObject(&cstate)))
958 DoMethodA(child, (Msg) msg);
960 return TRUE;
964 /**************************************************************************
965 MUIM_Setup
966 **************************************************************************/
967 IPTR Group__MUIM_Setup(struct IClass *cl, Object *obj,
968 struct MUIP_Setup *msg)
970 struct MUI_GroupData *data = INST_DATA(cl, obj);
971 Object *cstate;
972 Object *cstate_copy;
973 Object *child;
974 Object *childFailed;
975 struct MinList *ChildList = NULL;
977 if (!DoSuperMethodA(cl, obj, (Msg) msg))
978 return FALSE;
980 ASSERT_VALID_PTR(muiGlobalInfo(obj));
982 if (!(data->flags & GROUP_HSPACING))
983 data->horiz_spacing = muiGlobalInfo(obj)->mgi_Prefs->group_hspacing;
984 if (!(data->flags & GROUP_VSPACING))
985 data->vert_spacing = muiGlobalInfo(obj)->mgi_Prefs->group_vspacing;
986 get(data->family, MUIA_Family_List, &(ChildList));
987 cstate = cstate_copy = (Object *) ChildList->mlh_Head;
988 while ((child = NextObject(&cstate)))
990 #if 0 /* SHOWME affects only show/hide */
991 if (!(_flags(child) & MADF_SHOWME))
992 continue;
993 #endif
995 if (!DoSetupMethod(child, msg->RenderInfo))
997 /* Send MUIM_Cleanup to all objects that received MUIM_Setup.
999 childFailed = child;
1000 cstate = cstate_copy;
1001 while ((child = NextObject(&cstate)) && (child != childFailed))
1003 #if 0 /* SHOWME affects only show/hide */
1004 if (!(_flags(child) & MADF_SHOWME))
1005 continue;
1006 #endif
1007 DoMethod(child, MUIM_Cleanup);
1009 return FALSE;
1013 if (data->flags & GROUP_VIRTUAL)
1015 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
1016 (IPTR) & data->ehn);
1019 return TRUE;
1023 /**************************************************************************
1024 MUIM_Cleanup
1025 **************************************************************************/
1026 IPTR Group__MUIM_Cleanup(struct IClass *cl, Object *obj, Msg msg)
1028 struct MUI_GroupData *data = INST_DATA(cl, obj);
1029 Object *cstate;
1030 Object *child;
1031 struct MinList *ChildList = NULL;
1033 if (data->flags & GROUP_VIRTUAL)
1035 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
1036 (IPTR) & data->ehn);
1039 get(data->family, MUIA_Family_List, &(ChildList));
1040 cstate = (Object *) ChildList->mlh_Head;
1041 while ((child = NextObject(&cstate)))
1043 #if 0 /* SHOWME affects only show/hide */
1044 if (!(_flags(child) & MADF_SHOWME))
1045 continue;
1046 #endif
1047 DoMethodA(child, (Msg) msg);
1049 return DoSuperMethodA(cl, obj, (Msg) msg);
1054 /**************************************************************************
1055 MUIM_Draw - draw the group
1056 **************************************************************************/
1057 IPTR Group__MUIM_Draw(struct IClass *cl, Object *obj,
1058 struct MUIP_Draw *msg)
1060 struct MUI_GroupData *data = INST_DATA(cl, obj);
1061 Object *cstate;
1062 Object *child;
1063 struct MinList *ChildList = NULL;
1064 struct Rectangle group_rect; /* child_rect; */
1065 int page;
1066 struct Region *region = NULL;
1067 APTR clip = (APTR) - 1;
1069 /* D(bug("Group_Draw(%lx) %ldx%ldx%ldx%ld upd=%d page=%d\n", */
1070 /* obj,_left(obj),_top(obj),_right(obj),_bottom(obj), */
1071 /* data->update, data->active_page)); */
1072 /* D(bug("Group_Draw(%p) msg=0x%08lx flags=0x%08lx\n", */
1073 /* obj, msg->flags, _flags(obj))); */
1075 if (data->flags & GROUP_CHANGING)
1076 return FALSE;
1078 if (muiGlobalInfo(obj)->mgi_Prefs->window_redraw ==
1079 WINDOW_REDRAW_WITHOUT_CLEAR)
1081 region = NewRegion();
1082 if (region)
1084 struct Rectangle rect;
1086 rect.MinX = _left(obj);
1087 rect.MinY = _top(obj);
1088 rect.MaxX = _right(obj);
1089 rect.MaxY = _bottom(obj);
1091 OrRectRegion(region, &rect);
1092 page = -1;
1093 get(data->family, MUIA_Family_List, &(ChildList));
1094 cstate = (Object *) ChildList->mlh_Head;
1095 while ((child = NextObject(&cstate)))
1097 /*page++; */
1098 /* redraw problem with colorwheel in coloradjust register */
1099 if ((data->flags & GROUP_PAGEMODE)
1100 && ((page != data->active_page)
1101 && (child != data->titlegroup)))
1102 continue;
1104 if ((muiAreaData(child)->mad_Flags & MADF_CANDRAW)
1105 && (_width(child) > 0) && (_height(child) > 0))
1107 rect.MinX = MAX(_left(child), _mleft(obj));
1108 rect.MinY = MAX(_top(child), _mtop(obj));
1109 rect.MaxX = MIN(_right(child), _mright(obj));
1110 rect.MaxY = MIN(_bottom(child), _mbottom(obj));
1112 if ((rect.MaxX >= rect.MinX)
1113 && (rect.MaxY >= rect.MinY))
1115 ClearRectRegion(region, &rect);
1120 clip = MUI_AddClipRegion(muiRenderInfo(obj), region);
1124 DoSuperMethodA(cl, obj, (Msg) msg);
1126 if (region)
1128 MUI_RemoveClipRegion(muiRenderInfo(obj), clip);
1129 region = NULL;
1132 else
1134 DoSuperMethodA(cl, obj, (Msg) msg);
1136 /* D(bug("Group_Draw(%p) (after dsma) msg=0x%08lx flags=0x%08lx\n", */
1137 /* obj, msg->flags, _flags(obj))); */
1139 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 1)
1142 * update is set when changing active page of a page group
1143 * need to redraw background ourself
1145 DoMethod(obj, MUIM_DrawBackground,
1146 _mleft(obj), _mtop(obj), _mwidth(obj), _mheight(obj),
1147 _mleft(obj), _mtop(obj), 0);
1149 data->update = 0;
1151 else
1153 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 2)
1155 LONG left, top, right, bottom;
1156 LONG diff_virt_offx = data->virt_offx - data->old_virt_offx;
1157 LONG diff_virt_offy = data->virt_offy - data->old_virt_offy;
1158 struct Rectangle rect;
1159 struct Rectangle *clip_rect = &muiRenderInfo(obj)->mri_ClipRect;
1161 data->update = 0;
1163 if (!diff_virt_offx && !diff_virt_offy)
1165 return 1;
1168 /* sba: I don't know how MUI handle this but ScrollRasterBF() made problems when scrolling
1169 ** a (partly visible) virtual groups in a virtual group, because e.g. _mtop() is then
1170 ** smaller than the region. ScrollRasterBF() on AmigaOS then marks the complete region
1171 ** as damaged. Using ScrollWindowRaster() solved that problem but it flickers then.
1172 ** To avoid this we prevent that the scroll area is out of the region bounds.
1173 ** The region bounds are setted in MUI_Redraw() but should probably should go in the
1174 ** MUI's clip functions
1177 left = MAX(_mleft(obj), clip_rect->MinX);
1178 top = MAX(_mtop(obj), clip_rect->MinY);
1179 right = MIN(_mright(obj), clip_rect->MaxX);
1180 bottom = MIN(_mbottom(obj), clip_rect->MaxY);
1182 /* old code was
1183 ** ScrollRasterBF(_rp(obj), diff_virt_offx, diff_virt_offy, _mleft(obj), _mtop(obj), _mright(obj),_mbottom(obj));
1186 ScrollWindowRaster(_window(obj), diff_virt_offx, diff_virt_offy,
1187 left, top, right, bottom);
1189 if ((region = NewRegion()))
1191 if (diff_virt_offx)
1193 rect.MinY = top;
1194 rect.MaxY = bottom;
1196 if (diff_virt_offx > 0)
1198 rect.MinX = right - diff_virt_offx + 1;
1199 if (rect.MinX < left)
1200 rect.MinX = left;
1201 rect.MaxX = right;
1203 else
1205 rect.MinX = left;
1206 rect.MaxX = left - diff_virt_offx - 1;
1207 if (rect.MaxX > right)
1208 rect.MaxX = right;
1211 if (rect.MinX <= rect.MaxX)
1213 DoMethod(obj, MUIM_DrawBackground,
1214 rect.MinX, rect.MinY,
1215 rect.MaxX - rect.MinX + 1,
1216 rect.MaxY - rect.MinY + 1,
1217 rect.MinX, rect.MinY, 0);
1219 OrRectRegion(region, &rect);
1223 if (diff_virt_offy)
1225 rect.MinX = left;
1226 rect.MaxX = right;
1228 if (diff_virt_offy > 0)
1230 rect.MinY = bottom - diff_virt_offy + 1;
1231 if (rect.MinY < top)
1232 rect.MinY = top;
1233 rect.MaxY = bottom;
1235 else
1237 rect.MinY = top;
1238 rect.MaxY = top - diff_virt_offy - 1;
1239 if (rect.MaxY > bottom)
1240 rect.MaxY = bottom;
1242 if (rect.MinY <= rect.MaxY)
1244 DoMethod(obj, MUIM_DrawBackground,
1245 rect.MinX, rect.MinY,
1246 rect.MaxX - rect.MinX + 1,
1247 rect.MaxY - rect.MinY + 1,
1248 rect.MinX, rect.MinY, 0);
1250 OrRectRegion(region, &rect);
1256 else
1258 if (!(msg->flags & MADF_DRAWOBJECT)
1259 && !(msg->flags & MADF_DRAWALL))
1260 return TRUE;
1264 if (data->flags & GROUP_VIRTUAL && !region)
1266 /* Not really needed if MUI Draws all the objects, maybe that's
1267 * what DRAWALL is for??? */
1268 if ((region = NewRegion()))
1270 struct Rectangle rect;
1271 rect.MinX = _mleft(obj);
1272 rect.MinY = _mtop(obj);
1273 rect.MaxX = _mright(obj);
1274 rect.MaxY = _mbottom(obj);
1275 OrRectRegion(region, &rect);
1279 /* Add clipping region if we have one */
1280 if (region)
1281 clip = MUI_AddClipRegion(muiRenderInfo(obj), region);
1283 group_rect = muiRenderInfo(obj)->mri_ClipRect;
1284 page = -1;
1285 get(data->family, MUIA_Family_List, &(ChildList));
1286 cstate = (Object *) ChildList->mlh_Head;
1287 while ((child = NextObject(&cstate)))
1289 if (!(_flags(child) & MADF_SHOWME))
1290 continue;
1292 if (child != data->titlegroup)
1293 ++page;
1295 if ((data->flags & GROUP_PAGEMODE) && ((page != data->active_page)
1296 && (child != data->titlegroup)))
1298 continue;
1301 // msg->flags |= MADF_DRAWOBJECT; /* yup, do not forget */
1303 // child_rect.MinX = _left(child);
1304 // child_rect.MinY = _top(child);
1305 // child_rect.MaxX = _right(child);
1306 // child_rect.MaxY = _bottom(child);
1307 /* g_print("intersect: a=(%d, %d, %d, %d) b=(%d, %d, %d, %d)\n", */
1308 /* group_rect.x, group_rect.y, */
1309 /* group_rect.width, group_rect.height, */
1310 /* child_rect.x, child_rect.y, */
1311 /* child_rect.width, child_rect.height); */
1313 // if (gdk_rectangle_intersect(&group_rect, &child_rect,
1314 // &muiRenderInfo(obj)->mri_ClipRect))
1315 // DoMethodA(child, (Msg)msg);
1316 /* if (((msg->flags & MADF_DRAWUPDATE) && data->update) */
1317 /* || (data->flags & GROUP_PAGEMODE)) */
1318 MUI_Redraw(child, MADF_DRAWOBJECT);
1319 /* else */
1320 /* MUI_Redraw(child, msg->flags); */
1321 muiRenderInfo(obj)->mri_ClipRect = group_rect;
1322 /* g_print("set back clip to (%d, %d, %d, %d)\n", */
1323 /* group_rect.x, group_rect.y, group_rect.width, group_rect.height); */
1325 /* D(bug("Group_Draw(%p) end\n", obj)); */
1327 if (data->flags & GROUP_VIRTUAL && region && clip != (APTR) - 1)
1329 MUI_RemoveClipRegion(muiRenderInfo(obj), clip);
1332 data->old_virt_offx = data->virt_offx;
1333 data->old_virt_offy = data->virt_offy;
1334 data->update = 0;
1336 return TRUE;
1340 #define END_MINMAX() \
1341 tmp.MaxHeight = MAX(tmp.MaxHeight, tmp.MinHeight); \
1342 tmp.MaxWidth = MAX(tmp.MaxWidth, tmp.MinWidth); \
1343 tmp.DefHeight = CLAMP(tmp.DefHeight, tmp.MinHeight, tmp.MaxHeight); \
1344 tmp.DefWidth = CLAMP(tmp.DefWidth, tmp.MinWidth, tmp.MaxWidth); \
1345 msg->MinMaxInfo->MinWidth += tmp.MinWidth; \
1346 msg->MinMaxInfo->MinHeight += tmp.MinHeight; \
1347 msg->MinMaxInfo->MaxWidth += tmp.MaxWidth; \
1348 msg->MinMaxInfo->MaxHeight += tmp.MaxHeight; \
1349 msg->MinMaxInfo->DefWidth += tmp.DefWidth; \
1350 msg->MinMaxInfo->DefHeight += tmp.DefHeight;
1353 * MinMax calculation function. When this is called,
1354 * the children of your group have already been asked
1355 * about their min/max dimension so you can use their
1356 * dimensions to calculate yours.
1358 * Notes:
1359 * - Init minwidth and maxwidth with size needed for total child spacing.
1360 * - 1st pass to find maximum minimum width, to set minwidth of each child
1361 * if they should have the same width (for a row of buttons ...)
1362 * - Adjust minwidth w/o making object bigger than their max size.
1364 static void group_minmax_horiz(struct IClass *cl, Object *obj,
1365 struct MinList *children, struct MUIP_AskMinMax *msg)
1367 struct MUI_GroupData *data = INST_DATA(cl, obj);
1368 Object *cstate;
1369 Object *child;
1370 struct MUI_MinMax tmp;
1371 WORD maxminwidth = 0;
1372 BOOL found_nonzero_vweight = FALSE;
1374 tmp.MinHeight = 0;
1375 tmp.DefHeight = 0;
1376 tmp.MaxHeight = MUI_MAXMAX;
1377 if (data->num_visible_children > 0)
1379 tmp.MinWidth = tmp.DefWidth = tmp.MaxWidth =
1380 (data->num_visible_children - 1) * data->horiz_spacing;
1382 else
1384 tmp.MinWidth = tmp.DefWidth = 0;
1385 tmp.MaxWidth = MUI_MAXMAX;
1388 if (data->flags & GROUP_SAME_WIDTH)
1390 cstate = (Object *) children->mlh_Head;
1391 while ((child = NextObject(&cstate)))
1393 if (IS_HIDDEN(child))
1394 continue;
1395 maxminwidth = MAX(maxminwidth, _minwidth(child));
1399 data->samesize_maxmin_horiz = maxminwidth;
1400 /* D(bug("group_minmax_horiz(%p) : maxminwidth=%d\n", obj, maxminwidth)); */
1402 data->horiz_weight_sum = 0;
1403 cstate = (Object *) children->mlh_Head;
1404 while ((child = NextObject(&cstate)))
1406 WORD minwidth;
1408 if (IS_HIDDEN(child))
1409 continue;
1410 if (data->flags & GROUP_SAME_WIDTH)
1412 minwidth = MAX(maxminwidth, _minwidth(child));
1413 minwidth = MIN(minwidth, _maxwidth(child));
1415 else
1416 minwidth = _minwidth(child);
1417 tmp.MinWidth += minwidth;
1418 tmp.DefWidth += w0_defwidth(child);
1419 tmp.MaxWidth += w0_maxwidth(child);
1420 tmp.MaxWidth = MIN(tmp.MaxWidth, MUI_MAXMAX);
1421 tmp.MinHeight = MAX(tmp.MinHeight, _minheight(child));
1422 tmp.DefHeight = MAX(tmp.DefHeight, _defheight(child));
1424 if all children have null weight then maxheight=minheight
1425 if all but some children have null weights, the maxheight
1426 is the min of all maxheights
1428 tmp.MaxHeight = MIN(tmp.MaxHeight, _maxheight(child));
1429 data->horiz_weight_sum += _hweight(child);
1430 if (_vweight(child) > 0)
1432 found_nonzero_vweight = TRUE;
1435 if (!found_nonzero_vweight)
1437 tmp.MaxHeight = tmp.MinHeight;
1440 //if (data->flags & GROUP_VIRTUAL)
1442 //kprintf("# min %d x %d def %d x %d max %d x %d\n",
1443 // tmp.MinWidth, tmp.MinHeight,
1444 // tmp.DefWidth, tmp.DefHeight,
1445 // tmp.MaxWidth, tmp.MaxHeight);
1448 END_MINMAX();
1451 /* minmax calculation for vertical groups (see group_minmax_horiz)
1453 static void group_minmax_vert(struct IClass *cl, Object *obj,
1454 struct MinList *children, struct MUIP_AskMinMax *msg)
1456 struct MUI_GroupData *data = INST_DATA(cl, obj);
1457 Object *cstate;
1458 Object *child;
1459 struct MUI_MinMax tmp;
1460 WORD maxminheight = 0;
1461 BOOL found_nonzero_hweight = FALSE;
1463 tmp.MinWidth = 0;
1464 tmp.DefWidth = 0;
1465 tmp.MaxWidth = MUI_MAXMAX;
1466 if (data->num_visible_children > 0)
1468 tmp.MinHeight = tmp.DefHeight = tmp.MaxHeight =
1469 (data->num_visible_children - 1) * data->vert_spacing;
1471 else
1473 tmp.MinHeight = tmp.DefHeight = 0;
1474 tmp.MaxHeight = MUI_MAXMAX;
1477 if (data->flags & GROUP_SAME_HEIGHT)
1479 cstate = (Object *) children->mlh_Head;
1480 while ((child = NextObject(&cstate)))
1482 if (IS_HIDDEN(child))
1483 continue;
1484 maxminheight = MAX(maxminheight, _minheight(child));
1488 data->samesize_maxmin_vert = maxminheight;
1489 data->vert_weight_sum = 0;
1490 cstate = (Object *) children->mlh_Head;
1491 while ((child = NextObject(&cstate)))
1493 if (IS_HIDDEN(child))
1494 continue;
1496 if (data->flags & GROUP_SAME_HEIGHT)
1497 _minheight(child) = MIN(maxminheight, w0_maxheight(child));
1498 tmp.MinHeight += _minheight(child);
1499 tmp.DefHeight += w0_defheight(child);
1500 tmp.MaxHeight += w0_maxheight(child);
1501 tmp.MaxHeight = MIN(tmp.MaxHeight, MUI_MAXMAX);
1502 tmp.MinWidth = MAX(tmp.MinWidth, _minwidth(child));
1503 tmp.DefWidth = MAX(tmp.DefWidth, _defwidth(child));
1504 tmp.MaxWidth = MIN(tmp.MaxWidth, _maxwidth(child));
1505 data->vert_weight_sum += _vweight(child);
1506 if (_hweight(child) > 0)
1508 found_nonzero_hweight = TRUE;
1511 if (!found_nonzero_hweight)
1513 tmp.MaxWidth = tmp.MinWidth;
1516 END_MINMAX();
1520 static void
1521 minmax_2d_rows_pass(struct MUI_GroupData *data, struct MinList *children,
1522 struct MUI_MinMax *req, WORD maxmin_height, WORD maxdef_height)
1524 int i, j;
1525 Object *cstate;
1526 Object *child;
1528 /* do not rewind after the while, to process line by line */
1529 cstate = (Object *) children->mlh_Head;
1530 /* for each row */
1531 for (i = 0; i < data->rows; i++)
1533 /* calculate min and max height of this row */
1534 int min_h = 0, def_h = 0, max_h = MUI_MAXMAX;
1535 BOOL found_nonzero_vweight = FALSE;
1537 data->row_infos[i].weight = 0;
1539 j = 0;
1540 while ((child = NextObject(&cstate)))
1542 if (IS_HIDDEN(child))
1543 continue;
1544 if (data->flags & GROUP_SAME_HEIGHT)
1546 _minheight(child) = MIN(maxmin_height, w0_maxheight(child));
1547 _defheight(child) = MIN(maxdef_height, w0_maxheight(child));
1549 min_h = MAX(min_h, _minheight(child));
1550 def_h = MAX(def_h, w0_defheight(child));
1551 max_h = MIN(max_h, _maxheight(child));
1552 if (_vweight(child) > 0)
1554 found_nonzero_vweight = TRUE;
1555 data->row_infos[i].weight += _vweight(child);
1557 ++j;
1558 if ((j % data->columns) == 0)
1559 break;
1561 if (!found_nonzero_vweight)
1562 max_h = min_h;
1563 else
1564 max_h = MAX(max_h, min_h);
1565 /* D(bug("row %d : min_h=%d max_h=%d\n", i, min_h, max_h)); */
1567 data->row_infos[i].min = min_h;
1568 data->row_infos[i].max = max_h;
1569 data->vert_weight_sum += data->row_infos[i].weight;
1571 req->MinHeight += min_h;
1572 req->DefHeight += def_h;
1573 req->MaxHeight += max_h;
1574 if (req->MaxHeight > MUI_MAXMAX)
1575 req->MaxHeight = MUI_MAXMAX;
1580 static void
1581 minmax_2d_columns_pass(struct MUI_GroupData *data, struct MinList *children,
1582 struct MUI_MinMax *req, WORD maxmin_width, WORD maxdef_width)
1584 int i, j;
1585 Object *cstate;
1586 Object *child;
1588 for (i = 0; i < data->columns; i++)
1590 /* calculate min and max width of this column */
1591 int min_w = 0, def_w = 0, max_w = MUI_MAXMAX;
1592 BOOL found_nonzero_hweight = FALSE;
1594 data->col_infos[i].weight = 0;
1596 j = 0;
1597 /* process all children to get children on a column */
1598 cstate = (Object *) children->mlh_Head;
1599 while ((child = NextObject(&cstate)))
1601 if (IS_HIDDEN(child))
1602 continue;
1603 ++j;
1604 if (((j - 1) % data->columns) != i)
1605 continue;
1606 if (data->flags & GROUP_SAME_WIDTH)
1608 _minwidth(child) = MIN(maxmin_width, w0_maxwidth(child));
1609 _defwidth(child) = MIN(maxdef_width, w0_maxwidth(child));
1611 min_w = MAX(min_w, _minwidth(child));
1612 def_w = MAX(def_w, w0_defwidth(child));
1614 /* this handles the case of null weight children, which limit
1615 * the max size if they're alone, but not if they are with
1616 * non-null weight obj
1618 max_w = MIN(max_w, _maxwidth(child));
1619 if (_hweight(child) > 0)
1621 found_nonzero_hweight = TRUE;
1622 data->col_infos[i].weight += _hweight(child);
1625 if (!found_nonzero_hweight)
1626 max_w = min_w;
1627 else
1628 max_w = MAX(max_w, min_w);
1629 /* D(bug("col %d : min_w=%d max_w=%d\n", i, min_w, max_w)); */
1631 data->col_infos[i].min = min_w;
1632 data->col_infos[i].max = max_w;
1633 data->horiz_weight_sum += data->col_infos[i].weight;
1635 req->MinWidth += min_w;
1636 req->DefWidth += def_w;
1637 req->MaxWidth += max_w;
1638 if (req->MaxWidth > MUI_MAXMAX)
1639 req->MaxWidth = MUI_MAXMAX;
1643 static void
1644 group_minmax_2d(struct IClass *cl, Object *obj,
1645 struct MinList *children, struct MUIP_AskMinMax *msg)
1647 struct MUI_GroupData *data = INST_DATA(cl, obj);
1648 Object *cstate;
1649 Object *child;
1650 struct MUI_MinMax tmp;
1651 WORD maxmin_width;
1652 WORD maxmin_height;
1653 WORD maxdef_width;
1654 WORD maxdef_height;
1656 if (!data->columns)
1658 if (data->num_children % data->rows)
1660 data->columns = 1;
1661 data->rows = data->num_children;
1663 else
1664 data->columns = data->num_children / data->rows;
1666 else
1668 if (data->num_children % data->columns)
1670 data->rows = 1;
1671 data->columns = data->num_children;
1673 else
1674 data->rows = data->num_children / data->columns;
1677 if (data->columns < 1)
1678 data->columns = 1;
1679 if (data->rows < 1)
1680 data->rows = 1;
1682 if (data->row_infos != NULL)
1683 mui_free(data->row_infos);
1685 data->row_infos = mui_alloc(data->rows * sizeof(struct layout2d_elem));
1686 if (NULL == data->row_infos)
1687 return;
1689 if (data->col_infos != NULL)
1690 mui_free(data->col_infos);
1692 data->col_infos =
1693 mui_alloc(data->columns * sizeof(struct layout2d_elem));
1694 if (NULL == data->col_infos)
1695 return;
1697 data->horiz_weight_sum = 0;
1698 data->vert_weight_sum = 0;
1700 tmp.MinHeight = tmp.DefHeight = tmp.MaxHeight =
1701 (data->rows - 1) * data->vert_spacing;
1702 tmp.MinWidth = tmp.DefWidth = tmp.MaxWidth =
1703 (data->columns - 1) * data->horiz_spacing;
1704 /* get minimum dims if same dims for all children are needed */
1705 maxmin_width = 0;
1706 maxmin_height = 0;
1707 maxdef_width = 0;
1708 maxdef_height = 0;
1710 if ((data->flags & GROUP_SAME_WIDTH)
1711 || (data->flags & GROUP_SAME_HEIGHT))
1713 cstate = (Object *) children->mlh_Head;
1714 while ((child = NextObject(&cstate)))
1716 if (!(_flags(child) & MADF_SHOWME))
1717 continue;
1718 maxmin_width = MAX(maxmin_width, _minwidth(child));
1719 maxmin_height = MAX(maxmin_height, _minheight(child));
1720 maxdef_width = MAX(maxdef_width, w0_defwidth(child));
1721 maxdef_height = MAX(maxdef_height, w0_defheight(child));
1723 /* g_print("2d group: mminw=%d mminh=%d\n", */
1724 /* maxmin_width, maxmin_height); */
1726 if (data->flags & GROUP_SAME_HEIGHT)
1727 data->samesize_maxmin_vert = maxmin_height;
1728 else
1729 data->samesize_maxmin_vert = 0;
1731 if (data->flags & GROUP_SAME_WIDTH)
1732 data->samesize_maxmin_horiz = maxmin_width;
1733 else
1734 data->samesize_maxmin_horiz = 0;
1736 minmax_2d_rows_pass(data, children, &tmp, maxmin_height, maxdef_height);
1737 minmax_2d_columns_pass(data, children, &tmp, maxmin_width,
1738 maxdef_width);
1740 END_MINMAX();
1744 static void
1745 group_minmax_pagemode(struct IClass *cl, Object *obj,
1746 struct MinList *children, struct MUIP_AskMinMax *msg)
1748 Object *cstate;
1749 Object *child;
1750 struct MUI_GroupData *data = INST_DATA(cl, obj);
1751 struct MUI_MinMax tmp = { 0, 0, MUI_MAXMAX, MUI_MAXMAX, 0, 0 };
1753 cstate = (Object *) children->mlh_Head;
1755 D(bug("minmax_pagemode(%lx)\n", obj, tmp.DefWidth));
1757 while ((child = NextObject(&cstate)))
1759 if (!(_flags(child) & MADF_SHOWME))
1760 continue;
1762 if (child == data->titlegroup)
1763 continue;
1765 tmp.MinHeight = MAX(tmp.MinHeight, _minheight(child));
1766 D(bug("minmax_pagemode(%p) minh child = %d tmpmin=%d\n", obj,
1767 _minheight(child), tmp.MinHeight));
1768 tmp.MinWidth = MAX(tmp.MinWidth, _minwidth(child));
1769 tmp.MaxHeight = MIN(tmp.MaxHeight, w0_maxheight(child));
1770 tmp.MaxWidth = MIN(tmp.MaxWidth, w0_maxwidth(child));
1771 tmp.DefHeight = MAX(tmp.DefHeight,
1772 ((w0_defheight(child) <
1773 MUI_MAXMAX) ? w0_defheight(child) : tmp.DefHeight));
1774 tmp.DefWidth =
1775 MAX(tmp.DefWidth,
1776 ((w0_defwidth(child) <
1777 MUI_MAXMAX) ? w0_defwidth(child) : tmp.DefWidth));
1778 D(bug("minmax_pagemode(%lx) defw = %ld\n", obj, tmp.DefWidth));
1781 if (data->titlegroup)
1783 tmp.MinHeight += _minheight(data->titlegroup);
1784 tmp.MaxHeight += w0_maxheight(data->titlegroup);
1785 tmp.DefHeight += w0_defheight(data->titlegroup);
1788 END_MINMAX();
1791 /**************************************************************************
1792 MUIM_AskMinMax : ask children about min/max sizes, then
1793 either call a hook, or the builtin method, to calculate our minmax
1794 **************************************************************************/
1795 IPTR Group__MUIM_AskMinMax(struct IClass *cl, Object *obj,
1796 struct MUIP_AskMinMax *msg)
1798 struct MUI_GroupData *data = INST_DATA(cl, obj);
1799 struct MUI_LayoutMsg lm;
1800 struct MUIP_AskMinMax childMsg;
1801 struct MUI_MinMax childMinMax;
1802 Object *cstate;
1803 Object *child;
1804 LONG super_minwidth, super_minheight;
1807 * let our superclass first fill in its size with frame, inner spc etc ...
1809 DoSuperMethodA(cl, obj, (Msg) msg);
1810 super_minwidth = msg->MinMaxInfo->MinWidth;
1811 super_minheight = msg->MinMaxInfo->MinHeight;
1814 * Ask children
1816 childMsg.MethodID = msg->MethodID;
1817 childMsg.MinMaxInfo = &childMinMax;
1818 get(data->family, MUIA_Family_List, &(lm.lm_Children));
1820 cstate = (Object *) lm.lm_Children->mlh_Head;
1822 while ((child = NextObject(&cstate)))
1824 if (!(_flags(child) & MADF_SHOWME))
1825 /* BORDERGADGETs should handle this itself */
1826 continue;
1827 /* Ask child */
1828 DoMethodA(child, (Msg) & childMsg);
1829 /* D(bug("*** group %lx, child %lx min=%ld,%ld\n", */
1830 /* obj, child, childMinMax.MinWidth, childMinMax.MinHeight)); */
1831 __area_finish_minmax(child, childMsg.MinMaxInfo);
1835 * Use children infos to calculate group size
1837 if (data->flags & GROUP_PAGEMODE)
1839 D(bug("minmax_pagemode(%p) minh initial = %d\n", obj,
1840 msg->MinMaxInfo->MinHeight));
1841 group_minmax_pagemode(cl, obj, lm.lm_Children, msg);
1842 D(bug("minmax_pagemode(%p) minh = %d\n", obj,
1843 msg->MinMaxInfo->MinHeight));
1845 else if (data->layout_hook)
1847 lm.lm_Type = MUILM_MINMAX;
1848 CallHookPkt(data->layout_hook, obj, &lm);
1850 if (lm.lm_MinMax.MaxHeight < lm.lm_MinMax.MinHeight)
1851 lm.lm_MinMax.MaxHeight = lm.lm_MinMax.MinHeight;
1852 if (lm.lm_MinMax.DefHeight < lm.lm_MinMax.MinHeight)
1853 lm.lm_MinMax.DefHeight = lm.lm_MinMax.MinHeight;
1854 if (lm.lm_MinMax.MaxWidth < lm.lm_MinMax.MinWidth)
1855 lm.lm_MinMax.MaxWidth = lm.lm_MinMax.MinWidth;
1856 if (lm.lm_MinMax.DefWidth < lm.lm_MinMax.MinWidth)
1857 lm.lm_MinMax.DefWidth = lm.lm_MinMax.MinWidth;
1859 //kprintf("### min %d x %d def %d x %d max %d x %d\n",
1860 // msg->MinMaxInfo->MinWidth,
1861 // msg->MinMaxInfo->MinHeight,
1862 // msg->MinMaxInfo->DefWidth,
1863 // msg->MinMaxInfo->DefHeight,
1864 // msg->MinMaxInfo->MaxWidth,
1865 // msg->MinMaxInfo->MaxHeight);
1867 msg->MinMaxInfo->MinWidth += lm.lm_MinMax.MinWidth;
1868 msg->MinMaxInfo->MinHeight += lm.lm_MinMax.MinHeight;
1869 msg->MinMaxInfo->MaxWidth += lm.lm_MinMax.MaxWidth;
1870 if (msg->MinMaxInfo->MaxWidth > MUI_MAXMAX)
1871 msg->MinMaxInfo->MaxWidth = MUI_MAXMAX;
1872 msg->MinMaxInfo->MaxHeight += lm.lm_MinMax.MaxHeight;
1873 if (msg->MinMaxInfo->MaxHeight > MUI_MAXMAX)
1874 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1875 msg->MinMaxInfo->DefWidth += lm.lm_MinMax.DefWidth;
1876 msg->MinMaxInfo->DefHeight += lm.lm_MinMax.DefHeight;
1878 //kprintf("#### min %d x %d def %d x %d max %d x %d\n",
1879 // msg->MinMaxInfo->MinWidth,
1880 // msg->MinMaxInfo->MinHeight,
1881 // msg->MinMaxInfo->DefWidth,
1882 // msg->MinMaxInfo->DefHeight,
1883 // msg->MinMaxInfo->MaxWidth,
1884 // msg->MinMaxInfo->MaxHeight);
1887 else
1889 if ((data->rows == 1) && (data->columns == 1))
1891 data->num_visible_children =
1892 Group_GetNumVisibleChildren(data, lm.lm_Children);
1893 if (data->flags & GROUP_HORIZ)
1894 group_minmax_horiz(cl, obj, lm.lm_Children, msg);
1895 else
1896 group_minmax_vert(cl, obj, lm.lm_Children, msg);
1898 else
1900 group_minmax_2d(cl, obj, lm.lm_Children, msg);
1904 if (data->flags & GROUP_VIRTUAL)
1906 data->saved_minwidth = msg->MinMaxInfo->MinWidth;
1907 data->saved_minheight = msg->MinMaxInfo->MinHeight;
1908 msg->MinMaxInfo->MinWidth = super_minwidth + 2;
1909 msg->MinMaxInfo->MinHeight = super_minheight + 2;
1911 //kprintf("## min %d x %d def %d x %d max %d x %d\n",
1912 // msg->MinMaxInfo->MinWidth,
1913 // msg->MinMaxInfo->MinHeight,
1914 // msg->MinMaxInfo->DefWidth,
1915 // msg->MinMaxInfo->DefHeight,
1916 // msg->MinMaxInfo->MaxWidth,
1917 // msg->MinMaxInfo->MaxHeight);
1921 return 0;
1926 // enforce minmax constraint, but also update total growable/shrinkable weights
1927 // while we're at it
1928 static void Layout1D_minmax_constraint(WORD *sizep, WORD minsize,
1929 WORD maxsize, WORD *remainp, WORD *sizegrowp, WORD *sizeshrinkp,
1930 ULONG *weightgrowp, ULONG *weightshrinkp, UWORD weight, WORD samesize)
1932 WORD size = *sizep, remain = *remainp,
1933 sizegrow = *sizegrowp, sizeshrink = *sizeshrinkp;
1934 ULONG weightgrow = *weightgrowp, weightshrink = *weightshrinkp;
1936 /* D(bug("L1D_minmax_c size=%d min=%d max=%d w=%d ss=%d\n", */
1937 /* size, minsize, maxsize, */
1938 /* weight, samesize)); */
1940 if ((samesize > 0) && (weight == 0))
1942 remain += size - samesize;
1943 size = samesize;
1944 sizeshrink -= size;
1945 sizegrow -= size;
1948 if (size <= minsize) // too little
1950 remain += size - minsize;
1951 size = minsize;
1952 sizeshrink -= size;
1953 if (size == maxsize)
1954 sizegrow -= size;
1956 else if (size >= maxsize) // too big
1958 remain += size - maxsize;
1959 size = maxsize;
1960 sizegrow -= size;
1963 if (!((samesize > 0) && (weight == 0)))
1965 if (size < maxsize)
1966 weightgrow += weight;
1967 if (size > minsize)
1968 weightshrink += weight;
1971 *sizep = size;
1972 *remainp = remain;
1973 *sizegrowp = sizegrow;
1974 *sizeshrinkp = sizeshrink;
1975 *weightgrowp = weightgrow;
1976 *weightshrinkp = weightshrink;
1980 // redistribute excess size to growable child, or reduce size of a shrinkable
1981 // child
1982 static void Layout1D_redistribution(WORD *sizep, WORD minsize,
1983 WORD maxsize, WORD *remainp, WORD *sizegrowp, WORD *sizeshrinkp,
1984 ULONG *weightgrowp, ULONG *weightshrinkp, UWORD weight)
1986 WORD size = *sizep, remain = *remainp,
1987 sizegrow = *sizegrowp, sizeshrink = *sizeshrinkp;
1988 ULONG weightgrow = *weightgrowp, weightshrink = *weightshrinkp;
1990 if (weight == 0)
1991 return;
1993 if ((remain > 0) && (size < maxsize))
1995 LONG newsize;
1997 newsize = (sizegrow * weight + weightgrow / 2) / weightgrow;
1999 /* D(bug("newsize=%ld == size_growa=%ld * w=%ld / weight_grow=%d\n", */
2000 /* newsize, sizegrow, weight, weightgrow)); */
2002 /* take care of off-by-1 errors that may toggle remainder sign
2003 * by ensuring convergence to 0
2005 if (remain - newsize + size < 0)
2007 /* D(bug("adding remainder=%d => size = %d\n", */
2008 /* remain, size + remain)); */
2009 size += remain;
2010 remain = 0;
2012 else
2014 remain -= newsize - size;
2015 size = newsize;
2016 sizegrow -= size;
2017 weightgrow -= weight;
2020 else if ((remain < 0) && (size > minsize))
2022 LONG newsize;
2024 newsize = (sizeshrink * weight + weightshrink / 2) / weightshrink;
2026 /* D(bug("newsize=%ld == size_shrinkables=%ld * w=%ld " */
2027 /* "/ weight_shrinkables=%d\n", */
2028 /* newsize, sizeshrink, weight, weightshrink)); */
2030 if (remain - newsize + size > 0)
2032 /* D(bug("adding remainder=%d => size = %d\n", */
2033 /* remain, size + remain)); */
2034 size += remain;
2035 remain = 0;
2037 else
2039 remain -= newsize - size;
2040 size = newsize;
2041 sizeshrink -= size;
2042 weightshrink -= weight;
2046 *sizep = size;
2047 *remainp = remain;
2048 *sizegrowp = sizegrow;
2049 *sizeshrinkp = sizeshrink;
2050 *weightgrowp = weightgrow;
2051 *weightshrinkp = weightshrink;
2055 // 2 passes at most, less on average (0.5 or 1.5), each does
2056 // - a minmax clamping, evenutally adding to a remainder
2057 // (remainder = missing (underflow) or remaining (overflow) space compared
2058 // to ideal sizes where children fill the whole group)
2059 // - a redistribution of the remainder, by growing (pos. remainder) or
2060 // shrinking (neg. remainder) children able to support it.
2062 // Occasionnaly the first time the redistribution is done, the minmax
2063 // constraint can be broken, thus the extra pass to check and eventually
2064 // redistribute. The second redistribution never breaks minmax constraint
2065 // (there should be a mathematical proof, but feel free to prove me wrong
2066 // with an example)
2067 static void Layout1D_minmax_constraints_and_redistrib(struct MinList
2068 *children, WORD total_size, WORD remainder, WORD samesize,
2069 BOOL group_horiz)
2071 Object *cstate;
2072 Object *child;
2073 int i;
2075 for (i = 0; i < 2; i++)
2077 WORD size_growables = total_size;
2078 WORD size_shrinkables = total_size;
2079 ULONG weight_growables = 0;
2080 ULONG weight_shrinkables = 0;
2082 /* D(bug("start : rem=%ld, A=%ld, size_growables=%ld, " */
2083 /* "size_shrinkables=%ld\n", */
2084 /* remainder, total_size, size_growables, size_shrinkables)); */
2086 // minmax constraints
2087 cstate = (Object *) children->mlh_Head;
2088 while ((child = NextObject(&cstate)))
2090 /* WORD old_size; */
2092 if (IS_HIDDEN(child))
2093 continue;
2095 if (group_horiz)
2097 /* old_size = _width(child); */
2099 Layout1D_minmax_constraint(&_width(child), _minwidth(child),
2100 _maxwidth(child), &remainder, &size_growables,
2101 &size_shrinkables, &weight_growables,
2102 &weight_shrinkables, _hweight(child), samesize);
2104 /* D(bug("loop1 on %p : width=%d was %d, rem=%d, A=%d, " */
2105 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2106 /* child, _width(child), old_size, remainder, total_size, */
2107 /* size_growables, size_shrinkables, _hweight(child), */
2108 /* _minwidth(child), _maxwidth(child))); */
2110 else // ! group_horiz
2112 /* old_size = _height(child); */
2114 Layout1D_minmax_constraint(&_height(child),
2115 _minheight(child), _maxheight(child), &remainder,
2116 &size_growables, &size_shrinkables, &weight_growables,
2117 &weight_shrinkables, _vweight(child), samesize);
2119 /* D(bug("loop1 on %p : h=%ld was %d, rem=%d, A=%d, " */
2120 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2121 /* child, _height(child), old_size, remainder, total_size,*/
2122 /* size_growables, size_shrinkables, _vweight(child), */
2123 /* _minheight(child), _maxheight(child))); */
2124 } // if (group_horiz)
2125 } // while child, minmax constraints
2127 // mid-pass break
2128 if (remainder == 0)
2129 break;
2131 /* D(bug("mid : rem=%d, A=%d, size_grow=%d, size_shrink=%d, " */
2132 /* "wg=%ld, ws=%ld\n", remainder, total_size, size_growables, */
2133 /* size_shrinkables, weight_growables, weight_shrinkables)); */
2135 // distribute remaining space to possible candidates
2136 cstate = (Object *) children->mlh_Head;
2137 while (((child = NextObject(&cstate)) != NULL) && (remainder != 0))
2139 /* WORD old_size; */
2141 if (IS_HIDDEN(child))
2142 continue;
2144 if (group_horiz)
2146 /* old_size = _width(child); */
2148 Layout1D_redistribution(&_width(child), _minwidth(child),
2149 _maxwidth(child), &remainder, &size_growables,
2150 &size_shrinkables, &weight_growables,
2151 &weight_shrinkables, _hweight(child));
2153 /* D(bug("loop2 on %p : h=%d was %d, rem=%d, A=%d, " */
2154 /* "size_grow=%d, size_shrink=%d\n", child, */
2155 /* _width(child), old_size, remainder, total_size, */
2156 /* size_growables, size_shrinkables)); */
2158 else // ! group_horiz
2160 /* old_size = _height(child); */
2162 Layout1D_redistribution(&_height(child), _minheight(child),
2163 _maxheight(child), &remainder, &size_growables,
2164 &size_shrinkables, &weight_growables,
2165 &weight_shrinkables, _vweight(child));
2167 /* D(bug("loop2 on %p : h=%d was %d, rem=%d, A=%d, " */
2168 /* "size_grow=%d, size_shrink=%d\n", child, */
2169 /* _height(child), old_size, remainder, total_size, */
2170 /* size_growables, size_shrinkables)); */
2171 } // if (group_horiz)
2173 } // while child, redistribution
2175 /* if (remainder != 0) */
2176 /* { */
2177 /* D(bug("end : rem=%ld, A=%ld, size_grow=%ld, size_shrink=%ld\n", */
2178 /* remainder, total_size, size_growables, size_shrinkables)); */
2179 /* } */
2180 // dont break here if remainder == 0, some minmax constraints
2181 // may not be respected
2182 } // end for
2184 // to easily spot layout bugs, nothing like a (division by zero) exception
2185 /* if (remainder != 0) */
2186 /* { */
2187 /* ASSERT(remainder != 0); */
2188 /* D(bug("gonna crash, remainder = %d\n", remainder)); */
2189 /* remainder /= 0; */
2190 /* } */
2194 static void Layout1D_weight_constraint(WORD *total_sizep,
2195 WORD *total_init_sizep,
2196 ULONG *total_weightp, WORD *sizep, UWORD weight, WORD minsize)
2198 if (*total_weightp > 0)
2199 *sizep = (*total_sizep * weight + *total_weightp / 2)
2200 / *total_weightp;
2201 else
2202 *sizep = minsize;
2204 *total_weightp -= weight;
2205 *total_sizep -= *sizep;
2206 *total_init_sizep += *sizep;
2210 static void group_layout_vert(struct IClass *cl, Object *obj,
2211 struct MinList *children)
2213 struct MUI_GroupData *data = INST_DATA(cl, obj);
2214 Object *cstate;
2215 Object *child;
2216 ULONG total_weight;
2217 WORD remainder; /* must converge to 0 to successfully end layout */
2218 WORD total_size;
2219 WORD total_size_backup;
2220 WORD total_init_size; /* total size of the ideally sized children */
2221 WORD width;
2222 WORD left = 0;
2223 WORD top = 0;
2224 WORD layout_width;
2225 WORD layout_height;
2227 //kprintf("group_layout_vert: virtoff = %d,%d\n",
2228 // data->virt_offx, data->virt_offy);
2230 if (data->flags & GROUP_VIRTUAL)
2232 data->virt_mwidth =
2233 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2234 _maxwidth(obj) - _subwidth(obj));
2235 data->virt_mheight =
2236 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2237 _maxheight(obj) - _subheight(obj));
2239 layout_width = data->virt_mwidth;
2240 layout_height = data->virt_mheight;
2242 else
2244 layout_width = _mwidth(obj);
2245 layout_height = _mheight(obj);
2248 total_weight = data->vert_weight_sum;
2249 total_init_size = 0;
2250 total_size =
2251 layout_height - (data->num_visible_children -
2252 1) * data->vert_spacing;
2253 total_size_backup = total_size;
2255 /* D(bug("\nvert layout for %p, A=%d W=%ld\n", */
2256 /* obj, total_size, total_weight)); */
2258 // weight constraints
2259 // calculate ideal size for each object, and total ideal size
2260 cstate = (Object *) children->mlh_Head;
2261 while ((child = NextObject(&cstate)))
2263 if (IS_HIDDEN(child))
2264 continue;
2266 Layout1D_weight_constraint(&total_size, &total_init_size,
2267 &total_weight, &_height(child), _vweight(child),
2268 _minheight(child));
2269 /* D(bug("child %p : ideal=%d w=%ld\n", */
2270 /* child, _height(child), _vweight(child))); */
2271 } // while child, weight constraints
2273 total_size = total_size_backup;
2274 remainder = total_size - total_init_size;
2276 if (data->flags & GROUP_VIRTUAL)
2278 /* This is also true for non virtual groups, but if this would be the
2279 ** case then there is a bug in the layout function
2281 if (total_size < 0)
2282 total_size = 0;
2285 Layout1D_minmax_constraints_and_redistrib(children,
2286 total_size,
2287 remainder,
2288 (data->flags & GROUP_SAME_HEIGHT) ? data->samesize_maxmin_vert : 0,
2289 FALSE);
2291 // do the layout
2292 cstate = (Object *) children->mlh_Head;
2293 while ((child = NextObject(&cstate)))
2295 if (IS_HIDDEN(child))
2296 continue;
2298 width = MIN(_maxwidth(child), layout_width);
2299 width = MAX(width, _minwidth(child));
2300 left = (layout_width - width) / 2;
2302 /* D(bug("child %p -> layout %d x %d\n", */
2303 /* child, width, _height(child))); */
2304 if (!MUI_Layout(child, left, top, width, _height(child), 0))
2305 return;
2306 top += data->vert_spacing + _height(child);
2312 static void group_layout_horiz(struct IClass *cl, Object *obj,
2313 struct MinList *children)
2315 struct MUI_GroupData *data = INST_DATA(cl, obj);
2316 Object *cstate;
2317 Object *child;
2318 ULONG total_weight;
2319 WORD remainder; /* must converge to 0 to succesfully end layout */
2320 WORD total_size;
2321 WORD total_size_backup;
2322 WORD total_init_size; /* total size of the ideally sized children */
2323 WORD height;
2324 WORD top = 0;
2325 WORD left = 0;
2326 WORD layout_width;
2327 WORD layout_height;
2329 //kprintf("group_layout_horiz: virtoff = %d,%d\n",
2330 // data->virt_offx, data->virt_offy);
2332 if (data->flags & GROUP_VIRTUAL)
2334 data->virt_mwidth =
2335 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2336 _maxwidth(obj) - _subwidth(obj));
2337 data->virt_mheight =
2338 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2339 _maxheight(obj) - _subheight(obj));
2341 layout_width = data->virt_mwidth;
2342 layout_height = data->virt_mheight;
2344 //kprintf("group_layout_horiz: layoutsize %d x %d "
2345 // " virtsize %d x %d msize %d x %d\n",
2346 // layout_width, layout_height, data->virt_mwidth,
2347 // data->virt_mheight,
2348 // _mwidth(obj), _mheight(obj));
2350 else
2352 layout_width = _mwidth(obj);
2353 layout_height = _mheight(obj);
2356 total_weight = data->horiz_weight_sum;
2357 total_init_size = 0;
2358 total_size =
2359 layout_width - (data->num_visible_children -
2360 1) * data->horiz_spacing;
2361 total_size_backup = total_size;
2363 /* D(bug("\nhoriz layout for %p, A=%d W=%ld\n", */
2364 /* obj, total_size, total_weight)); */
2366 // weight constraints
2367 // calculate ideal size for each object, and total ideal size
2368 cstate = (Object *) children->mlh_Head;
2369 while ((child = NextObject(&cstate)))
2371 if (IS_HIDDEN(child))
2372 continue;
2374 Layout1D_weight_constraint(&total_size, &total_init_size,
2375 &total_weight, &_width(child), _hweight(child),
2376 _minwidth(child));
2377 /* D(bug("child %p : ideal=%d w=%ld\n", */
2378 /* child, _width(child), _hweight(child))); */
2379 } // while child, weight constraints
2381 total_size = total_size_backup;
2382 if (data->horiz_weight_sum > 0)
2383 remainder = total_size - total_init_size;
2384 else
2385 remainder = 0;
2387 if (data->flags & GROUP_VIRTUAL)
2389 /* This is also true for non virtual groups, but if this would be the
2390 ** case then there is a bug in the layout function
2392 if (total_size < 0)
2393 total_size = 0;
2396 Layout1D_minmax_constraints_and_redistrib(children,
2397 total_size,
2398 remainder,
2399 (data->flags & GROUP_SAME_WIDTH) ? data->samesize_maxmin_horiz : 0,
2400 TRUE);
2402 // do the layout
2403 cstate = (Object *) children->mlh_Head;
2404 while ((child = NextObject(&cstate)))
2406 if (IS_HIDDEN(child))
2407 continue;
2409 height = MIN(_maxheight(child), layout_height);
2410 height = MAX(height, _minheight(child));
2411 top = (layout_height - height) / 2;
2413 /* D(bug("child %p -> layout %d x %d\n", */
2414 /* child, _width(child), height)); */
2415 if (!MUI_Layout(child, left, top, _width(child), height, 0))
2416 return;
2417 left += data->horiz_spacing + _width(child);
2423 static void Layout2D_weight_constraint(struct MUI_GroupData *data,
2424 struct layout2d_elem *row_infos,
2425 struct layout2d_elem *col_infos,
2426 WORD total_size_height, WORD total_size_width,
2427 WORD *total_init_height, WORD *total_init_width)
2429 int i;
2430 ULONG total_weight_vert = data->vert_weight_sum;
2431 ULONG total_weight_horiz = data->horiz_weight_sum;
2433 *total_init_height = 0;
2434 *total_init_width = 0;
2436 /* calc row heights */
2437 for (i = 0; i < data->rows; i++)
2439 if (total_weight_vert > 0)
2440 row_infos[i].dim =
2441 (total_size_height * row_infos[i].weight +
2442 total_weight_vert / 2) / total_weight_vert;
2443 else
2444 row_infos[i].dim = row_infos[i].min;
2446 /* D(bug("l2 row %d : ideal = %d with w=%d, A=%d, W=%d\n", */
2447 /* i, row_infos[i].dim, */
2448 /* row_infos[i].weight, total_size_height, total_weight_vert)); */
2450 total_weight_vert -= row_infos[i].weight;
2451 total_size_height -= row_infos[i].dim;
2452 *total_init_height += row_infos[i].dim;
2455 /* calc columns widths */
2456 for (i = 0; i < data->columns; i++)
2458 if (total_weight_horiz)
2459 col_infos[i].dim =
2460 (total_size_width * col_infos[i].weight +
2461 total_weight_horiz / 2) / total_weight_horiz;
2462 else
2463 col_infos[i].dim = col_infos[i].min;
2465 /* D(bug("l2 col %d : ideal = %d with w=%d, A=%d, W=%d\n", */
2466 /* i, col_infos[i].dim, */
2467 /* col_infos[i].weight, total_size_width, total_weight_horiz)); */
2469 total_weight_horiz -= col_infos[i].weight;
2470 total_size_width -= col_infos[i].dim;
2471 *total_init_width += col_infos[i].dim;
2477 static void Layout2D_minmax_constraints_and_redistrib(struct layout2d_elem
2478 *infos, WORD nitems, WORD total_size, WORD samesize, WORD remainder)
2480 int j;
2482 /* D(bug("L2D mc&r n=%d A=%d ss=%d rem=%d\n", */
2483 /* nitems, total_size, samesize, remainder)); */
2485 for (j = 0; j < 2; j++)
2487 WORD size_growables = total_size;
2488 WORD size_shrinkables = total_size;
2489 ULONG weight_growables = 0;
2490 ULONG weight_shrinkables = 0;
2491 /* WORD old_size; */
2492 int i;
2494 // minmax constraints
2495 for (i = 0; i < nitems; i++)
2497 /* old_size = infos[i].dim; */
2499 /* D(bug("bef loop1 on %d : size=%d, rem=%d, A=%d, " */
2500 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2501 /* i, infos[i].dim, remainder, total_size, */
2502 /* size_growables, size_shrinkables, infos[i].weight, */
2503 /* infos[i].min, infos[i].max)); */
2505 Layout1D_minmax_constraint(&infos[i].dim, infos[i].min,
2506 infos[i].max, &remainder, &size_growables,
2507 &size_shrinkables, &weight_growables, &weight_shrinkables,
2508 infos[i].weight, samesize);
2510 /* D(bug("loop1 on %d : size=%d was %d, rem=%d, A=%d, " */
2511 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2512 /* i, infos[i].dim, old_size, remainder, total_size, */
2513 /* size_growables, size_shrinkables, infos[i].weight, */
2514 /* infos[i].min, infos[i].max)); */
2517 if (remainder == 0)
2518 break;
2520 for (i = 0; i < nitems; i++)
2522 /* old_size = infos[i].dim; */
2524 /* D(bug("bef loop2 on %d : size=%d, rem=%d, A=%d, " */
2525 /* "size_grow=%d, size_shrink=%d\n", i, */
2526 /* infos[i].dim, remainder, total_size, */
2527 /* size_growables, size_shrinkables)); */
2529 Layout1D_redistribution(&infos[i].dim, infos[i].min,
2530 infos[i].max, &remainder, &size_growables,
2531 &size_shrinkables, &weight_growables, &weight_shrinkables,
2532 infos[i].weight);
2534 /* D(bug("loop2 on %d : size=%d was %d, rem=%d, A=%d, " */
2535 /* "size_grow=%d, size_shrink=%d\n", i, */
2536 /* infos[i].dim, old_size, remainder, total_size, */
2537 /* size_growables, size_shrinkables)); */
2542 static void
2543 layout_2d_distribute_space(struct MUI_GroupData *data,
2544 struct layout2d_elem *row_infos,
2545 struct layout2d_elem *col_infos,
2546 struct MinList *children, LONG left_start, LONG top_start)
2548 Object *cstate;
2549 Object *child;
2550 LONG left;
2551 LONG top;
2552 LONG col_width;
2553 LONG row_height;
2554 int i, j;
2557 * pass 2 : distribute space
2559 cstate = (Object *) children->mlh_Head;
2560 top = top_start;
2561 /* for each row */
2562 for (i = 0; i < data->rows; i++)
2564 /* left start for child layout in this row */
2565 left = left_start;
2567 /* max height for children in this row */
2568 row_height = row_infos[i].dim;
2569 j = 0;
2570 /* for each column */
2571 while ((child = NextObject(&cstate)))
2573 LONG cleft;
2574 LONG ctop;
2575 LONG cwidth;
2576 LONG cheight;
2578 if (IS_HIDDEN(child))
2579 continue;
2580 /* max width for children in this column */
2581 col_width = col_infos[j].dim;
2583 /* center child if col width is bigger than child maxwidth */
2584 cwidth = MIN(_maxwidth(child), col_width);
2585 cwidth = MAX(cwidth, _minwidth(child));
2586 cleft = left + (col_width - cwidth) / 2;
2588 /* center child if row height is bigger than child maxheight */
2589 cheight = MIN(_maxheight(child), row_height);
2590 cheight = MAX(cheight, _minheight(child));
2591 ctop = top + (row_height - cheight) / 2;
2593 /* g_print("layout %d %d %d %d\n", cleft, ctop, cwidth, cheight); */
2594 /* D(bug("2DL/child %p -> layout %d x %d\n", */
2595 /* child, cwidth, cheight)); */
2596 if (!MUI_Layout(child, cleft, ctop, cwidth, cheight, 0))
2597 return;
2599 left += data->horiz_spacing + col_width;
2601 ++j;
2602 if ((j % data->columns) == 0)
2603 break;
2606 top += data->vert_spacing + row_height;
2613 * all children in the same row have the same maximum height
2614 * all children in the same column have the same maximum height
2615 * if a child maximum size is smaller than the biggest minimum size,
2616 * the chid will be centered in the remaining space.
2618 * for each row, determine its height allocation
2619 * weight ? the vertical weight of a row, if no fixed-height child
2620 * in the row, is the sum of all vertical weights of children
2621 * all row members will have the same height
2623 * for each column, determine its width allocation
2624 * all column members will have the same width
2626 /* Write a proper hook function */
2627 static void
2628 group_layout_2d(struct IClass *cl, Object *obj, struct MinList *children)
2630 struct MUI_GroupData *data = INST_DATA(cl, obj);
2631 WORD left_start = 0;
2632 WORD top_start = 0;
2633 WORD total_size_height =
2634 _mheight(obj) - (data->rows - 1) * data->vert_spacing;
2635 WORD total_size_width =
2636 _mwidth(obj) - (data->columns - 1) * data->horiz_spacing;
2637 WORD total_init_height;
2638 WORD total_init_width;
2639 WORD remainder_height;
2640 WORD remainder_width;
2641 WORD layout_width;
2642 WORD layout_height;
2644 if (data->rows == 0 || data->columns == 0)
2645 return;
2646 if (data->num_children % data->rows
2647 || data->num_children % data->columns)
2648 return;
2649 if (data->row_infos == NULL || data->col_infos == NULL)
2650 return;
2652 //kprintf("group_layout_horiz: virtoff = %d,%d\n",
2653 // data->virt_offx, data->virt_offy);
2655 if (data->flags & GROUP_VIRTUAL)
2657 data->virt_mwidth =
2658 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2659 _maxwidth(obj) - _subwidth(obj));
2660 data->virt_mheight =
2661 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2662 _maxheight(obj) - _subheight(obj));
2664 layout_width = data->virt_mwidth;
2665 layout_height = data->virt_mheight;
2667 else
2669 layout_width = _mwidth(obj);
2670 layout_height = _mheight(obj);
2673 total_size_height =
2674 layout_height - (data->rows - 1) * data->vert_spacing;
2675 total_size_width =
2676 layout_width - (data->columns - 1) * data->horiz_spacing;
2678 // fix left/top ?
2680 // weight constraints
2681 Layout2D_weight_constraint(data, data->row_infos, data->col_infos,
2682 total_size_height, total_size_width,
2683 &total_init_height, &total_init_width);
2685 remainder_height = total_size_height - total_init_height;
2686 remainder_width = total_size_width - total_init_width;
2688 Layout2D_minmax_constraints_and_redistrib(data->row_infos,
2689 data->rows, total_size_height,
2690 /* (data->flags & GROUP_SAME_HEIGHT) ? data->samesize_maxmin_vert : 0, */
2691 0, remainder_height);
2693 Layout2D_minmax_constraints_and_redistrib(data->col_infos,
2694 data->columns, total_size_width,
2695 /* (data->flags & GROUP_SAME_WIDTH) ? data->samesize_maxmin_horiz : 0, */
2696 0, remainder_width);
2698 layout_2d_distribute_space(data, data->row_infos, data->col_infos,
2699 children, left_start, top_start);
2703 /* Write a proper hook function */
2704 static void group_layout_pagemode(struct IClass *cl, Object *obj,
2705 struct MinList *children)
2707 struct MUI_GroupData *data = INST_DATA(cl, obj);
2708 Object *cstate;
2709 Object *child;
2710 WORD layout_width;
2711 WORD layout_height;
2712 int w, h, yoffset = 0;
2714 if (data->flags & GROUP_VIRTUAL)
2716 data->virt_mwidth =
2717 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2718 _maxwidth(obj) - _subwidth(obj));
2719 data->virt_mheight =
2720 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2721 _maxheight(obj) - _subheight(obj));
2723 layout_width = data->virt_mwidth;
2724 layout_height = data->virt_mheight;
2726 else
2728 layout_width = _mwidth(obj);
2729 layout_height = _mheight(obj);
2732 if (data->titlegroup)
2734 yoffset = _minheight(data->titlegroup);
2735 layout_height -= yoffset;
2738 cstate = (Object *) children->mlh_Head;
2739 while ((child = NextObject(&cstate)))
2741 w = MIN(layout_width, _maxwidth(child));
2742 h = MIN(layout_height, _maxheight(child));
2744 if (child == data->titlegroup)
2746 MUI_Layout(child, (layout_width - w) / 2, 0, w, yoffset, 0);
2748 else
2750 D(bug("PM/child %p -> layout %d x %d\n", child, w, h));
2751 MUI_Layout(child, (layout_width - w) / 2,
2752 yoffset + (layout_height - h) / 2, w, h, 0);
2758 /**************************************************************************
2759 MUIM_Layout
2760 Either use a given layout hook, or the builtin method.
2761 **************************************************************************/
2762 IPTR Group__MUIM_Layout(struct IClass *cl, Object *obj,
2763 struct MUIP_Layout *msg)
2765 struct MUI_GroupData *data = INST_DATA(cl, obj);
2766 struct MUI_LayoutMsg lm = { 0 };
2768 get(data->family, MUIA_Family_List, &(lm.lm_Children));
2769 if (data->flags & GROUP_PAGEMODE)
2771 group_layout_pagemode(cl, obj, lm.lm_Children);
2773 else if (data->layout_hook)
2775 lm.lm_Type = MUILM_LAYOUT;
2776 lm.lm_Layout.Width = _mwidth(obj);
2777 lm.lm_Layout.Height = _mheight(obj);
2779 CallHookPkt(data->layout_hook, obj, &lm);
2781 if (data->flags & GROUP_VIRTUAL)
2783 data->virt_mwidth = lm.lm_Layout.Width;
2784 data->virt_mheight = lm.lm_Layout.Height;
2787 else
2789 if ((data->rows == 1) && (data->columns == 1))
2791 if (data->flags & GROUP_HORIZ)
2792 group_layout_horiz(cl, obj, lm.lm_Children);
2793 else
2794 group_layout_vert(cl, obj, lm.lm_Children);
2796 else
2797 group_layout_2d(cl, obj, lm.lm_Children);
2800 if (data->flags & GROUP_VIRTUAL)
2802 WORD new_virt_offx, new_virt_offy;
2804 new_virt_offx = data->virt_offx;
2805 new_virt_offy = data->virt_offy;
2807 if (new_virt_offx + _mwidth(obj) > data->virt_mwidth)
2809 new_virt_offx = data->virt_mwidth - _mwidth(obj);
2811 if (new_virt_offx < 0)
2812 new_virt_offx = 0;
2814 if (new_virt_offy + _mheight(obj) > data->virt_mheight)
2816 new_virt_offy = data->virt_mheight - _mheight(obj);
2818 if (new_virt_offy < 0)
2819 new_virt_offy = 0;
2821 if (new_virt_offx != data->virt_offx)
2823 nfset(obj, MUIA_Virtgroup_Left, new_virt_offx);
2826 if (new_virt_offy != data->virt_offy)
2828 nfset(obj, MUIA_Virtgroup_Top, new_virt_offy);
2833 return 0;
2836 /**************************************************************************
2837 MUIM_Show
2838 **************************************************************************/
2839 IPTR Group__MUIM_Show(struct IClass *cl, Object *obj,
2840 struct MUIP_Show *msg)
2842 struct MUI_GroupData *data = INST_DATA(cl, obj);
2843 Object *cstate;
2844 Object *child;
2845 struct MinList *ChildList = NULL;
2847 /* If msg is NULL, we won't want that the super method actually gets
2848 * this call */
2849 if (msg)
2850 DoSuperMethodA(cl, obj, (Msg) msg);
2852 get(data->family, MUIA_Family_List, &(ChildList));
2853 cstate = (Object *) ChildList->mlh_Head;
2855 if (data->flags & GROUP_PAGEMODE)
2857 int page = 0;
2858 while ((child = NextObject(&cstate)))
2860 if (child == data->titlegroup)
2862 DoShowMethod(child);
2863 continue; /* Title group is not counted as page */
2866 if (page == data->active_page)
2868 DoShowMethod(child);
2869 break;
2871 page++;
2874 else
2876 while ((child = NextObject(&cstate)))
2878 if (!(data->flags & GROUP_VIRTUAL) ||
2879 IsObjectVisible(child, MUIMasterBase))
2881 if (_flags(child) & MADF_SHOWME)
2882 DoShowMethod(child);
2886 return TRUE;
2889 /**************************************************************************
2890 MUIM_Hide
2891 **************************************************************************/
2892 IPTR Group__MUIM_Hide(struct IClass *cl, Object *obj,
2893 struct MUIP_Hide *msg)
2895 struct MUI_GroupData *data = INST_DATA(cl, obj);
2896 Object *cstate;
2897 Object *child;
2898 struct MinList *ChildList = NULL;
2900 get(data->family, MUIA_Family_List, &(ChildList));
2901 cstate = (Object *) ChildList->mlh_Head;
2903 if (data->flags & GROUP_PAGEMODE)
2905 int page = 0;
2906 while ((child = NextObject(&cstate)))
2908 if (child == data->titlegroup)
2910 DoHideMethod(child);
2911 continue; /* Title group is not counted as page */
2914 if (page == data->active_page)
2916 DoHideMethod(child);
2917 break;
2919 page++;
2922 else
2924 while ((child = NextObject(&cstate)))
2926 if (_flags(child) & MADF_CANDRAW)
2927 DoHideMethod(child);
2931 /* If msg is NULL, we won't want that the super method actually gets
2932 * this call */
2933 if (msg)
2934 return DoSuperMethodA(cl, obj, (Msg) msg);
2935 return 1;
2939 * MUIM_FindUData : tests if the MUIA_UserData of the object
2940 * contains the given <udata> and returns the object pointer in this case.
2942 IPTR Group__MUIM_FindUData(struct IClass *cl, Object *obj,
2943 struct MUIP_FindUData *msg)
2945 struct MUI_GroupData *data = INST_DATA(cl, obj);
2947 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2948 return (IPTR) obj;
2950 return DoMethodA(data->family, (Msg) msg);
2955 * MUIM_GetUData : This method tests if the MUIA_UserData of the object
2956 * contains the given <udata> and gets <attr> to <storage> for itself
2957 * in this case.
2959 IPTR Group__MUIM_GetUData(struct IClass *cl, Object *obj,
2960 struct MUIP_GetUData *msg)
2962 struct MUI_GroupData *data = INST_DATA(cl, obj);
2964 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2966 get(obj, msg->attr, msg->storage);
2967 return TRUE;
2970 return DoMethodA(data->family, (Msg) msg);
2975 * MUIM_SetUData : This method tests if the MUIA_UserData of the object
2976 * contains the given <udata> and sets <attr> to <val> for itself in this case.
2978 IPTR Group__MUIM_SetUData(struct IClass *cl, Object *obj,
2979 struct MUIP_SetUData *msg)
2981 struct MUI_GroupData *data = INST_DATA(cl, obj);
2983 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2984 set(obj, msg->attr, msg->val);
2986 DoMethodA(data->family, (Msg) msg);
2987 return TRUE;
2992 * MUIM_SetUDataOnce : This method tests if the MUIA_UserData of the object
2993 * contains the given <udata> and sets <attr> to <val> for itself in this case.
2994 * Stop after the first udata found.
2996 IPTR Group__MUIM_SetUDataOnce(struct IClass *cl, Object *obj,
2997 struct MUIP_SetUData *msg)
2999 struct MUI_GroupData *data = INST_DATA(cl, obj);
3001 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
3003 set(obj, msg->attr, msg->val);
3004 return TRUE;
3006 return DoMethodA(data->family, (Msg) msg);
3009 /**************************************************************************
3010 MUIM_DragQueryExtented
3011 **************************************************************************/
3012 IPTR Group__MUIM_DragQueryExtended(struct IClass *cl, Object *obj,
3013 struct MUIP_DragQueryExtended *msg)
3015 struct MUI_GroupData *data = INST_DATA(cl, obj);
3016 Object *cstate;
3017 Object *child;
3018 Object *found_obj;
3019 struct MinList *ChildList = NULL;
3021 get(data->family, MUIA_Family_List, &(ChildList));
3022 cstate = (Object *) ChildList->mlh_Head;
3023 while ((child = NextObject(&cstate)))
3025 if (!(_flags(child) & MADF_CANDRAW))
3026 continue;
3028 if ((found_obj = (Object *) DoMethodA(child, (Msg) msg)))
3029 return (IPTR) found_obj;
3031 return DoSuperMethodA(cl, obj, (Msg) msg);
3034 /**************************************************************************
3035 MUIM_HandleEvent
3036 **************************************************************************/
3037 IPTR Group__MUIM_HandleEvent(struct IClass *cl, Object *obj,
3038 struct MUIP_HandleEvent *msg)
3040 struct MUI_GroupData *data = INST_DATA(cl, obj);
3042 /* check this, otherwise a superclass who has IDCMP_MOUSEBUTTONS
3043 eventhandler might call DoSuperMethod, and this function gets
3044 called even when he have not added any eventhandler */
3046 if ((data->flags & GROUP_VIRTUAL) && msg->imsg)
3048 switch (msg->imsg->Class)
3050 case IDCMP_MOUSEBUTTONS:
3051 /* For virtual groups */
3052 if (msg->imsg->Code == SELECTDOWN)
3054 if (_between(_mleft(obj), msg->imsg->MouseX, _mright(obj))
3055 && _between(_mtop(obj), msg->imsg->MouseY,
3056 _mbottom(obj)))
3058 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
3059 (IPTR) & data->ehn);
3060 data->ehn.ehn_Events |= IDCMP_INTUITICKS;
3061 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
3062 (IPTR) & data->ehn);
3065 else
3067 if (data->ehn.ehn_Events & IDCMP_INTUITICKS)
3069 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
3070 (IPTR) & data->ehn);
3071 data->ehn.ehn_Events &= ~IDCMP_INTUITICKS;
3072 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
3073 (IPTR) & data->ehn);
3076 break;
3078 case IDCMP_INTUITICKS:
3079 if (!(data->ehn.ehn_Events & IDCMP_INTUITICKS))
3080 break;
3082 if (!(_between(_mleft(obj), msg->imsg->MouseX, _mright(obj))
3083 && _between(_mtop(obj), msg->imsg->MouseY,
3084 _mbottom(obj))))
3086 LONG new_virt_offx = data->virt_offx;
3087 LONG new_virt_offy = data->virt_offy;
3089 if (msg->imsg->MouseX < _mleft(obj))
3091 /* scroll left */
3092 if (new_virt_offx >= 4)
3093 new_virt_offx -= 4;
3094 else
3095 new_virt_offx = 0;
3097 else if (msg->imsg->MouseX > _mright(obj))
3099 /* scroll right */
3100 new_virt_offx += 4;
3101 if (new_virt_offx > data->virt_mwidth - _mwidth(obj))
3102 new_virt_offx = data->virt_mwidth - _mwidth(obj);
3103 if (new_virt_offx < 0)
3104 new_virt_offx = 0;
3107 if (msg->imsg->MouseY < _mtop(obj))
3109 /* scroll top */
3110 if (new_virt_offy >= 4)
3111 new_virt_offy -= 4;
3112 else
3113 new_virt_offy = 0;
3115 else if (msg->imsg->MouseY > _mbottom(obj))
3117 /* scroll bottom */
3118 new_virt_offy += 4;
3119 if (new_virt_offy > data->virt_mheight - _mheight(obj))
3120 new_virt_offy = data->virt_mheight - _mheight(obj);
3121 if (new_virt_offy < 0)
3122 new_virt_offy = 0;
3125 if (new_virt_offx != data->virt_offx
3126 || new_virt_offy != data->virt_offy)
3128 SetAttrs(obj,
3129 MUIA_Virtgroup_Left, new_virt_offx,
3130 MUIA_Virtgroup_Top, new_virt_offy,
3131 MUIA_Group_Forward, FALSE, TAG_DONE);
3134 break;
3138 return 0;
3141 /**************************************************************************
3142 MUIM_DrawBackground
3143 **************************************************************************/
3144 IPTR Group__MUIM_DrawBackground(struct IClass *cl, Object *obj,
3145 struct MUIP_DrawBackground *msg)
3147 struct MUI_GroupData *data = INST_DATA(cl, obj);
3149 if (data->flags & GROUP_VIRTUAL)
3151 struct MUIP_DrawBackground msg2 = *msg;
3153 msg2.xoffset += data->virt_offx;
3154 msg2.yoffset += data->virt_offy;
3156 return DoSuperMethodA(cl, obj, (Msg) & msg2);
3159 return DoSuperMethodA(cl, obj, (Msg) msg);
3162 /**************************************************************************
3163 MUIM_FindAreaObject
3164 Find the given object or return NULL
3165 **************************************************************************/
3166 IPTR Group__MUIM_FindAreaObject(struct IClass *cl, Object *obj,
3167 struct MUIP_FindAreaObject *msg)
3169 struct MUI_GroupData *data = INST_DATA(cl, obj);
3170 Object *cstate;
3171 Object *child;
3172 struct MinList *ChildList = NULL;
3174 // it's me ?
3175 if (msg->obj == obj)
3176 return (IPTR) obj;
3178 // it's one of my children ?
3179 get(data->family, MUIA_Family_List, &(ChildList));
3180 cstate = (Object *) ChildList->mlh_Head;
3181 while ((child = NextObject(&cstate)))
3183 if (msg->obj == child)
3184 return (IPTR) child;
3187 // let the children find it
3188 get(data->family, MUIA_Family_List, &(ChildList));
3189 cstate = (Object *) ChildList->mlh_Head;
3190 while ((child = NextObject(&cstate)))
3192 Object *res = (Object *) DoMethodA(child, (Msg) msg);
3193 if (res != NULL)
3194 return (IPTR) res;
3197 return (IPTR) NULL;
3200 /**************************************************************************
3201 MUIM_Export : to export an object's "contents" to a dataspace object.
3202 **************************************************************************/
3203 static IPTR Group__MUIM_Export(struct IClass *cl, Object *obj,
3204 struct MUIP_Export *msg)
3206 struct MUI_GroupData *data = INST_DATA(cl, obj);
3207 Object *cstate;
3208 Object *child;
3209 struct MinList *ChildList = NULL;
3211 get(data->family, MUIA_Family_List, &(ChildList));
3212 if (!ChildList)
3213 return 0;
3215 cstate = (Object *) ChildList->mlh_Head;
3216 while ((child = NextObject(&cstate)))
3218 DoMethodA(child, (Msg) msg);
3221 return 0;
3225 /**************************************************************************
3226 MUIM_Import : to import an object's "contents" from a dataspace object.
3227 **************************************************************************/
3228 static IPTR Group__MUIM_Import(struct IClass *cl, Object *obj,
3229 struct MUIP_Import *msg)
3231 struct MUI_GroupData *data = INST_DATA(cl, obj);
3232 Object *cstate;
3233 Object *child;
3234 struct MinList *ChildList = NULL;
3236 get(data->family, MUIA_Family_List, &(ChildList));
3237 if (!ChildList)
3238 return 0;
3240 cstate = (Object *) ChildList->mlh_Head;
3241 while ((child = NextObject(&cstate)))
3243 DoMethodA(child, (Msg) msg);
3246 return 0;
3249 /**************************************************************************
3250 MUIM_Notify - disabled now because previous Zune versions had a OM_GET
3251 check in MUIM_Notify which is no longer the case
3252 **************************************************************************/
3253 #if 0
3254 STATIC IPTR Group_Notify(struct IClass *cl, Object *obj,
3255 struct MUIP_Notify *msg)
3257 struct MUI_GroupData *data = INST_DATA(cl, obj);
3258 Object *cstate;
3259 Object *child;
3260 struct MinList *ChildList;
3262 /* Try at first if understand the message our self
3263 ** We disable the forwarding of the OM_GET message
3264 ** as the MUIM_Notify otherwise would "think" that
3265 ** the group class actually understands the attribute
3266 ** although a child does this only
3268 data->dont_forward_get = 1;
3269 if (DoSuperMethodA(cl, obj, (Msg) msg))
3271 data->dont_forward_get = 0;
3272 return 1;
3275 /* We ourselves didn't understand the notify tag so we try the
3276 * children now */
3277 data->dont_forward_get = 0;
3279 get(data->family, MUIA_Family_List, (IPTR *) & (ChildList));
3280 cstate = (Object *) ChildList->mlh_Head;
3281 while ((child = NextObject(&cstate)))
3283 if (DoMethodA(child, (Msg) msg))
3284 return 1;
3286 return 0;
3288 #endif
3290 #if 1
3291 /* Notes about Group_Notify() and echo notification problem:
3292 It was discovered that MUI seems to have some special handling for group class
3293 which will drop notifications on the children which are found to not
3294 understand the attribute.
3296 This is done by checking if an OM_GET on the child returns TRUE.
3297 There's a little problem here because it is not known how big the storage
3298 needed for the attribute in question will be. Almost no class uses anything
3299 bigger than one IPTR. For "big" attributes those return a pointer to the data,
3300 not the data itself. Unfortuntely there are some exceptions like colorwheel
3301 class which does not return a pointer, but the data itself. So it's not
3302 enough to use one single IPTR variable (4 Bytes on 32bit machines, 8 bytes
3303 on 64 bit machines) to store the result of the test-OM_Get.
3305 There is no general way to query the size needed so if one wants to change
3306 Zune to work like MUI one needs to choose a size which one hopes will be
3307 big enough to hold all possible attributes of all classes, old, present
3308 and future ones.
3310 STATIC IPTR Group_Notify(struct IClass *cl, Object *obj,
3311 struct MUIP_Notify *msg)
3313 struct MUI_GroupData *data = INST_DATA(cl, obj);
3314 Object *cstate;
3315 Object *child;
3316 struct MinList *ChildList = NULL;
3317 IPTR attr[30];
3319 data->dont_forward_get = 1;
3321 if (GetAttr(msg->TrigAttr, obj, attr))
3323 data->dont_forward_get = 0;
3324 return DoSuperMethodA(cl, obj, (Msg) msg);
3326 data->dont_forward_get = 0;
3328 get(data->family, MUIA_Family_List, &(ChildList));
3329 if (!ChildList)
3330 return TRUE;
3332 cstate = (Object *) ChildList->mlh_Head;
3333 while ((child = NextObject(&cstate)))
3336 if (GetAttr(msg->TrigAttr, child, attr))
3338 DoMethodA(child, (Msg) msg);
3339 /* No return here! */
3342 return TRUE;
3344 #endif
3346 BOOPSI_DISPATCHER(IPTR, Group_Dispatcher, cl, obj, msg)
3348 switch (msg->MethodID)
3350 case OM_NEW:
3351 return Group__OM_NEW(cl, obj, (struct opSet *)msg);
3352 case OM_DISPOSE:
3353 return Group__OM_DISPOSE(cl, obj, msg);
3354 case OM_SET:
3355 return Group__OM_SET(cl, obj, (struct opSet *)msg);
3356 case OM_GET:
3357 return Group__OM_GET(cl, obj, (struct opGet *)msg);
3358 case OM_ADDMEMBER: /* Fall through */
3359 case MUIM_Group_AddTail:
3360 return Group__MUIM_AddTail(cl, obj, (APTR) msg);
3361 case MUIM_Group_AddHead:
3362 return Group__MUIM_AddHead(cl, obj, (APTR) msg);
3363 case MUIM_Group_Insert:
3364 return Group__MUIM_Insert(cl, obj, (APTR) msg);
3365 case OM_REMMEMBER: /* Fall through */
3366 case MUIM_Group_Remove:
3367 return Group__MUIM_Remove(cl, obj, (APTR) msg);
3368 case MUIM_AskMinMax:
3369 return Group__MUIM_AskMinMax(cl, obj, (APTR) msg);
3370 case MUIM_Group_ExitChange:
3371 return Group__MUIM_ExitChange(cl, obj, (APTR) msg);
3372 case MUIM_Group_ExitChange2:
3373 return Group__MUIM_ExitChange2(cl, obj, (APTR) msg);
3374 case MUIM_Group_InitChange:
3375 return Group__MUIM_InitChange(cl, obj, (APTR) msg);
3376 case MUIM_Group_Sort:
3377 return Group__MUIM_Sort(cl, obj, (APTR) msg);
3378 case MUIM_Group_DoMethodNoForward:
3379 return Group__MUIM_DoMethodNoForward(cl, obj, (APTR) msg);
3380 case MUIM_ConnectParent:
3381 return Group__MUIM_ConnectParent(cl, obj, (APTR) msg);
3382 case MUIM_DisconnectParent:
3383 return Group__MUIM_DisconnectParent(cl, obj, (APTR) msg);
3384 case MUIM_Layout:
3385 return Group__MUIM_Layout(cl, obj, (APTR) msg);
3386 case MUIM_Setup:
3387 return Group__MUIM_Setup(cl, obj, (APTR) msg);
3388 case MUIM_Cleanup:
3389 return Group__MUIM_Cleanup(cl, obj, (APTR) msg);
3390 case MUIM_Draw:
3391 return Group__MUIM_Draw(cl, obj, (APTR) msg);
3393 case MUIM_FindUData:
3394 return Group__MUIM_FindUData(cl, obj, (APTR) msg);
3395 case MUIM_GetUData:
3396 return Group__MUIM_GetUData(cl, obj, (APTR) msg);
3397 case MUIM_SetUData:
3398 return Group__MUIM_SetUData(cl, obj, (APTR) msg);
3399 case MUIM_SetUDataOnce:
3400 return Group__MUIM_SetUDataOnce(cl, obj, (APTR) msg);
3401 case MUIM_Show:
3402 return Group__MUIM_Show(cl, obj, (APTR) msg);
3403 case MUIM_Hide:
3404 return Group__MUIM_Hide(cl, obj, (APTR) msg);
3405 case MUIM_HandleEvent:
3406 return Group__MUIM_HandleEvent(cl, obj, (APTR) msg);
3407 case MUIM_DrawBackground:
3408 return Group__MUIM_DrawBackground(cl, obj, (APTR) msg);
3409 case MUIM_DragQueryExtended:
3410 return Group__MUIM_DragQueryExtended(cl, obj, (APTR) msg);
3411 case MUIM_FindAreaObject:
3412 return Group__MUIM_FindAreaObject(cl, obj, (APTR) msg);
3413 case MUIM_Export:
3414 return Group__MUIM_Export(cl, obj, (APTR) msg);
3415 case MUIM_Import:
3416 return Group__MUIM_Import(cl, obj, (APTR) msg);
3418 //#if 0
3419 #if 1
3420 /* Disabled. See above */
3421 case MUIM_Notify:
3422 return Group_Notify(cl, obj, (APTR) msg);
3423 #endif
3424 case MUIM_Set:
3425 case MUIM_MultiSet:
3426 case MUIM_CallHook:
3427 case MUIM_DrawParentBackground:
3428 case MUIM_DragBegin:
3429 case MUIM_DragDrop:
3430 case MUIM_DragQuery:
3431 case MUIM_DragFinish:
3432 case MUIM_DoDrag:
3433 case MUIM_CreateDragImage:
3434 case MUIM_DeleteDragImage:
3435 case MUIM_GoActive:
3436 case MUIM_GoInactive:
3437 case MUIM_CreateBubble:
3438 case MUIM_DeleteBubble:
3439 case MUIM_CreateShortHelp:
3440 case MUIM_DeleteShortHelp:
3441 case OM_ADDTAIL:
3442 case OM_REMOVE:
3443 return DoSuperMethodA(cl, obj, (APTR) msg);
3444 /* Needs not to be forwarded? */
3447 /* sometimes you want to call a superclass method,
3448 * but not dispatching to child.
3449 * But what to do with list methods in a listview ?
3451 Group_DispatchMsg(cl, obj, (APTR) msg);
3453 return DoSuperMethodA(cl, obj, msg);
3455 BOOPSI_DISPATCHER_END
3458 * Class descriptor.
3460 const struct __MUIBuiltinClass _MUI_Group_desc =
3462 MUIC_Group,
3463 MUIC_Area,
3464 sizeof(struct MUI_GroupData),
3465 (void *) Group_Dispatcher