revert commit 56204.
[AROS.git] / workbench / libs / muimaster / classes / group.c
blobe3628891f4d7f1fca360004acb325727f5dd00dd
1 /*
2 Copyright © 1999, David Le Corfec.
3 Copyright © 2002-2017, 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
280 * added to application tree */
281 muiNotifyData(msgint->obj)->mnd_ParentObject = obj;
283 if (_flags(obj) & MADF_SETUP)
285 DoSetupMethod(msgint->obj, muiRenderInfo(obj));
287 /* if (_flags(obj) & MADF_CANDRAW) */
288 /* DoShowMethod(msg->opam_Object); */
290 return TRUE;
293 /**************************************************************************
294 OM_NEW - Constructor
295 **************************************************************************/
296 IPTR Group__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
298 struct MUI_GroupData *data;
299 struct TagItem *tags, *tag;
300 BOOL bad_children = FALSE;
301 IPTR disabled = FALSE;
302 IPTR frame = MUIV_Frame_None;
304 D(bug("[group.mui] OM_NEW, object 0x%p\n", obj));
306 obj = (Object *) DoSuperMethodA(cl, obj, (Msg) msg);
307 if (!obj)
308 return 0;
310 /* Initial local instance data */
311 data = INST_DATA(cl, obj);
313 data->family = MUI_NewObjectA(MUIC_Family, NULL);
314 if (!data->family)
316 CoerceMethod(cl, obj, OM_DISPOSE);
317 return 0;
320 data->horiz_spacing = -1;
321 data->vert_spacing = -1;
322 data->columns = 1;
323 data->rows = 1;
324 data->active_page = 0;
325 get(obj, MUIA_Frame, &frame);
327 /* parse initial taglist */
328 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
330 switch (tag->ti_Tag)
332 case MUIA_Group_Child:
333 D(bug("[group.mui] Adding child 0x%p\n", tag->ti_Data));
334 if (tag->ti_Data)
336 DoMethod(obj, OM_ADDMEMBER, tag->ti_Data);
337 /* Set first child as group title */
338 if ((frame == MUIV_Frame_Register)
339 && (data->titlegroup == NULL))
340 data->titlegroup = (Object *) tag->ti_Data;
342 else
343 bad_children = TRUE;
344 break;
346 case MUIA_Group_ActivePage:
347 change_active_page(cl, obj, (LONG) tag->ti_Data);
348 break;
350 case MUIA_Group_Columns:
351 data->columns = (tag->ti_Data) > 1 ? tag->ti_Data : 1;
352 data->rows = 0;
353 break;
355 case MUIA_Group_Horiz:
356 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_HORIZ);
357 break;
359 case MUIA_Group_HorizSpacing:
360 data->flags |= GROUP_HSPACING;
361 data->horiz_spacing = tag->ti_Data;
362 break;
364 case MUIA_Group_LayoutHook:
365 data->layout_hook = (struct Hook *)tag->ti_Data;
366 break;
368 case MUIA_Group_PageMode:
369 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_PAGEMODE);
370 break;
372 case MUIA_Group_Rows:
373 data->rows = MAX((ULONG) tag->ti_Data, 1);
374 data->columns = 0;
375 break;
377 case MUIA_Group_SameHeight:
378 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_HEIGHT);
379 break;
381 case MUIA_Group_SameSize:
382 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_HEIGHT);
383 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_WIDTH);
384 break;
386 case MUIA_Group_SameWidth:
387 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_WIDTH);
388 break;
390 case MUIA_Group_Spacing:
391 data->flags |= (GROUP_HSPACING | GROUP_VSPACING);
392 data->horiz_spacing = tag->ti_Data;
393 data->vert_spacing = tag->ti_Data;
394 break;
396 case MUIA_Group_VertSpacing:
397 data->flags |= GROUP_VSPACING;
398 data->vert_spacing = tag->ti_Data;
399 break;
401 case MUIA_Group_Virtual:
402 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_VIRTUAL);
403 break;
407 if (bad_children)
409 CoerceMethod(cl, obj, OM_DISPOSE);
410 return 0;
413 /* D(bug("Group_New(0x%lx)\n",obj)); */
415 if (data->flags & GROUP_VIRTUAL)
417 /* This is used by MUI_Render() to determine if group is virtual.
418 * It then installs a clip region.
419 * Also MUI_Layout() uses this. Probably for speed up reason */
420 _flags(obj) |= MADF_ISVIRTUALGROUP;
423 /* will forward MUIA_Disabled to children */
424 get(obj, MUIA_Disabled, &disabled);
425 if (disabled)
427 set(obj, MUIA_Disabled, TRUE);
430 /* This is only used for virtual groups */
431 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS; /* Will be filled on demand */
432 data->ehn.ehn_Priority = 10; /* Will hear the click before all
433 * other normal objects */
434 data->ehn.ehn_Flags = 0;
435 data->ehn.ehn_Object = obj;
436 data->ehn.ehn_Class = cl;
437 return (IPTR) obj;
440 /**************************************************************************
441 OM_DISPOSE
442 **************************************************************************/
443 IPTR Group__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
445 struct MUI_GroupData *data = INST_DATA(cl, obj);
447 if (data->row_infos != NULL)
448 mui_free(data->row_infos);
449 if (data->col_infos != NULL)
450 mui_free(data->col_infos);
451 if (data->family != NULL)
452 MUI_DisposeObject(data->family);
453 return DoSuperMethodA(cl, obj, msg);
456 /**************************************************************************
457 OM_SET
458 **************************************************************************/
459 IPTR Group__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
461 struct MUI_GroupData *data = INST_DATA(cl, obj);
462 struct TagItem *tags = msg->ops_AttrList;
463 struct TagItem *tag;
464 BOOL forward = TRUE;
465 BOOL need_recalc = FALSE;
466 IPTR retval;
468 int virt_offx = data->virt_offx, virt_offy = data->virt_offy;
470 /* There are many ways to find out what tag items provided by set()
471 ** we do know. The best way should be using NextTagItem() and simply
472 ** browsing through the list.
475 /* Parse group attributes before calling DoSuperMethodA(),
476 ** otherwise if an app for example sets up a notification
477 ** on MUIA_Group_ActivePage which calls a hook, and then
478 ** the hook function calls get(obj, MUIA_Group_ActivePage),
479 ** it would get returned the old active page instead of the new
480 ** active page
483 while ((tag = NextTagItem(&tags)) != NULL)
485 switch (tag->ti_Tag)
487 case MUIA_Group_Columns:
488 data->columns = MAX((ULONG) tag->ti_Data, 1);
489 data->rows = 0;
490 need_recalc = TRUE;
491 break;
492 case MUIA_Group_ActivePage:
493 change_active_page(cl, obj, (LONG) tag->ti_Data);
494 break;
495 case MUIA_Group_Forward:
496 forward = tag->ti_Data;
497 break;
498 case MUIA_Group_HorizSpacing:
499 data->flags |= GROUP_HSPACING;
500 data->horiz_spacing = tag->ti_Data;
501 break;
502 case MUIA_Group_Rows:
503 data->rows = MAX((ULONG) tag->ti_Data, 1);
504 data->columns = 0;
505 need_recalc = TRUE;
506 break;
507 case MUIA_Group_Spacing:
508 data->flags |= (GROUP_HSPACING | GROUP_VSPACING);
509 data->horiz_spacing = tag->ti_Data;
510 data->vert_spacing = tag->ti_Data;
511 break;
512 case MUIA_Group_VertSpacing:
513 data->flags |= GROUP_VSPACING;
514 data->vert_spacing = tag->ti_Data;
515 break;
516 case MUIA_Virtgroup_Left:
517 //kprintf("set virtgroup_left: %d\n", tag->ti_Data);
518 virt_offx = tag->ti_Data;
519 break;
521 case MUIA_Group_LayoutHook:
523 [ach] Seems like MUI supports setting this attribute after
524 initialization, even though the documentation states
525 otherwise. At least some programs use it...
527 data->layout_hook = (struct Hook *)tag->ti_Data;
528 break;
530 case MUIA_Virtgroup_Top:
531 //kprintf("set virtgroup_top: %d\n", tag->ti_Data);
532 virt_offy = tag->ti_Data;
533 break;
538 if (muiRenderInfo(obj) && need_recalc)
539 DoMethod(_win(obj), MUIM_Window_RecalcDisplay, (IPTR) obj);
541 retval = DoSuperMethodA(cl, obj, (Msg) msg);
543 /* seems to be the documented behaviour, however it should be slow! */
545 if (forward)
547 /* Attributes which are to be filtered out, so that they are ignored
548 when OM_SET is passed to group's children */
550 tags = msg->ops_AttrList;
551 while ((tag = NextTagItem(&tags)) != NULL)
553 switch (tag->ti_Tag)
555 case MUIA_HelpLine:
556 case MUIA_HelpNode:
557 case MUIA_ObjectID:
558 case MUIA_UserData:
560 case MUIA_ContextMenu:
561 case MUIA_ContextMenuTrigger:
562 case MUIA_ControlChar:
563 case MUIA_CycleChain:
564 case MUIA_Draggable:
565 case MUIA_FillArea:
566 case MUIA_Group_ActivePage:
567 case MUIA_Frame:
568 case MUIA_FrameTitle:
569 case MUIA_HorizWeight:
570 case MUIA_Pressed:
571 case MUIA_ShortHelp:
572 case MUIA_ShowMe:
573 case MUIA_VertWeight:
574 case MUIA_Weight:
575 case MUIA_Virtgroup_Left:
576 case MUIA_Virtgroup_Top:
577 case MUIA_AppMessage:
578 case MUIA_Timer:
579 tag->ti_Tag = TAG_IGNORE;
580 break;
581 case MUIA_Selected:
582 /* D(bug("Group_Set(%p) MUIA_Selected forwarded\n", obj)); */
583 /* tag->ti_Tag = TAG_IGNORE; */
584 break;
588 Group_DispatchMsg(cl, obj, (Msg) msg);
592 if (virt_offx != data->virt_offx || virt_offy != data->virt_offy)
594 if (_flags(obj) & MADF_CANDRAW)
595 Group__MUIM_Hide(cl, obj, NULL);
596 data->virt_offx = virt_offx;
597 data->virt_offy = virt_offy;
598 /* Relayout ourselves. This will also relayout all the children */
599 DoMethod(obj, MUIM_Layout);
600 if (_flags(obj) & MADF_CANDRAW)
601 Group__MUIM_Show(cl, obj, NULL);
602 data->update = 2;
603 MUI_Redraw(obj, MADF_DRAWUPDATE);
606 return retval;
610 /**************************************************************************
611 OM_GET
612 **************************************************************************/
613 IPTR Group__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
615 /* small macro to simplify return value storage */
616 #define STORE *(msg->opg_Storage)
618 struct MUI_GroupData *data = INST_DATA(cl, obj);
620 switch (msg->opg_AttrID)
622 case MUIA_Version:
623 STORE = __version;
624 return 1;
625 case MUIA_Revision:
626 STORE = __revision;
627 return 1;
628 case MUIA_Group_ActivePage:
629 STORE = data->active_page;
630 return 1;
631 case MUIA_Group_ChildList:
632 return GetAttr(MUIA_Family_List, data->family, msg->opg_Storage);
633 case MUIA_Group_Horiz:
634 STORE = (data->flags & GROUP_HORIZ);
635 return 1;
636 case MUIA_Group_HorizSpacing:
637 STORE = data->horiz_spacing;
638 return 1;
639 case MUIA_Group_VertSpacing:
640 STORE = data->vert_spacing;
641 return 1;
642 case MUIA_Group_ChildCount:
643 STORE = data->num_children;
644 return 1;
645 case MUIA_Virtgroup_Left:
646 STORE = data->virt_offx;
647 return 1;
648 case MUIA_Virtgroup_Top:
649 STORE = data->virt_offy;
650 return 1;
651 case MUIA_Virtgroup_Width:
652 STORE = data->virt_mwidth;
653 return 1;
654 case MUIA_Virtgroup_Height:
655 STORE = data->virt_mheight;
656 return 1;
657 case MUIA_Virtgroup_MinWidth:
658 STORE = data->saved_minwidth;
659 return 1;
660 case MUIA_Virtgroup_MinHeight:
661 STORE = data->saved_minheight;
662 return 1;
665 /* our handler didn't understand the attribute, we simply pass
666 ** it to our superclass now
668 if (DoSuperMethodA(cl, obj, (Msg) msg))
669 return 1;
671 /* seems to be the documented behaviour, however it should be slow! */
672 if (!data->dont_forward_get && !data->dont_forward_methods)
674 Object *cstate;
675 Object *child;
676 struct MinList *ChildList = NULL;
678 get(data->family, MUIA_Family_List, &(ChildList));
679 cstate = (Object *) ChildList->mlh_Head;
680 while ((child = NextObject(&cstate)))
681 if (DoMethodA(child, (Msg) msg))
682 return 1;
684 return 0;
685 #undef STORE
689 /**************************************************************************
690 MUIM_AddTail
691 **************************************************************************/
692 IPTR Group__MUIM_AddTail(struct IClass *cl, Object *obj,
693 struct MUIP_Group_AddTail *msg)
695 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
698 /**************************************************************************
699 MUIM_AddHead
700 **************************************************************************/
701 IPTR Group__MUIM_AddHead(struct IClass *cl, Object *obj,
702 struct MUIP_Group_AddHead *msg)
704 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
707 /**************************************************************************
708 MUIM_Insert
709 **************************************************************************/
710 IPTR Group__MUIM_Insert(struct IClass *cl, Object *obj,
711 struct MUIP_Group_Insert *msg)
713 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
716 /**************************************************************************
717 MUIM_Remove
718 **************************************************************************/
719 IPTR Group__MUIM_Remove(struct IClass *cl, Object *obj,
720 struct MUIP_Group_Remove *msg)
722 struct MUI_GroupData *data = INST_DATA(cl, obj);
724 if (_flags(obj) & MADF_CANDRAW)
725 DoHideMethod(msg->obj);
726 if (_flags(obj) & MADF_SETUP)
727 DoMethod(msg->obj, MUIM_Cleanup);
728 if (muiNotifyData(obj)->mnd_GlobalInfo)
730 DoMethod(msg->obj, MUIM_DisconnectParent);
731 muiNotifyData(msg->obj)->mnd_ParentObject = NULL;
733 _flags(msg->obj) &= ~MADF_INVIRTUALGROUP;
736 if ((data->flags & GROUP_CHANGING) != 0)
737 data->flags |= GROUP_CHANGED;
738 data->num_children--;
739 DoMethodA(data->family, (Msg) msg);
741 return TRUE;
744 /**************************************************************************
745 MUIM_Family_GetChild
746 **************************************************************************/
747 IPTR Group__MUIM_Family_GetChild(struct IClass *cl, Object *obj,
748 struct MUIP_Group_Remove *msg)
750 struct MUI_GroupData *data = INST_DATA(cl, obj);
752 return DoMethodA(data->family, (APTR) msg);
755 /**************************************************************************
756 MUIM_ConnectParent
757 **************************************************************************/
758 IPTR Group__MUIM_ConnectParent(struct IClass *cl, Object *obj,
759 struct MUIP_ConnectParent *msg)
761 struct MUI_GroupData *data = INST_DATA(cl, obj);
762 Object *cstate;
763 Object *child;
764 struct MinList *ChildList = NULL;
766 DoSuperMethodA(cl, obj, (Msg) msg);
768 get(data->family, MUIA_Family_List, &(ChildList));
769 cstate = (Object *) ChildList->mlh_Head;
770 while ((child = NextObject(&cstate)))
772 if ((_flags(obj) & MADF_INVIRTUALGROUP)
773 || (data->flags & GROUP_VIRTUAL))
775 _flags(child) |= MADF_INVIRTUALGROUP;
778 /* Only children of groups can have parents */
779 muiNotifyData(child)->mnd_ParentObject = obj;
781 DoMethod(child, MUIM_ConnectParent, (IPTR) obj);
783 return TRUE;
786 /**************************************************************************
787 MUIM_DisconnectParent
788 **************************************************************************/
789 IPTR Group__MUIM_DisconnectParent(struct IClass *cl, Object *obj,
790 struct MUIP_ConnectParent *msg)
792 struct MUI_GroupData *data = INST_DATA(cl, obj);
793 Object *cstate;
794 Object *child;
795 struct MinList *ChildList = NULL;
797 get(data->family, MUIA_Family_List, &(ChildList));
798 cstate = (Object *) ChildList->mlh_Head;
799 while ((child = NextObject(&cstate)))
801 DoMethodA(child, (Msg) msg);
802 muiNotifyData(child)->mnd_ParentObject = NULL;
803 _flags(child) &= ~MADF_INVIRTUALGROUP;
805 DoSuperMethodA(cl, obj, (Msg) msg);
806 return TRUE;
810 * Put group in exchange state
812 IPTR Group__MUIM_InitChange(struct IClass *cl, Object *obj,
813 struct MUIP_Group_InitChange *msg)
815 struct MUI_GroupData *data = INST_DATA(cl, obj);
817 data->flags &= ~GROUP_CHANGED;
818 data->flags |= GROUP_CHANGING;
819 return TRUE;
824 * Will recalculate display after dynamic adding/removing
826 IPTR Group__MUIM_ExitChange(struct IClass *cl, Object *obj,
827 struct MUIP_Group_ExitChange *msg)
829 struct MUI_GroupData *data = INST_DATA(cl, obj);
831 data->flags &= ~GROUP_CHANGING;
833 #if 0
834 /* Code is invalid. ExitChange needs to re-layout each time (tested
835 * with MUI 3.8 and MUI 4.0 on m68k).
836 * This is temporary change until proper implementaion is in place. */
837 if (data->flags & GROUP_CHANGED)
838 #endif
840 data->flags &= ~GROUP_CHANGED;
842 if ((_flags(obj) & MADF_SETUP) && _win(obj))
844 Object *win = _win(obj);
845 Object *parent = obj;
847 /* CHECKME: Don't call RecalcDisplay if one of our parents is
848 in GROUP_CHANGING state to prevent crash with Zune prefs
849 program NListtree page because NList/NListtree when
850 killing tree images in MUIM_Cleanup uses InitChange/
851 ExitChange. Zune prefs program uses InitChange/ExitChange
852 when switching page -> nesting -> mess. */
854 while ((parent = _parent(parent)))
856 struct MUI_GroupData *pdata = INST_DATA(cl, parent);
858 if (parent == win)
859 break;
861 if (pdata->flags & GROUP_CHANGING)
863 return TRUE;
868 DoMethod(win, MUIM_Window_RecalcDisplay, (IPTR) obj);
872 return TRUE;
877 * Will recalculate display after dynamic adding/removing
879 IPTR Group__MUIM_ExitChange2(struct IClass *cl, Object *obj,
880 struct MUIP_Group_ExitChange2 *msg)
882 struct MUI_GroupData *data = INST_DATA(cl, obj);
884 if (data->flags & GROUP_CHANGING)
886 data->flags &= ~(GROUP_CHANGING | GROUP_CHANGED);
888 if ((_flags(obj) & MADF_SETUP) && _win(obj))
890 Object *win = _win(obj);
891 Object *parent = obj;
893 /* CHECKME: Don't call RecalcDisplay if one of our parents is
894 in GROUP_CHANGING state to prevent crash with Zune prefs
895 program NListtree page because NList/NListtree when
896 killing tree images in MUIM_Cleanup uses InitChange/
897 ExitChange. Zune prefs program uses InitChange/ExitChange
898 when switching page -> nesting -> mess. */
900 while ((parent = _parent(parent)))
902 struct MUI_GroupData *pdata = INST_DATA(cl, parent);
904 if (parent == win)
905 break;
907 if (pdata->flags & GROUP_CHANGING)
909 return TRUE;
914 DoMethod(win, MUIM_Window_RecalcDisplay, (IPTR) obj);
918 return TRUE;
923 * Sort the family
925 IPTR Group__MUIM_Sort(struct IClass *cl, Object *obj,
926 struct MUIP_Group_Sort *msg)
928 struct MUI_GroupData *data = INST_DATA(cl, obj);
930 /* modify message */
931 msg->MethodID = MUIM_Family_Sort;
933 DoMethodA(data->family, (APTR) msg);
935 /* restore original message */
936 msg->MethodID = MUIM_Group_Sort;
937 return TRUE;
940 /**************************************************************************
941 MUIM_Group_DoMethodNoForward
943 Executes the given method but does not forward it to the children
944 **************************************************************************/
945 IPTR Group__MUIM_DoMethodNoForward(struct IClass *cl, Object *obj,
946 struct MUIP_Group_DoMethodNoForward *msg)
948 struct MUI_GroupData *data = INST_DATA(cl, obj);
949 IPTR rc;
950 data->dont_forward_methods = 1; /* disable forwarding */
951 rc = DoMethodA(obj, (Msg) & msg->DoMethodID);
952 /* Probably doesn't work correctly on AROS? */
954 data->dont_forward_methods = 0;
955 return rc;
959 * Propagate a method to group children.
961 static ULONG Group_DispatchMsg(struct IClass *cl, Object *obj, Msg msg)
963 struct MUI_GroupData *data = INST_DATA(cl, obj);
964 Object *cstate;
965 Object *child;
966 struct MinList *ChildList = NULL;
968 if (data->dont_forward_methods)
969 return TRUE;
971 get(data->family, MUIA_Family_List, &(ChildList));
972 cstate = (Object *) ChildList->mlh_Head;
973 while ((child = NextObject(&cstate)))
975 DoMethodA(child, (Msg) msg);
977 return TRUE;
981 /**************************************************************************
982 MUIM_Setup
983 **************************************************************************/
984 IPTR Group__MUIM_Setup(struct IClass *cl, Object *obj,
985 struct MUIP_Setup *msg)
987 struct MUI_GroupData *data = INST_DATA(cl, obj);
988 Object *cstate;
989 Object *cstate_copy;
990 Object *child;
991 Object *childFailed;
992 struct MinList *ChildList = NULL;
994 if (!DoSuperMethodA(cl, obj, (Msg) msg))
995 return FALSE;
997 ASSERT_VALID_PTR(muiGlobalInfo(obj));
999 if (!(data->flags & GROUP_HSPACING))
1000 data->horiz_spacing = muiGlobalInfo(obj)->mgi_Prefs->group_hspacing;
1001 if (!(data->flags & GROUP_VSPACING))
1002 data->vert_spacing = muiGlobalInfo(obj)->mgi_Prefs->group_vspacing;
1003 get(data->family, MUIA_Family_List, &(ChildList));
1004 cstate = cstate_copy = (Object *) ChildList->mlh_Head;
1005 while ((child = NextObject(&cstate)))
1007 #if 0 /* SHOWME affects only show/hide */
1008 if (!(_flags(child) & MADF_SHOWME))
1009 continue;
1010 #endif
1012 if (!DoSetupMethod(child, msg->RenderInfo))
1014 /* Send MUIM_Cleanup to all objects that received MUIM_Setup.
1016 childFailed = child;
1017 cstate = cstate_copy;
1018 while ((child = NextObject(&cstate)) && (child != childFailed))
1020 #if 0 /* SHOWME affects only show/hide */
1021 if (!(_flags(child) & MADF_SHOWME))
1022 continue;
1023 #endif
1024 DoMethod(child, MUIM_Cleanup);
1026 return FALSE;
1030 if (data->flags & GROUP_VIRTUAL)
1032 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
1033 (IPTR) & data->ehn);
1036 return TRUE;
1040 /**************************************************************************
1041 MUIM_Cleanup
1042 **************************************************************************/
1043 IPTR Group__MUIM_Cleanup(struct IClass *cl, Object *obj, Msg msg)
1045 struct MUI_GroupData *data = INST_DATA(cl, obj);
1046 Object *cstate;
1047 Object *child;
1048 struct MinList *ChildList = NULL;
1050 if (data->flags & GROUP_VIRTUAL)
1052 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
1053 (IPTR) & data->ehn);
1056 get(data->family, MUIA_Family_List, &(ChildList));
1057 cstate = (Object *) ChildList->mlh_Head;
1058 while ((child = NextObject(&cstate)))
1060 #if 0 /* SHOWME affects only show/hide */
1061 if (!(_flags(child) & MADF_SHOWME))
1062 continue;
1063 #endif
1064 DoMethodA(child, (Msg) msg);
1066 return DoSuperMethodA(cl, obj, (Msg) msg);
1069 static struct Region *group_children_clip_region(struct IClass *cl,
1070 Object *obj)
1072 struct Region *region = NULL;
1074 region = NewRegion();
1075 if (region)
1077 struct MUI_GroupData *data = INST_DATA(cl, obj);
1078 struct Rectangle rect;
1079 LONG page = -1;
1080 struct MinList *ChildList = NULL;
1081 Object *cstate;
1082 Object *child;
1084 rect.MinX = _left(obj);
1085 rect.MinY = _top(obj);
1086 rect.MaxX = _right(obj);
1087 rect.MaxY = _bottom(obj);
1089 OrRectRegion(region, &rect);
1090 get(data->family, MUIA_Family_List, &ChildList);
1091 cstate = (Object *) ChildList->mlh_Head;
1092 while ((child = NextObject(&cstate)))
1094 if (child != data->titlegroup)
1095 ++page;
1097 if ((data->flags & GROUP_PAGEMODE) && (page != data->active_page)
1098 && (child != data->titlegroup))
1099 continue;
1101 if ((muiAreaData(child)->mad_Flags & MADF_CANDRAW)
1102 && (_width(child) > 0) && (_height(child) > 0))
1104 rect.MinX = MAX(_left(child), _mleft(obj));
1105 rect.MinY = MAX(_top(child), _mtop(obj));
1106 rect.MaxX = MIN(_right(child), _mright(obj));
1107 rect.MaxY = MIN(_bottom(child), _mbottom(obj));
1109 if ((rect.MaxX >= rect.MinX) && (rect.MaxY >= rect.MinY))
1111 ClearRectRegion(region, &rect);
1117 return region;
1120 /**************************************************************************
1121 MUIM_Draw - draw the group
1122 **************************************************************************/
1123 IPTR Group__MUIM_Draw(struct IClass *cl, Object *obj,
1124 struct MUIP_Draw *msg)
1126 struct MUI_GroupData *data = INST_DATA(cl, obj);
1127 Object *cstate;
1128 Object *child;
1129 struct MinList *ChildList = NULL;
1130 struct Rectangle group_rect; /* child_rect; */
1131 int page;
1132 struct Region *region = NULL;
1133 APTR clip = (APTR) - 1;
1135 if (data->flags & GROUP_CHANGING)
1136 return FALSE;
1138 if (muiGlobalInfo(obj)->mgi_Prefs->window_redraw
1139 == WINDOW_REDRAW_WITHOUT_CLEAR)
1141 struct Region *r = group_children_clip_region(cl, obj);
1142 APTR c = (APTR)-1;
1143 if (r)
1144 c = MUI_AddClipRegion(muiRenderInfo(obj), r);
1146 DoSuperMethodA(cl, obj, (Msg) msg);
1148 if (r)
1149 MUI_RemoveClipRegion(muiRenderInfo(obj), c);
1151 else
1153 DoSuperMethodA(cl, obj, (Msg) msg);
1156 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 1)
1158 struct Region *r = NULL;
1159 APTR c = (APTR)-1;
1161 if (muiGlobalInfo(obj)->mgi_Prefs->window_redraw
1162 == WINDOW_REDRAW_WITHOUT_CLEAR)
1163 r = group_children_clip_region(cl, obj);
1165 if (r)
1166 c = MUI_AddClipRegion(muiRenderInfo(obj), r);
1169 * update is set when changing active page of a page group
1170 * need to redraw background ourself
1172 DoMethod(obj, MUIM_DrawBackground,
1173 _mleft(obj), _mtop(obj), _mwidth(obj), _mheight(obj),
1174 _mleft(obj), _mtop(obj), 0);
1176 if (r)
1177 MUI_RemoveClipRegion(muiRenderInfo(obj), c);
1179 else
1181 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 2)
1183 LONG left, top, right, bottom;
1184 LONG diff_virt_offx = data->virt_offx - data->old_virt_offx;
1185 LONG diff_virt_offy = data->virt_offy - data->old_virt_offy;
1186 struct Rectangle rect;
1187 struct Rectangle *clip_rect = &muiRenderInfo(obj)->mri_ClipRect;
1189 data->update = 0;
1191 if (!diff_virt_offx && !diff_virt_offy)
1193 return 1;
1196 /* sba: I don't know how MUI handle this but ScrollRasterBF() made problems when scrolling
1197 ** a (partly visible) virtual groups in a virtual group, because e.g. _mtop() is then
1198 ** smaller than the region. ScrollRasterBF() on AmigaOS then marks the complete region
1199 ** as damaged. Using ScrollWindowRaster() solved that problem but it flickers then.
1200 ** To avoid this we prevent that the scroll area is out of the region bounds.
1201 ** The region bounds are setted in MUI_Redraw() but should probably should go in the
1202 ** MUI's clip functions
1205 left = MAX(_mleft(obj), clip_rect->MinX);
1206 top = MAX(_mtop(obj), clip_rect->MinY);
1207 right = MIN(_mright(obj), clip_rect->MaxX);
1208 bottom = MIN(_mbottom(obj), clip_rect->MaxY);
1210 /* old code was
1211 ** ScrollRasterBF(_rp(obj), diff_virt_offx, diff_virt_offy, _mleft(obj), _mtop(obj), _mright(obj),_mbottom(obj));
1214 ScrollWindowRaster(_window(obj), diff_virt_offx, diff_virt_offy,
1215 left, top, right, bottom);
1217 if ((region = NewRegion()))
1219 if (diff_virt_offx)
1221 rect.MinY = top;
1222 rect.MaxY = bottom;
1224 if (diff_virt_offx > 0)
1226 rect.MinX = right - diff_virt_offx + 1;
1227 if (rect.MinX < left)
1228 rect.MinX = left;
1229 rect.MaxX = right;
1231 else
1233 rect.MinX = left;
1234 rect.MaxX = left - diff_virt_offx - 1;
1235 if (rect.MaxX > right)
1236 rect.MaxX = right;
1239 if (rect.MinX <= rect.MaxX)
1241 DoMethod(obj, MUIM_DrawBackground,
1242 rect.MinX, rect.MinY,
1243 rect.MaxX - rect.MinX + 1,
1244 rect.MaxY - rect.MinY + 1,
1245 rect.MinX, rect.MinY, 0);
1247 OrRectRegion(region, &rect);
1251 if (diff_virt_offy)
1253 rect.MinX = left;
1254 rect.MaxX = right;
1256 if (diff_virt_offy > 0)
1258 rect.MinY = bottom - diff_virt_offy + 1;
1259 if (rect.MinY < top)
1260 rect.MinY = top;
1261 rect.MaxY = bottom;
1263 else
1265 rect.MinY = top;
1266 rect.MaxY = top - diff_virt_offy - 1;
1267 if (rect.MaxY > bottom)
1268 rect.MaxY = bottom;
1270 if (rect.MinY <= rect.MaxY)
1272 DoMethod(obj, MUIM_DrawBackground,
1273 rect.MinX, rect.MinY,
1274 rect.MaxX - rect.MinX + 1,
1275 rect.MaxY - rect.MinY + 1,
1276 rect.MinX, rect.MinY, 0);
1278 OrRectRegion(region, &rect);
1284 else
1286 if (!(msg->flags & MADF_DRAWOBJECT)
1287 && !(msg->flags & MADF_DRAWALL))
1288 return TRUE;
1292 if (data->flags & GROUP_VIRTUAL && !region)
1294 /* Not really needed if MUI Draws all the objects, maybe that's
1295 * what DRAWALL is for??? */
1296 if ((region = NewRegion()))
1298 struct Rectangle rect;
1299 rect.MinX = _mleft(obj);
1300 rect.MinY = _mtop(obj);
1301 rect.MaxX = _mright(obj);
1302 rect.MaxY = _mbottom(obj);
1303 OrRectRegion(region, &rect);
1307 /* Add clipping region if we have one */
1308 if (region)
1309 clip = MUI_AddClipRegion(muiRenderInfo(obj), region);
1311 group_rect = muiRenderInfo(obj)->mri_ClipRect;
1312 page = -1;
1313 get(data->family, MUIA_Family_List, &(ChildList));
1314 cstate = (Object *) ChildList->mlh_Head;
1315 while ((child = NextObject(&cstate)))
1317 if (!(_flags(child) & MADF_SHOWME))
1318 continue;
1320 if (child != data->titlegroup)
1321 ++page;
1323 if ((data->flags & GROUP_PAGEMODE) && ((page != data->active_page)
1324 && (child != data->titlegroup)))
1326 continue;
1329 if ((data->flags & GROUP_PAGEMODE) && (child == data->titlegroup)
1330 && (msg->flags & MADF_DRAWUPDATE) && (data->update == 1))
1332 /* Do not issue a re-draw to title group during page switch.
1333 * The group will re-draw itself due to setting of
1334 * MUIA_Group_ActivePage attribute.
1336 continue;
1339 MUI_Redraw(child, MADF_DRAWOBJECT);
1340 muiRenderInfo(obj)->mri_ClipRect = group_rect;
1343 if (data->flags & GROUP_VIRTUAL && region && clip != (APTR) - 1)
1345 MUI_RemoveClipRegion(muiRenderInfo(obj), clip);
1348 data->old_virt_offx = data->virt_offx;
1349 data->old_virt_offy = data->virt_offy;
1350 data->update = 0;
1352 return TRUE;
1356 #define END_MINMAX() \
1357 tmp.MaxHeight = MAX(tmp.MaxHeight, tmp.MinHeight); \
1358 tmp.MaxWidth = MAX(tmp.MaxWidth, tmp.MinWidth); \
1359 tmp.DefHeight = CLAMP(tmp.DefHeight, tmp.MinHeight, tmp.MaxHeight); \
1360 tmp.DefWidth = CLAMP(tmp.DefWidth, tmp.MinWidth, tmp.MaxWidth); \
1361 msg->MinMaxInfo->MinWidth += tmp.MinWidth; \
1362 msg->MinMaxInfo->MinHeight += tmp.MinHeight; \
1363 msg->MinMaxInfo->MaxWidth += tmp.MaxWidth; \
1364 msg->MinMaxInfo->MaxHeight += tmp.MaxHeight; \
1365 msg->MinMaxInfo->DefWidth += tmp.DefWidth; \
1366 msg->MinMaxInfo->DefHeight += tmp.DefHeight;
1369 * MinMax calculation function. When this is called,
1370 * the children of your group have already been asked
1371 * about their min/max dimension so you can use their
1372 * dimensions to calculate yours.
1374 * Notes:
1375 * - Init minwidth and maxwidth with size needed for total child spacing.
1376 * - 1st pass to find maximum minimum width, to set minwidth of each child
1377 * if they should have the same width (for a row of buttons ...)
1378 * - Adjust minwidth w/o making object bigger than their max size.
1380 static void group_minmax_horiz(struct IClass *cl, Object *obj,
1381 struct MinList *children, struct MUIP_AskMinMax *msg)
1383 struct MUI_GroupData *data = INST_DATA(cl, obj);
1384 Object *cstate;
1385 Object *child;
1386 struct MUI_MinMax tmp;
1387 WORD maxminwidth = 0;
1388 BOOL found_nonzero_vweight = FALSE;
1390 tmp.MinHeight = 0;
1391 tmp.DefHeight = 0;
1392 tmp.MaxHeight = MUI_MAXMAX;
1393 if (data->num_visible_children > 0)
1395 tmp.MinWidth = tmp.DefWidth = tmp.MaxWidth =
1396 (data->num_visible_children - 1) * data->horiz_spacing;
1398 else
1400 tmp.MinWidth = tmp.DefWidth = 0;
1401 tmp.MaxWidth = MUI_MAXMAX;
1404 if (data->flags & GROUP_SAME_WIDTH)
1406 cstate = (Object *) children->mlh_Head;
1407 while ((child = NextObject(&cstate)))
1409 if (IS_HIDDEN(child))
1410 continue;
1411 maxminwidth = MAX(maxminwidth, _minwidth(child));
1415 data->samesize_maxmin_horiz = maxminwidth;
1416 /* D(bug("group_minmax_horiz(%p) : maxminwidth=%d\n", obj, maxminwidth)); */
1418 data->horiz_weight_sum = 0;
1419 cstate = (Object *) children->mlh_Head;
1420 while ((child = NextObject(&cstate)))
1422 WORD minwidth;
1424 if (IS_HIDDEN(child))
1425 continue;
1426 if (data->flags & GROUP_SAME_WIDTH)
1428 minwidth = MAX(maxminwidth, _minwidth(child));
1429 minwidth = MIN(minwidth, _maxwidth(child));
1431 else
1432 minwidth = _minwidth(child);
1433 tmp.MinWidth += minwidth;
1434 tmp.DefWidth += w0_defwidth(child);
1435 tmp.MaxWidth += w0_maxwidth(child);
1436 tmp.MaxWidth = MIN(tmp.MaxWidth, MUI_MAXMAX);
1437 tmp.MinHeight = MAX(tmp.MinHeight, _minheight(child));
1438 tmp.DefHeight = MAX(tmp.DefHeight, _defheight(child));
1440 if all children have null weight then maxheight=minheight
1441 if all but some children have null weights, the maxheight
1442 is the min of all maxheights
1444 tmp.MaxHeight = MIN(tmp.MaxHeight, _maxheight(child));
1445 data->horiz_weight_sum += _hweight(child);
1446 if (_vweight(child) > 0)
1448 found_nonzero_vweight = TRUE;
1451 if (!found_nonzero_vweight)
1453 tmp.MaxHeight = tmp.MinHeight;
1456 //if (data->flags & GROUP_VIRTUAL)
1458 //kprintf("# min %d x %d def %d x %d max %d x %d\n",
1459 // tmp.MinWidth, tmp.MinHeight,
1460 // tmp.DefWidth, tmp.DefHeight,
1461 // tmp.MaxWidth, tmp.MaxHeight);
1464 END_MINMAX();
1467 /* minmax calculation for vertical groups (see group_minmax_horiz)
1469 static void group_minmax_vert(struct IClass *cl, Object *obj,
1470 struct MinList *children, struct MUIP_AskMinMax *msg)
1472 struct MUI_GroupData *data = INST_DATA(cl, obj);
1473 Object *cstate;
1474 Object *child;
1475 struct MUI_MinMax tmp;
1476 WORD maxminheight = 0;
1477 BOOL found_nonzero_hweight = FALSE;
1479 tmp.MinWidth = 0;
1480 tmp.DefWidth = 0;
1481 tmp.MaxWidth = MUI_MAXMAX;
1482 if (data->num_visible_children > 0)
1484 tmp.MinHeight = tmp.DefHeight = tmp.MaxHeight =
1485 (data->num_visible_children - 1) * data->vert_spacing;
1487 else
1489 tmp.MinHeight = tmp.DefHeight = 0;
1490 tmp.MaxHeight = MUI_MAXMAX;
1493 if (data->flags & GROUP_SAME_HEIGHT)
1495 cstate = (Object *) children->mlh_Head;
1496 while ((child = NextObject(&cstate)))
1498 if (IS_HIDDEN(child))
1499 continue;
1500 maxminheight = MAX(maxminheight, _minheight(child));
1504 data->samesize_maxmin_vert = maxminheight;
1505 data->vert_weight_sum = 0;
1506 cstate = (Object *) children->mlh_Head;
1507 while ((child = NextObject(&cstate)))
1509 if (IS_HIDDEN(child))
1510 continue;
1512 if (data->flags & GROUP_SAME_HEIGHT)
1513 _minheight(child) = MIN(maxminheight, w0_maxheight(child));
1514 tmp.MinHeight += _minheight(child);
1515 tmp.DefHeight += w0_defheight(child);
1516 tmp.MaxHeight += w0_maxheight(child);
1517 tmp.MaxHeight = MIN(tmp.MaxHeight, MUI_MAXMAX);
1518 tmp.MinWidth = MAX(tmp.MinWidth, _minwidth(child));
1519 tmp.DefWidth = MAX(tmp.DefWidth, _defwidth(child));
1520 tmp.MaxWidth = MIN(tmp.MaxWidth, _maxwidth(child));
1521 data->vert_weight_sum += _vweight(child);
1522 if (_hweight(child) > 0)
1524 found_nonzero_hweight = TRUE;
1527 if (!found_nonzero_hweight)
1529 tmp.MaxWidth = tmp.MinWidth;
1532 END_MINMAX();
1536 static void
1537 minmax_2d_rows_pass(struct MUI_GroupData *data, struct MinList *children,
1538 struct MUI_MinMax *req, WORD maxmin_height, WORD maxdef_height)
1540 int i, j;
1541 Object *cstate;
1542 Object *child;
1544 /* do not rewind after the while, to process line by line */
1545 cstate = (Object *) children->mlh_Head;
1546 /* for each row */
1547 for (i = 0; i < data->rows; i++)
1549 /* calculate min and max height of this row */
1550 int min_h = 0, def_h = 0, max_h = MUI_MAXMAX;
1551 BOOL found_nonzero_vweight = FALSE;
1553 data->row_infos[i].weight = 0;
1555 j = 0;
1556 while ((child = NextObject(&cstate)))
1558 if (IS_HIDDEN(child))
1559 continue;
1560 if (data->flags & GROUP_SAME_HEIGHT)
1562 _minheight(child) = MIN(maxmin_height, w0_maxheight(child));
1563 _defheight(child) = MIN(maxdef_height, w0_maxheight(child));
1565 min_h = MAX(min_h, _minheight(child));
1566 def_h = MAX(def_h, w0_defheight(child));
1567 max_h = MIN(max_h, _maxheight(child));
1568 if (_vweight(child) > 0)
1570 found_nonzero_vweight = TRUE;
1571 data->row_infos[i].weight += _vweight(child);
1573 ++j;
1574 if ((j % data->columns) == 0)
1575 break;
1577 if (!found_nonzero_vweight)
1578 max_h = min_h;
1579 else
1580 max_h = MAX(max_h, min_h);
1581 /* D(bug("row %d : min_h=%d max_h=%d\n", i, min_h, max_h)); */
1583 data->row_infos[i].min = min_h;
1584 data->row_infos[i].max = max_h;
1585 data->vert_weight_sum += data->row_infos[i].weight;
1587 req->MinHeight += min_h;
1588 req->DefHeight += def_h;
1589 req->MaxHeight += max_h;
1590 if (req->MaxHeight > MUI_MAXMAX)
1591 req->MaxHeight = MUI_MAXMAX;
1596 static void
1597 minmax_2d_columns_pass(struct MUI_GroupData *data, struct MinList *children,
1598 struct MUI_MinMax *req, WORD maxmin_width, WORD maxdef_width)
1600 int i, j;
1601 Object *cstate;
1602 Object *child;
1604 for (i = 0; i < data->columns; i++)
1606 /* calculate min and max width of this column */
1607 int min_w = 0, def_w = 0, max_w = MUI_MAXMAX;
1608 BOOL found_nonzero_hweight = FALSE;
1610 data->col_infos[i].weight = 0;
1612 j = 0;
1613 /* process all children to get children on a column */
1614 cstate = (Object *) children->mlh_Head;
1615 while ((child = NextObject(&cstate)))
1617 if (IS_HIDDEN(child))
1618 continue;
1619 ++j;
1620 if (((j - 1) % data->columns) != i)
1621 continue;
1622 if (data->flags & GROUP_SAME_WIDTH)
1624 _minwidth(child) = MIN(maxmin_width, w0_maxwidth(child));
1625 _defwidth(child) = MIN(maxdef_width, w0_maxwidth(child));
1627 min_w = MAX(min_w, _minwidth(child));
1628 def_w = MAX(def_w, w0_defwidth(child));
1630 /* this handles the case of null weight children, which limit
1631 * the max size if they're alone, but not if they are with
1632 * non-null weight obj
1634 max_w = MIN(max_w, _maxwidth(child));
1635 if (_hweight(child) > 0)
1637 found_nonzero_hweight = TRUE;
1638 data->col_infos[i].weight += _hweight(child);
1641 if (!found_nonzero_hweight)
1642 max_w = min_w;
1643 else
1644 max_w = MAX(max_w, min_w);
1645 /* D(bug("col %d : min_w=%d max_w=%d\n", i, min_w, max_w)); */
1647 data->col_infos[i].min = min_w;
1648 data->col_infos[i].max = max_w;
1649 data->horiz_weight_sum += data->col_infos[i].weight;
1651 req->MinWidth += min_w;
1652 req->DefWidth += def_w;
1653 req->MaxWidth += max_w;
1654 if (req->MaxWidth > MUI_MAXMAX)
1655 req->MaxWidth = MUI_MAXMAX;
1659 static void
1660 group_minmax_2d(struct IClass *cl, Object *obj,
1661 struct MinList *children, struct MUIP_AskMinMax *msg)
1663 struct MUI_GroupData *data = INST_DATA(cl, obj);
1664 Object *cstate;
1665 Object *child;
1666 struct MUI_MinMax tmp;
1667 WORD maxmin_width;
1668 WORD maxmin_height;
1669 WORD maxdef_width;
1670 WORD maxdef_height;
1672 if (!data->columns)
1674 if (data->num_children % data->rows)
1676 data->columns = 1;
1677 data->rows = data->num_children;
1679 else
1680 data->columns = data->num_children / data->rows;
1682 else
1684 if (data->num_children % data->columns)
1686 data->rows = 1;
1687 data->columns = data->num_children;
1689 else
1690 data->rows = data->num_children / data->columns;
1693 if (data->columns < 1)
1694 data->columns = 1;
1695 if (data->rows < 1)
1696 data->rows = 1;
1698 if (data->row_infos != NULL)
1699 mui_free(data->row_infos);
1701 data->row_infos = mui_alloc(data->rows * sizeof(struct layout2d_elem));
1702 if (NULL == data->row_infos)
1703 return;
1705 if (data->col_infos != NULL)
1706 mui_free(data->col_infos);
1708 data->col_infos =
1709 mui_alloc(data->columns * sizeof(struct layout2d_elem));
1710 if (NULL == data->col_infos)
1711 return;
1713 data->horiz_weight_sum = 0;
1714 data->vert_weight_sum = 0;
1716 tmp.MinHeight = tmp.DefHeight = tmp.MaxHeight =
1717 (data->rows - 1) * data->vert_spacing;
1718 tmp.MinWidth = tmp.DefWidth = tmp.MaxWidth =
1719 (data->columns - 1) * data->horiz_spacing;
1720 /* get minimum dims if same dims for all children are needed */
1721 maxmin_width = 0;
1722 maxmin_height = 0;
1723 maxdef_width = 0;
1724 maxdef_height = 0;
1726 if ((data->flags & GROUP_SAME_WIDTH)
1727 || (data->flags & GROUP_SAME_HEIGHT))
1729 cstate = (Object *) children->mlh_Head;
1730 while ((child = NextObject(&cstate)))
1732 if (!(_flags(child) & MADF_SHOWME))
1733 continue;
1734 maxmin_width = MAX(maxmin_width, _minwidth(child));
1735 maxmin_height = MAX(maxmin_height, _minheight(child));
1736 maxdef_width = MAX(maxdef_width, w0_defwidth(child));
1737 maxdef_height = MAX(maxdef_height, w0_defheight(child));
1739 /* g_print("2d group: mminw=%d mminh=%d\n", */
1740 /* maxmin_width, maxmin_height); */
1742 if (data->flags & GROUP_SAME_HEIGHT)
1743 data->samesize_maxmin_vert = maxmin_height;
1744 else
1745 data->samesize_maxmin_vert = 0;
1747 if (data->flags & GROUP_SAME_WIDTH)
1748 data->samesize_maxmin_horiz = maxmin_width;
1749 else
1750 data->samesize_maxmin_horiz = 0;
1752 minmax_2d_rows_pass(data, children, &tmp, maxmin_height, maxdef_height);
1753 minmax_2d_columns_pass(data, children, &tmp, maxmin_width,
1754 maxdef_width);
1756 END_MINMAX();
1760 static void
1761 group_minmax_pagemode(struct IClass *cl, Object *obj,
1762 struct MinList *children, struct MUIP_AskMinMax *msg)
1764 Object *cstate;
1765 Object *child;
1766 struct MUI_GroupData *data = INST_DATA(cl, obj);
1767 struct MUI_MinMax tmp = { 0, 0, MUI_MAXMAX, MUI_MAXMAX, 0, 0 };
1769 cstate = (Object *) children->mlh_Head;
1771 D(bug("minmax_pagemode(%lx)\n", obj, tmp.DefWidth));
1773 while ((child = NextObject(&cstate)))
1775 if (!(_flags(child) & MADF_SHOWME))
1776 continue;
1778 if (child == data->titlegroup)
1779 continue;
1781 tmp.MinHeight = MAX(tmp.MinHeight, _minheight(child));
1782 D(bug("minmax_pagemode(%p) minh child = %d tmpmin=%d\n", obj,
1783 _minheight(child), tmp.MinHeight));
1784 tmp.MinWidth = MAX(tmp.MinWidth, _minwidth(child));
1785 tmp.MaxHeight = MIN(tmp.MaxHeight, w0_maxheight(child));
1786 tmp.MaxWidth = MIN(tmp.MaxWidth, w0_maxwidth(child));
1787 tmp.DefHeight = MAX(tmp.DefHeight,
1788 ((w0_defheight(child) <
1789 MUI_MAXMAX) ? w0_defheight(child) : tmp.DefHeight));
1790 tmp.DefWidth =
1791 MAX(tmp.DefWidth,
1792 ((w0_defwidth(child) <
1793 MUI_MAXMAX) ? w0_defwidth(child) : tmp.DefWidth));
1794 D(bug("minmax_pagemode(%lx) defw = %ld\n", obj, tmp.DefWidth));
1797 if (data->titlegroup)
1799 tmp.MinHeight += _minheight(data->titlegroup);
1800 tmp.MaxHeight += w0_maxheight(data->titlegroup);
1801 tmp.DefHeight += w0_defheight(data->titlegroup);
1804 END_MINMAX();
1807 /**************************************************************************
1808 MUIM_AskMinMax : ask children about min/max sizes, then
1809 either call a hook, or the builtin method, to calculate our minmax
1810 **************************************************************************/
1811 IPTR Group__MUIM_AskMinMax(struct IClass *cl, Object *obj,
1812 struct MUIP_AskMinMax *msg)
1814 struct MUI_GroupData *data = INST_DATA(cl, obj);
1815 struct MUI_LayoutMsg lm;
1816 struct MUIP_AskMinMax childMsg;
1817 struct MUI_MinMax childMinMax;
1818 Object *cstate;
1819 Object *child;
1820 LONG super_minwidth, super_minheight;
1823 * let our superclass first fill in its size with frame, inner spc etc ...
1825 DoSuperMethodA(cl, obj, (Msg) msg);
1826 super_minwidth = msg->MinMaxInfo->MinWidth;
1827 super_minheight = msg->MinMaxInfo->MinHeight;
1830 * Ask children
1832 childMsg.MethodID = msg->MethodID;
1833 childMsg.MinMaxInfo = &childMinMax;
1834 get(data->family, MUIA_Family_List, &(lm.lm_Children));
1836 cstate = (Object *) lm.lm_Children->mlh_Head;
1838 while ((child = NextObject(&cstate)))
1840 if (!(_flags(child) & MADF_SHOWME))
1841 /* BORDERGADGETs should handle this itself */
1842 continue;
1843 /* Ask child */
1844 DoMethodA(child, (Msg) & childMsg);
1845 /* D(bug("*** group %lx, child %lx min=%ld,%ld\n", */
1846 /* obj, child, childMinMax.MinWidth, childMinMax.MinHeight)); */
1847 __area_finish_minmax(child, childMsg.MinMaxInfo);
1851 * Use children infos to calculate group size
1853 if (data->flags & GROUP_PAGEMODE)
1855 D(bug("minmax_pagemode(%p) minh initial = %d\n", obj,
1856 msg->MinMaxInfo->MinHeight));
1857 group_minmax_pagemode(cl, obj, lm.lm_Children, msg);
1858 D(bug("minmax_pagemode(%p) minh = %d\n", obj,
1859 msg->MinMaxInfo->MinHeight));
1861 else if (data->layout_hook)
1863 lm.lm_Type = MUILM_MINMAX;
1864 CallHookPkt(data->layout_hook, obj, &lm);
1866 if (lm.lm_MinMax.MaxHeight < lm.lm_MinMax.MinHeight)
1867 lm.lm_MinMax.MaxHeight = lm.lm_MinMax.MinHeight;
1868 if (lm.lm_MinMax.DefHeight < lm.lm_MinMax.MinHeight)
1869 lm.lm_MinMax.DefHeight = lm.lm_MinMax.MinHeight;
1870 if (lm.lm_MinMax.MaxWidth < lm.lm_MinMax.MinWidth)
1871 lm.lm_MinMax.MaxWidth = lm.lm_MinMax.MinWidth;
1872 if (lm.lm_MinMax.DefWidth < lm.lm_MinMax.MinWidth)
1873 lm.lm_MinMax.DefWidth = lm.lm_MinMax.MinWidth;
1875 //kprintf("### min %d x %d def %d x %d max %d x %d\n",
1876 // msg->MinMaxInfo->MinWidth,
1877 // msg->MinMaxInfo->MinHeight,
1878 // msg->MinMaxInfo->DefWidth,
1879 // msg->MinMaxInfo->DefHeight,
1880 // msg->MinMaxInfo->MaxWidth,
1881 // msg->MinMaxInfo->MaxHeight);
1883 msg->MinMaxInfo->MinWidth += lm.lm_MinMax.MinWidth;
1884 msg->MinMaxInfo->MinHeight += lm.lm_MinMax.MinHeight;
1885 msg->MinMaxInfo->MaxWidth += lm.lm_MinMax.MaxWidth;
1886 if (msg->MinMaxInfo->MaxWidth > MUI_MAXMAX)
1887 msg->MinMaxInfo->MaxWidth = MUI_MAXMAX;
1888 msg->MinMaxInfo->MaxHeight += lm.lm_MinMax.MaxHeight;
1889 if (msg->MinMaxInfo->MaxHeight > MUI_MAXMAX)
1890 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1891 msg->MinMaxInfo->DefWidth += lm.lm_MinMax.DefWidth;
1892 msg->MinMaxInfo->DefHeight += lm.lm_MinMax.DefHeight;
1894 //kprintf("#### min %d x %d def %d x %d max %d x %d\n",
1895 // msg->MinMaxInfo->MinWidth,
1896 // msg->MinMaxInfo->MinHeight,
1897 // msg->MinMaxInfo->DefWidth,
1898 // msg->MinMaxInfo->DefHeight,
1899 // msg->MinMaxInfo->MaxWidth,
1900 // msg->MinMaxInfo->MaxHeight);
1903 else
1905 if ((data->rows == 1) && (data->columns == 1))
1907 data->num_visible_children =
1908 Group_GetNumVisibleChildren(data, lm.lm_Children);
1909 if (data->flags & GROUP_HORIZ)
1910 group_minmax_horiz(cl, obj, lm.lm_Children, msg);
1911 else
1912 group_minmax_vert(cl, obj, lm.lm_Children, msg);
1914 else
1916 group_minmax_2d(cl, obj, lm.lm_Children, msg);
1920 if (data->flags & GROUP_VIRTUAL)
1922 data->saved_minwidth = msg->MinMaxInfo->MinWidth;
1923 data->saved_minheight = msg->MinMaxInfo->MinHeight;
1924 msg->MinMaxInfo->MinWidth = super_minwidth + 2;
1925 msg->MinMaxInfo->MinHeight = super_minheight + 2;
1927 //kprintf("## min %d x %d def %d x %d max %d x %d\n",
1928 // msg->MinMaxInfo->MinWidth,
1929 // msg->MinMaxInfo->MinHeight,
1930 // msg->MinMaxInfo->DefWidth,
1931 // msg->MinMaxInfo->DefHeight,
1932 // msg->MinMaxInfo->MaxWidth,
1933 // msg->MinMaxInfo->MaxHeight);
1937 return 0;
1942 // enforce minmax constraint, but also update total growable/shrinkable weights
1943 // while we're at it
1944 static void Layout1D_minmax_constraint(WORD *sizep, WORD minsize,
1945 WORD maxsize, WORD *remainp, WORD *sizegrowp, WORD *sizeshrinkp,
1946 ULONG *weightgrowp, ULONG *weightshrinkp, UWORD weight, WORD samesize)
1948 WORD size = *sizep, remain = *remainp,
1949 sizegrow = *sizegrowp, sizeshrink = *sizeshrinkp;
1950 ULONG weightgrow = *weightgrowp, weightshrink = *weightshrinkp;
1952 /* D(bug("L1D_minmax_c size=%d min=%d max=%d w=%d ss=%d\n", */
1953 /* size, minsize, maxsize, */
1954 /* weight, samesize)); */
1956 if ((samesize > 0) && (weight == 0))
1958 remain += size - samesize;
1959 size = samesize;
1960 sizeshrink -= size;
1961 sizegrow -= size;
1964 if (size <= minsize) // too little
1966 remain += size - minsize;
1967 size = minsize;
1968 sizeshrink -= size;
1969 if (size == maxsize)
1970 sizegrow -= size;
1972 else if (size >= maxsize) // too big
1974 remain += size - maxsize;
1975 size = maxsize;
1976 sizegrow -= size;
1979 if (!((samesize > 0) && (weight == 0)))
1981 if (size < maxsize)
1982 weightgrow += weight;
1983 if (size > minsize)
1984 weightshrink += weight;
1987 *sizep = size;
1988 *remainp = remain;
1989 *sizegrowp = sizegrow;
1990 *sizeshrinkp = sizeshrink;
1991 *weightgrowp = weightgrow;
1992 *weightshrinkp = weightshrink;
1996 // redistribute excess size to growable child, or reduce size of a shrinkable
1997 // child
1998 static void Layout1D_redistribution(WORD *sizep, WORD minsize,
1999 WORD maxsize, WORD *remainp, WORD *sizegrowp, WORD *sizeshrinkp,
2000 ULONG *weightgrowp, ULONG *weightshrinkp, UWORD weight)
2002 WORD size = *sizep, remain = *remainp,
2003 sizegrow = *sizegrowp, sizeshrink = *sizeshrinkp;
2004 ULONG weightgrow = *weightgrowp, weightshrink = *weightshrinkp;
2006 if (weight == 0)
2007 return;
2009 if ((remain > 0) && (size < maxsize))
2011 LONG newsize;
2013 newsize = (sizegrow * weight + weightgrow / 2) / weightgrow;
2015 /* D(bug("newsize=%ld == size_growa=%ld * w=%ld / weight_grow=%d\n", */
2016 /* newsize, sizegrow, weight, weightgrow)); */
2018 /* take care of off-by-1 errors that may toggle remainder sign
2019 * by ensuring convergence to 0
2021 if (remain - newsize + size < 0)
2023 /* D(bug("adding remainder=%d => size = %d\n", */
2024 /* remain, size + remain)); */
2025 size += remain;
2026 remain = 0;
2028 else
2030 remain -= newsize - size;
2031 size = newsize;
2032 sizegrow -= size;
2033 weightgrow -= weight;
2036 else if ((remain < 0) && (size > minsize))
2038 LONG newsize;
2040 newsize = (sizeshrink * weight + weightshrink / 2) / weightshrink;
2042 /* D(bug("newsize=%ld == size_shrinkables=%ld * w=%ld " */
2043 /* "/ weight_shrinkables=%d\n", */
2044 /* newsize, sizeshrink, weight, weightshrink)); */
2046 if (remain - newsize + size > 0)
2048 /* D(bug("adding remainder=%d => size = %d\n", */
2049 /* remain, size + remain)); */
2050 size += remain;
2051 remain = 0;
2053 else
2055 remain -= newsize - size;
2056 size = newsize;
2057 sizeshrink -= size;
2058 weightshrink -= weight;
2062 *sizep = size;
2063 *remainp = remain;
2064 *sizegrowp = sizegrow;
2065 *sizeshrinkp = sizeshrink;
2066 *weightgrowp = weightgrow;
2067 *weightshrinkp = weightshrink;
2071 // 2 passes at most, less on average (0.5 or 1.5), each does
2072 // - a minmax clamping, evenutally adding to a remainder
2073 // (remainder = missing (underflow) or remaining (overflow) space compared
2074 // to ideal sizes where children fill the whole group)
2075 // - a redistribution of the remainder, by growing (pos. remainder) or
2076 // shrinking (neg. remainder) children able to support it.
2078 // Occasionnaly the first time the redistribution is done, the minmax
2079 // constraint can be broken, thus the extra pass to check and eventually
2080 // redistribute. The second redistribution never breaks minmax constraint
2081 // (there should be a mathematical proof, but feel free to prove me wrong
2082 // with an example)
2083 static void Layout1D_minmax_constraints_and_redistrib(struct MinList
2084 *children, WORD total_size, WORD remainder, WORD samesize,
2085 BOOL group_horiz)
2087 Object *cstate;
2088 Object *child;
2089 int i;
2091 for (i = 0; i < 2; i++)
2093 WORD size_growables = total_size;
2094 WORD size_shrinkables = total_size;
2095 ULONG weight_growables = 0;
2096 ULONG weight_shrinkables = 0;
2098 /* D(bug("start : rem=%ld, A=%ld, size_growables=%ld, " */
2099 /* "size_shrinkables=%ld\n", */
2100 /* remainder, total_size, size_growables, size_shrinkables)); */
2102 // minmax constraints
2103 cstate = (Object *) children->mlh_Head;
2104 while ((child = NextObject(&cstate)))
2106 /* WORD old_size; */
2108 if (IS_HIDDEN(child))
2109 continue;
2111 if (group_horiz)
2113 /* old_size = _width(child); */
2115 Layout1D_minmax_constraint(&_width(child), _minwidth(child),
2116 _maxwidth(child), &remainder, &size_growables,
2117 &size_shrinkables, &weight_growables,
2118 &weight_shrinkables, _hweight(child), samesize);
2120 /* D(bug("loop1 on %p : width=%d was %d, rem=%d, A=%d, " */
2121 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2122 /* child, _width(child), old_size, remainder, total_size, */
2123 /* size_growables, size_shrinkables, _hweight(child), */
2124 /* _minwidth(child), _maxwidth(child))); */
2126 else // ! group_horiz
2128 /* old_size = _height(child); */
2130 Layout1D_minmax_constraint(&_height(child),
2131 _minheight(child), _maxheight(child), &remainder,
2132 &size_growables, &size_shrinkables, &weight_growables,
2133 &weight_shrinkables, _vweight(child), samesize);
2135 /* D(bug("loop1 on %p : h=%ld was %d, rem=%d, A=%d, " */
2136 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2137 /* child, _height(child), old_size, remainder, total_size,*/
2138 /* size_growables, size_shrinkables, _vweight(child), */
2139 /* _minheight(child), _maxheight(child))); */
2140 } // if (group_horiz)
2141 } // while child, minmax constraints
2143 // mid-pass break
2144 if (remainder == 0)
2145 break;
2147 /* D(bug("mid : rem=%d, A=%d, size_grow=%d, size_shrink=%d, " */
2148 /* "wg=%ld, ws=%ld\n", remainder, total_size, size_growables, */
2149 /* size_shrinkables, weight_growables, weight_shrinkables)); */
2151 // distribute remaining space to possible candidates
2152 cstate = (Object *) children->mlh_Head;
2153 while (((child = NextObject(&cstate)) != NULL) && (remainder != 0))
2155 /* WORD old_size; */
2157 if (IS_HIDDEN(child))
2158 continue;
2160 if (group_horiz)
2162 /* old_size = _width(child); */
2164 Layout1D_redistribution(&_width(child), _minwidth(child),
2165 _maxwidth(child), &remainder, &size_growables,
2166 &size_shrinkables, &weight_growables,
2167 &weight_shrinkables, _hweight(child));
2169 /* D(bug("loop2 on %p : h=%d was %d, rem=%d, A=%d, " */
2170 /* "size_grow=%d, size_shrink=%d\n", child, */
2171 /* _width(child), old_size, remainder, total_size, */
2172 /* size_growables, size_shrinkables)); */
2174 else // ! group_horiz
2176 /* old_size = _height(child); */
2178 Layout1D_redistribution(&_height(child), _minheight(child),
2179 _maxheight(child), &remainder, &size_growables,
2180 &size_shrinkables, &weight_growables,
2181 &weight_shrinkables, _vweight(child));
2183 /* D(bug("loop2 on %p : h=%d was %d, rem=%d, A=%d, " */
2184 /* "size_grow=%d, size_shrink=%d\n", child, */
2185 /* _height(child), old_size, remainder, total_size, */
2186 /* size_growables, size_shrinkables)); */
2187 } // if (group_horiz)
2189 } // while child, redistribution
2191 /* if (remainder != 0) */
2192 /* { */
2193 /* D(bug("end : rem=%ld, A=%ld, size_grow=%ld, size_shrink=%ld\n", */
2194 /* remainder, total_size, size_growables, size_shrinkables)); */
2195 /* } */
2196 // dont break here if remainder == 0, some minmax constraints
2197 // may not be respected
2198 } // end for
2200 // to easily spot layout bugs, nothing like a (division by zero) exception
2201 /* if (remainder != 0) */
2202 /* { */
2203 /* ASSERT(remainder != 0); */
2204 /* D(bug("gonna crash, remainder = %d\n", remainder)); */
2205 /* remainder /= 0; */
2206 /* } */
2210 static void Layout1D_weight_constraint(WORD *total_sizep,
2211 WORD *total_init_sizep,
2212 ULONG *total_weightp, WORD *sizep, UWORD weight, WORD minsize)
2214 if (*total_weightp > 0)
2215 *sizep = (*total_sizep * weight + *total_weightp / 2)
2216 / *total_weightp;
2217 else
2218 *sizep = minsize;
2220 *total_weightp -= weight;
2221 *total_sizep -= *sizep;
2222 *total_init_sizep += *sizep;
2226 static void group_layout_vert(struct IClass *cl, Object *obj,
2227 struct MinList *children)
2229 struct MUI_GroupData *data = INST_DATA(cl, obj);
2230 Object *cstate;
2231 Object *child;
2232 ULONG total_weight;
2233 WORD remainder; /* must converge to 0 to successfully end layout */
2234 WORD total_size;
2235 WORD total_size_backup;
2236 WORD total_init_size; /* total size of the ideally sized children */
2237 WORD width;
2238 WORD left = 0;
2239 WORD top = 0;
2240 WORD layout_width;
2241 WORD layout_height;
2243 //kprintf("group_layout_vert: virtoff = %d,%d\n",
2244 // data->virt_offx, data->virt_offy);
2246 if (data->flags & GROUP_VIRTUAL)
2248 data->virt_mwidth =
2249 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2250 _maxwidth(obj) - _subwidth(obj));
2251 data->virt_mheight =
2252 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2253 _maxheight(obj) - _subheight(obj));
2255 layout_width = data->virt_mwidth;
2256 layout_height = data->virt_mheight;
2258 else
2260 layout_width = _mwidth(obj);
2261 layout_height = _mheight(obj);
2264 total_weight = data->vert_weight_sum;
2265 total_init_size = 0;
2266 total_size =
2267 layout_height - (data->num_visible_children -
2268 1) * data->vert_spacing;
2269 total_size_backup = total_size;
2271 /* D(bug("\nvert layout for %p, A=%d W=%ld\n", */
2272 /* obj, total_size, total_weight)); */
2274 // weight constraints
2275 // calculate ideal size for each object, and total ideal size
2276 cstate = (Object *) children->mlh_Head;
2277 while ((child = NextObject(&cstate)))
2279 if (IS_HIDDEN(child))
2280 continue;
2282 Layout1D_weight_constraint(&total_size, &total_init_size,
2283 &total_weight, &_height(child), _vweight(child),
2284 _minheight(child));
2285 /* D(bug("child %p : ideal=%d w=%ld\n", */
2286 /* child, _height(child), _vweight(child))); */
2287 } // while child, weight constraints
2289 total_size = total_size_backup;
2290 remainder = total_size - total_init_size;
2292 if (data->flags & GROUP_VIRTUAL)
2294 /* This is also true for non virtual groups, but if this would be the
2295 ** case then there is a bug in the layout function
2297 if (total_size < 0)
2298 total_size = 0;
2301 Layout1D_minmax_constraints_and_redistrib(children,
2302 total_size,
2303 remainder,
2304 (data->flags & GROUP_SAME_HEIGHT) ? data->samesize_maxmin_vert : 0,
2305 FALSE);
2307 // do the layout
2308 cstate = (Object *) children->mlh_Head;
2309 while ((child = NextObject(&cstate)))
2311 if (IS_HIDDEN(child))
2312 continue;
2314 width = MIN(_maxwidth(child), layout_width);
2315 width = MAX(width, _minwidth(child));
2316 left = (layout_width - width) / 2;
2318 /* D(bug("child %p -> layout %d x %d\n", */
2319 /* child, width, _height(child))); */
2320 if (!MUI_Layout(child, left, top, width, _height(child), 0))
2321 return;
2322 top += data->vert_spacing + _height(child);
2328 static void group_layout_horiz(struct IClass *cl, Object *obj,
2329 struct MinList *children)
2331 struct MUI_GroupData *data = INST_DATA(cl, obj);
2332 Object *cstate;
2333 Object *child;
2334 ULONG total_weight;
2335 WORD remainder; /* must converge to 0 to successfully end layout */
2336 WORD total_size;
2337 WORD total_size_backup;
2338 WORD total_init_size; /* total size of the ideally sized children */
2339 WORD height;
2340 WORD top = 0;
2341 WORD left = 0;
2342 WORD layout_width;
2343 WORD layout_height;
2345 //kprintf("group_layout_horiz: virtoff = %d,%d\n",
2346 // data->virt_offx, data->virt_offy);
2348 if (data->flags & GROUP_VIRTUAL)
2350 data->virt_mwidth =
2351 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2352 _maxwidth(obj) - _subwidth(obj));
2353 data->virt_mheight =
2354 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2355 _maxheight(obj) - _subheight(obj));
2357 layout_width = data->virt_mwidth;
2358 layout_height = data->virt_mheight;
2360 //kprintf("group_layout_horiz: layoutsize %d x %d "
2361 // " virtsize %d x %d msize %d x %d\n",
2362 // layout_width, layout_height, data->virt_mwidth,
2363 // data->virt_mheight,
2364 // _mwidth(obj), _mheight(obj));
2366 else
2368 layout_width = _mwidth(obj);
2369 layout_height = _mheight(obj);
2372 total_weight = data->horiz_weight_sum;
2373 total_init_size = 0;
2374 total_size =
2375 layout_width - (data->num_visible_children -
2376 1) * data->horiz_spacing;
2377 total_size_backup = total_size;
2379 /* D(bug("\nhoriz layout for %p, A=%d W=%ld\n", */
2380 /* obj, total_size, total_weight)); */
2382 // weight constraints
2383 // calculate ideal size for each object, and total ideal size
2384 cstate = (Object *) children->mlh_Head;
2385 while ((child = NextObject(&cstate)))
2387 if (IS_HIDDEN(child))
2388 continue;
2390 Layout1D_weight_constraint(&total_size, &total_init_size,
2391 &total_weight, &_width(child), _hweight(child),
2392 _minwidth(child));
2393 /* D(bug("child %p : ideal=%d w=%ld\n", */
2394 /* child, _width(child), _hweight(child))); */
2395 } // while child, weight constraints
2397 total_size = total_size_backup;
2398 if (data->horiz_weight_sum > 0)
2399 remainder = total_size - total_init_size;
2400 else
2401 remainder = 0;
2403 if (data->flags & GROUP_VIRTUAL)
2405 /* This is also true for non virtual groups, but if this would be the
2406 ** case then there is a bug in the layout function
2408 if (total_size < 0)
2409 total_size = 0;
2412 Layout1D_minmax_constraints_and_redistrib(children,
2413 total_size,
2414 remainder,
2415 (data->flags & GROUP_SAME_WIDTH) ? data->samesize_maxmin_horiz : 0,
2416 TRUE);
2418 // do the layout
2419 cstate = (Object *) children->mlh_Head;
2420 while ((child = NextObject(&cstate)))
2422 if (IS_HIDDEN(child))
2423 continue;
2425 height = MIN(_maxheight(child), layout_height);
2426 height = MAX(height, _minheight(child));
2427 top = (layout_height - height) / 2;
2429 /* D(bug("child %p -> layout %d x %d\n", */
2430 /* child, _width(child), height)); */
2431 if (!MUI_Layout(child, left, top, _width(child), height, 0))
2432 return;
2433 left += data->horiz_spacing + _width(child);
2439 static void Layout2D_weight_constraint(struct MUI_GroupData *data,
2440 struct layout2d_elem *row_infos,
2441 struct layout2d_elem *col_infos,
2442 WORD total_size_height, WORD total_size_width,
2443 WORD *total_init_height, WORD *total_init_width)
2445 int i;
2446 ULONG total_weight_vert = data->vert_weight_sum;
2447 ULONG total_weight_horiz = data->horiz_weight_sum;
2449 *total_init_height = 0;
2450 *total_init_width = 0;
2452 /* calc row heights */
2453 for (i = 0; i < data->rows; i++)
2455 if (total_weight_vert > 0)
2456 row_infos[i].dim =
2457 (total_size_height * row_infos[i].weight +
2458 total_weight_vert / 2) / total_weight_vert;
2459 else
2460 row_infos[i].dim = row_infos[i].min;
2462 /* D(bug("l2 row %d : ideal = %d with w=%d, A=%d, W=%d\n", */
2463 /* i, row_infos[i].dim, */
2464 /* row_infos[i].weight, total_size_height, total_weight_vert)); */
2466 total_weight_vert -= row_infos[i].weight;
2467 total_size_height -= row_infos[i].dim;
2468 *total_init_height += row_infos[i].dim;
2471 /* calc columns widths */
2472 for (i = 0; i < data->columns; i++)
2474 if (total_weight_horiz)
2475 col_infos[i].dim =
2476 (total_size_width * col_infos[i].weight +
2477 total_weight_horiz / 2) / total_weight_horiz;
2478 else
2479 col_infos[i].dim = col_infos[i].min;
2481 /* D(bug("l2 col %d : ideal = %d with w=%d, A=%d, W=%d\n", */
2482 /* i, col_infos[i].dim, */
2483 /* col_infos[i].weight, total_size_width, total_weight_horiz)); */
2485 total_weight_horiz -= col_infos[i].weight;
2486 total_size_width -= col_infos[i].dim;
2487 *total_init_width += col_infos[i].dim;
2493 static void Layout2D_minmax_constraints_and_redistrib(struct layout2d_elem
2494 *infos, WORD nitems, WORD total_size, WORD samesize, WORD remainder)
2496 int j;
2498 /* D(bug("L2D mc&r n=%d A=%d ss=%d rem=%d\n", */
2499 /* nitems, total_size, samesize, remainder)); */
2501 for (j = 0; j < 2; j++)
2503 WORD size_growables = total_size;
2504 WORD size_shrinkables = total_size;
2505 ULONG weight_growables = 0;
2506 ULONG weight_shrinkables = 0;
2507 /* WORD old_size; */
2508 int i;
2510 // minmax constraints
2511 for (i = 0; i < nitems; i++)
2513 /* old_size = infos[i].dim; */
2515 /* D(bug("bef loop1 on %d : size=%d, rem=%d, A=%d, " */
2516 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2517 /* i, infos[i].dim, remainder, total_size, */
2518 /* size_growables, size_shrinkables, infos[i].weight, */
2519 /* infos[i].min, infos[i].max)); */
2521 Layout1D_minmax_constraint(&infos[i].dim, infos[i].min,
2522 infos[i].max, &remainder, &size_growables,
2523 &size_shrinkables, &weight_growables, &weight_shrinkables,
2524 infos[i].weight, samesize);
2526 /* D(bug("loop1 on %d : size=%d was %d, rem=%d, A=%d, " */
2527 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2528 /* i, infos[i].dim, old_size, remainder, total_size, */
2529 /* size_growables, size_shrinkables, infos[i].weight, */
2530 /* infos[i].min, infos[i].max)); */
2533 if (remainder == 0)
2534 break;
2536 for (i = 0; i < nitems; i++)
2538 /* old_size = infos[i].dim; */
2540 /* D(bug("bef loop2 on %d : size=%d, rem=%d, A=%d, " */
2541 /* "size_grow=%d, size_shrink=%d\n", i, */
2542 /* infos[i].dim, remainder, total_size, */
2543 /* size_growables, size_shrinkables)); */
2545 Layout1D_redistribution(&infos[i].dim, infos[i].min,
2546 infos[i].max, &remainder, &size_growables,
2547 &size_shrinkables, &weight_growables, &weight_shrinkables,
2548 infos[i].weight);
2550 /* D(bug("loop2 on %d : size=%d was %d, rem=%d, A=%d, " */
2551 /* "size_grow=%d, size_shrink=%d\n", i, */
2552 /* infos[i].dim, old_size, remainder, total_size, */
2553 /* size_growables, size_shrinkables)); */
2558 static void
2559 layout_2d_distribute_space(struct MUI_GroupData *data,
2560 struct layout2d_elem *row_infos,
2561 struct layout2d_elem *col_infos,
2562 struct MinList *children, LONG left_start, LONG top_start)
2564 Object *cstate;
2565 Object *child;
2566 LONG left;
2567 LONG top;
2568 LONG col_width;
2569 LONG row_height;
2570 int i, j;
2573 * pass 2 : distribute space
2575 cstate = (Object *) children->mlh_Head;
2576 top = top_start;
2577 /* for each row */
2578 for (i = 0; i < data->rows; i++)
2580 /* left start for child layout in this row */
2581 left = left_start;
2583 /* max height for children in this row */
2584 row_height = row_infos[i].dim;
2585 j = 0;
2586 /* for each column */
2587 while ((child = NextObject(&cstate)))
2589 LONG cleft;
2590 LONG ctop;
2591 LONG cwidth;
2592 LONG cheight;
2594 if (IS_HIDDEN(child))
2595 continue;
2596 /* max width for children in this column */
2597 col_width = col_infos[j].dim;
2599 /* center child if col width is bigger than child maxwidth */
2600 cwidth = MIN(_maxwidth(child), col_width);
2601 cwidth = MAX(cwidth, _minwidth(child));
2602 cleft = left + (col_width - cwidth) / 2;
2604 /* center child if row height is bigger than child maxheight */
2605 cheight = MIN(_maxheight(child), row_height);
2606 cheight = MAX(cheight, _minheight(child));
2607 ctop = top + (row_height - cheight) / 2;
2609 /* g_print("layout %d %d %d %d\n", cleft, ctop, cwidth, cheight); */
2610 /* D(bug("2DL/child %p -> layout %d x %d\n", */
2611 /* child, cwidth, cheight)); */
2612 if (!MUI_Layout(child, cleft, ctop, cwidth, cheight, 0))
2613 return;
2615 left += data->horiz_spacing + col_width;
2617 ++j;
2618 if ((j % data->columns) == 0)
2619 break;
2622 top += data->vert_spacing + row_height;
2629 * all children in the same row have the same maximum height
2630 * all children in the same column have the same maximum height
2631 * if a child maximum size is smaller than the biggest minimum size,
2632 * the chid will be centered in the remaining space.
2634 * for each row, determine its height allocation
2635 * weight ? the vertical weight of a row, if no fixed-height child
2636 * in the row, is the sum of all vertical weights of children
2637 * all row members will have the same height
2639 * for each column, determine its width allocation
2640 * all column members will have the same width
2642 /* Write a proper hook function */
2643 static void
2644 group_layout_2d(struct IClass *cl, Object *obj, struct MinList *children)
2646 struct MUI_GroupData *data = INST_DATA(cl, obj);
2647 WORD left_start = 0;
2648 WORD top_start = 0;
2649 WORD total_size_height =
2650 _mheight(obj) - (data->rows - 1) * data->vert_spacing;
2651 WORD total_size_width =
2652 _mwidth(obj) - (data->columns - 1) * data->horiz_spacing;
2653 WORD total_init_height;
2654 WORD total_init_width;
2655 WORD remainder_height;
2656 WORD remainder_width;
2657 WORD layout_width;
2658 WORD layout_height;
2660 if (data->rows == 0 || data->columns == 0)
2661 return;
2662 if (data->num_children % data->rows
2663 || data->num_children % data->columns)
2664 return;
2665 if (data->row_infos == NULL || data->col_infos == NULL)
2666 return;
2668 //kprintf("group_layout_horiz: virtoff = %d,%d\n",
2669 // data->virt_offx, data->virt_offy);
2671 if (data->flags & GROUP_VIRTUAL)
2673 data->virt_mwidth =
2674 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2675 _maxwidth(obj) - _subwidth(obj));
2676 data->virt_mheight =
2677 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2678 _maxheight(obj) - _subheight(obj));
2680 layout_width = data->virt_mwidth;
2681 layout_height = data->virt_mheight;
2683 else
2685 layout_width = _mwidth(obj);
2686 layout_height = _mheight(obj);
2689 total_size_height =
2690 layout_height - (data->rows - 1) * data->vert_spacing;
2691 total_size_width =
2692 layout_width - (data->columns - 1) * data->horiz_spacing;
2694 // fix left/top ?
2696 // weight constraints
2697 Layout2D_weight_constraint(data, data->row_infos, data->col_infos,
2698 total_size_height, total_size_width,
2699 &total_init_height, &total_init_width);
2701 remainder_height = total_size_height - total_init_height;
2702 remainder_width = total_size_width - total_init_width;
2704 Layout2D_minmax_constraints_and_redistrib(data->row_infos,
2705 data->rows, total_size_height,
2706 /* (data->flags & GROUP_SAME_HEIGHT) ? data->samesize_maxmin_vert : 0, */
2707 0, remainder_height);
2709 Layout2D_minmax_constraints_and_redistrib(data->col_infos,
2710 data->columns, total_size_width,
2711 /* (data->flags & GROUP_SAME_WIDTH) ? data->samesize_maxmin_horiz : 0, */
2712 0, remainder_width);
2714 layout_2d_distribute_space(data, data->row_infos, data->col_infos,
2715 children, left_start, top_start);
2719 /* Write a proper hook function */
2720 static void group_layout_pagemode(struct IClass *cl, Object *obj,
2721 struct MinList *children)
2723 struct MUI_GroupData *data = INST_DATA(cl, obj);
2724 Object *cstate;
2725 Object *child;
2726 WORD layout_width;
2727 WORD layout_height;
2728 int w, h, yoffset = 0;
2730 if (data->flags & GROUP_VIRTUAL)
2732 data->virt_mwidth =
2733 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2734 _maxwidth(obj) - _subwidth(obj));
2735 data->virt_mheight =
2736 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2737 _maxheight(obj) - _subheight(obj));
2739 layout_width = data->virt_mwidth;
2740 layout_height = data->virt_mheight;
2742 else
2744 layout_width = _mwidth(obj);
2745 layout_height = _mheight(obj);
2748 if (data->titlegroup)
2750 yoffset = _minheight(data->titlegroup);
2751 layout_height -= yoffset;
2754 cstate = (Object *) children->mlh_Head;
2755 while ((child = NextObject(&cstate)))
2757 w = MIN(layout_width, _maxwidth(child));
2758 h = MIN(layout_height, _maxheight(child));
2760 if (child == data->titlegroup)
2762 MUI_Layout(child, (layout_width - w) / 2, 0, w, yoffset, 0);
2764 else
2766 D(bug("PM/child %p -> layout %d x %d\n", child, w, h));
2767 MUI_Layout(child, (layout_width - w) / 2,
2768 yoffset + (layout_height - h) / 2, w, h, 0);
2774 /**************************************************************************
2775 MUIM_Layout
2776 Either use a given layout hook, or the builtin method.
2777 **************************************************************************/
2778 IPTR Group__MUIM_Layout(struct IClass *cl, Object *obj,
2779 struct MUIP_Layout *msg)
2781 struct MUI_GroupData *data = INST_DATA(cl, obj);
2782 struct MUI_LayoutMsg lm = { 0 };
2784 get(data->family, MUIA_Family_List, &(lm.lm_Children));
2785 if (data->flags & GROUP_PAGEMODE)
2787 group_layout_pagemode(cl, obj, lm.lm_Children);
2789 else if (data->layout_hook)
2791 lm.lm_Type = MUILM_LAYOUT;
2792 lm.lm_Layout.Width = _mwidth(obj);
2793 lm.lm_Layout.Height = _mheight(obj);
2795 CallHookPkt(data->layout_hook, obj, &lm);
2797 if (data->flags & GROUP_VIRTUAL)
2799 data->virt_mwidth = lm.lm_Layout.Width;
2800 data->virt_mheight = lm.lm_Layout.Height;
2803 else
2805 if ((data->rows == 1) && (data->columns == 1))
2807 if (data->flags & GROUP_HORIZ)
2808 group_layout_horiz(cl, obj, lm.lm_Children);
2809 else
2810 group_layout_vert(cl, obj, lm.lm_Children);
2812 else
2813 group_layout_2d(cl, obj, lm.lm_Children);
2816 if (data->flags & GROUP_VIRTUAL)
2818 WORD new_virt_offx, new_virt_offy;
2820 new_virt_offx = data->virt_offx;
2821 new_virt_offy = data->virt_offy;
2823 if (new_virt_offx + _mwidth(obj) > data->virt_mwidth)
2825 new_virt_offx = data->virt_mwidth - _mwidth(obj);
2827 if (new_virt_offx < 0)
2828 new_virt_offx = 0;
2830 if (new_virt_offy + _mheight(obj) > data->virt_mheight)
2832 new_virt_offy = data->virt_mheight - _mheight(obj);
2834 if (new_virt_offy < 0)
2835 new_virt_offy = 0;
2837 if (new_virt_offx != data->virt_offx)
2839 nfset(obj, MUIA_Virtgroup_Left, new_virt_offx);
2842 if (new_virt_offy != data->virt_offy)
2844 nfset(obj, MUIA_Virtgroup_Top, new_virt_offy);
2849 return 0;
2852 /**************************************************************************
2853 MUIM_Show
2854 **************************************************************************/
2855 IPTR Group__MUIM_Show(struct IClass *cl, Object *obj,
2856 struct MUIP_Show *msg)
2858 struct MUI_GroupData *data = INST_DATA(cl, obj);
2859 Object *cstate;
2860 Object *child;
2861 struct MinList *ChildList = NULL;
2863 /* If msg is NULL, we won't want that the super method actually gets
2864 * this call */
2865 if (msg)
2866 DoSuperMethodA(cl, obj, (Msg) msg);
2868 get(data->family, MUIA_Family_List, &(ChildList));
2869 cstate = (Object *) ChildList->mlh_Head;
2871 if (data->flags & GROUP_PAGEMODE)
2873 int page = 0;
2874 while ((child = NextObject(&cstate)))
2876 if (child == data->titlegroup)
2878 DoShowMethod(child);
2879 continue; /* Title group is not counted as page */
2882 if (page == data->active_page)
2884 DoShowMethod(child);
2885 break;
2887 page++;
2890 else
2892 while ((child = NextObject(&cstate)))
2894 if (!(data->flags & GROUP_VIRTUAL) ||
2895 IsObjectVisible(child, MUIMasterBase))
2897 if (_flags(child) & MADF_SHOWME)
2898 DoShowMethod(child);
2902 return TRUE;
2905 /**************************************************************************
2906 MUIM_Hide
2907 **************************************************************************/
2908 IPTR Group__MUIM_Hide(struct IClass *cl, Object *obj,
2909 struct MUIP_Hide *msg)
2911 struct MUI_GroupData *data = INST_DATA(cl, obj);
2912 Object *cstate;
2913 Object *child;
2914 struct MinList *ChildList = NULL;
2916 get(data->family, MUIA_Family_List, &(ChildList));
2917 cstate = (Object *) ChildList->mlh_Head;
2919 if (data->flags & GROUP_PAGEMODE)
2921 int page = 0;
2922 while ((child = NextObject(&cstate)))
2924 if (child == data->titlegroup)
2926 DoHideMethod(child);
2927 continue; /* Title group is not counted as page */
2930 if (page == data->active_page)
2932 DoHideMethod(child);
2933 break;
2935 page++;
2938 else
2940 while ((child = NextObject(&cstate)))
2942 if (_flags(child) & MADF_CANDRAW)
2943 DoHideMethod(child);
2947 /* If msg is NULL, we won't want that the super method actually gets
2948 * this call */
2949 if (msg)
2950 return DoSuperMethodA(cl, obj, (Msg) msg);
2951 return 1;
2955 * MUIM_FindUData : tests if the MUIA_UserData of the object
2956 * contains the given <udata> and returns the object pointer in this case.
2958 IPTR Group__MUIM_FindUData(struct IClass *cl, Object *obj,
2959 struct MUIP_FindUData *msg)
2961 struct MUI_GroupData *data = INST_DATA(cl, obj);
2963 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2964 return (IPTR) obj;
2966 return DoMethodA(data->family, (Msg) msg);
2971 * MUIM_GetUData : This method tests if the MUIA_UserData of the object
2972 * contains the given <udata> and gets <attr> to <storage> for itself
2973 * in this case.
2975 IPTR Group__MUIM_GetUData(struct IClass *cl, Object *obj,
2976 struct MUIP_GetUData *msg)
2978 struct MUI_GroupData *data = INST_DATA(cl, obj);
2980 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2982 get(obj, msg->attr, msg->storage);
2983 return TRUE;
2986 return DoMethodA(data->family, (Msg) msg);
2991 * MUIM_SetUData : This method tests if the MUIA_UserData of the object
2992 * contains the given <udata> and sets <attr> to <val> for itself in this case.
2994 IPTR Group__MUIM_SetUData(struct IClass *cl, Object *obj,
2995 struct MUIP_SetUData *msg)
2997 struct MUI_GroupData *data = INST_DATA(cl, obj);
2999 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
3000 set(obj, msg->attr, msg->val);
3002 DoMethodA(data->family, (Msg) msg);
3003 return TRUE;
3008 * MUIM_SetUDataOnce : This method tests if the MUIA_UserData of the object
3009 * contains the given <udata> and sets <attr> to <val> for itself in this case.
3010 * Stop after the first udata found.
3012 IPTR Group__MUIM_SetUDataOnce(struct IClass *cl, Object *obj,
3013 struct MUIP_SetUData *msg)
3015 struct MUI_GroupData *data = INST_DATA(cl, obj);
3017 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
3019 set(obj, msg->attr, msg->val);
3020 return TRUE;
3022 return DoMethodA(data->family, (Msg) msg);
3025 /**************************************************************************
3026 MUIM_DragQueryExtented
3027 **************************************************************************/
3028 IPTR Group__MUIM_DragQueryExtended(struct IClass *cl, Object *obj,
3029 struct MUIP_DragQueryExtended *msg)
3031 struct MUI_GroupData *data = INST_DATA(cl, obj);
3032 Object *cstate;
3033 Object *child;
3034 Object *found_obj;
3035 struct MinList *ChildList = NULL;
3037 get(data->family, MUIA_Family_List, &(ChildList));
3038 cstate = (Object *) ChildList->mlh_Head;
3039 while ((child = NextObject(&cstate)))
3041 if (!(_flags(child) & MADF_CANDRAW))
3042 continue;
3044 if ((found_obj = (Object *) DoMethodA(child, (Msg) msg)))
3045 return (IPTR) found_obj;
3047 return DoSuperMethodA(cl, obj, (Msg) msg);
3050 /**************************************************************************
3051 MUIM_HandleEvent
3052 **************************************************************************/
3053 IPTR Group__MUIM_HandleEvent(struct IClass *cl, Object *obj,
3054 struct MUIP_HandleEvent *msg)
3056 struct MUI_GroupData *data = INST_DATA(cl, obj);
3058 /* check this, otherwise a superclass who has IDCMP_MOUSEBUTTONS
3059 eventhandler might call DoSuperMethod, and this function gets
3060 called even when he have not added any eventhandler */
3062 if ((data->flags & GROUP_VIRTUAL) && msg->imsg)
3064 switch (msg->imsg->Class)
3066 case IDCMP_MOUSEBUTTONS:
3067 /* For virtual groups */
3068 if (msg->imsg->Code == SELECTDOWN)
3070 if (_between(_mleft(obj), msg->imsg->MouseX, _mright(obj))
3071 && _between(_mtop(obj), msg->imsg->MouseY,
3072 _mbottom(obj)))
3074 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
3075 (IPTR) & data->ehn);
3076 data->ehn.ehn_Events |= IDCMP_INTUITICKS;
3077 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
3078 (IPTR) & data->ehn);
3081 else
3083 if (data->ehn.ehn_Events & IDCMP_INTUITICKS)
3085 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
3086 (IPTR) & data->ehn);
3087 data->ehn.ehn_Events &= ~IDCMP_INTUITICKS;
3088 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
3089 (IPTR) & data->ehn);
3092 break;
3094 case IDCMP_INTUITICKS:
3095 if (!(data->ehn.ehn_Events & IDCMP_INTUITICKS))
3096 break;
3098 if (!(_between(_mleft(obj), msg->imsg->MouseX, _mright(obj))
3099 && _between(_mtop(obj), msg->imsg->MouseY,
3100 _mbottom(obj))))
3102 LONG new_virt_offx = data->virt_offx;
3103 LONG new_virt_offy = data->virt_offy;
3105 if (msg->imsg->MouseX < _mleft(obj))
3107 /* scroll left */
3108 if (new_virt_offx >= 4)
3109 new_virt_offx -= 4;
3110 else
3111 new_virt_offx = 0;
3113 else if (msg->imsg->MouseX > _mright(obj))
3115 /* scroll right */
3116 new_virt_offx += 4;
3117 if (new_virt_offx > data->virt_mwidth - _mwidth(obj))
3118 new_virt_offx = data->virt_mwidth - _mwidth(obj);
3119 if (new_virt_offx < 0)
3120 new_virt_offx = 0;
3123 if (msg->imsg->MouseY < _mtop(obj))
3125 /* scroll top */
3126 if (new_virt_offy >= 4)
3127 new_virt_offy -= 4;
3128 else
3129 new_virt_offy = 0;
3131 else if (msg->imsg->MouseY > _mbottom(obj))
3133 /* scroll bottom */
3134 new_virt_offy += 4;
3135 if (new_virt_offy > data->virt_mheight - _mheight(obj))
3136 new_virt_offy = data->virt_mheight - _mheight(obj);
3137 if (new_virt_offy < 0)
3138 new_virt_offy = 0;
3141 if (new_virt_offx != data->virt_offx
3142 || new_virt_offy != data->virt_offy)
3144 SetAttrs(obj,
3145 MUIA_Virtgroup_Left, new_virt_offx,
3146 MUIA_Virtgroup_Top, new_virt_offy,
3147 MUIA_Group_Forward, FALSE, TAG_DONE);
3150 break;
3154 return 0;
3157 /**************************************************************************
3158 MUIM_DrawBackground
3159 **************************************************************************/
3160 IPTR Group__MUIM_DrawBackground(struct IClass *cl, Object *obj,
3161 struct MUIP_DrawBackground *msg)
3163 struct MUI_GroupData *data = INST_DATA(cl, obj);
3165 if (data->flags & GROUP_VIRTUAL)
3167 struct MUIP_DrawBackground msg2 = *msg;
3169 msg2.xoffset += data->virt_offx;
3170 msg2.yoffset += data->virt_offy;
3172 return DoSuperMethodA(cl, obj, (Msg) & msg2);
3175 return DoSuperMethodA(cl, obj, (Msg) msg);
3178 /**************************************************************************
3179 MUIM_FindAreaObject
3180 Find the given object or return NULL
3181 **************************************************************************/
3182 IPTR Group__MUIM_FindAreaObject(struct IClass *cl, Object *obj,
3183 struct MUIP_FindAreaObject *msg)
3185 struct MUI_GroupData *data = INST_DATA(cl, obj);
3186 Object *cstate;
3187 Object *child;
3188 struct MinList *ChildList = NULL;
3190 // it's me ?
3191 if (msg->obj == obj)
3192 return (IPTR) obj;
3194 // it's one of my children ?
3195 get(data->family, MUIA_Family_List, &(ChildList));
3196 cstate = (Object *) ChildList->mlh_Head;
3197 while ((child = NextObject(&cstate)))
3199 if (msg->obj == child)
3200 return (IPTR) child;
3203 // let the children find it
3204 get(data->family, MUIA_Family_List, &(ChildList));
3205 cstate = (Object *) ChildList->mlh_Head;
3206 while ((child = NextObject(&cstate)))
3208 Object *res = (Object *) DoMethodA(child, (Msg) msg);
3209 if (res != NULL)
3210 return (IPTR) res;
3213 return (IPTR) NULL;
3216 /**************************************************************************
3217 MUIM_Export : to export an object's "contents" to a dataspace object.
3218 **************************************************************************/
3219 static IPTR Group__MUIM_Export(struct IClass *cl, Object *obj,
3220 struct MUIP_Export *msg)
3222 struct MUI_GroupData *data = INST_DATA(cl, obj);
3223 Object *cstate;
3224 Object *child;
3225 struct MinList *ChildList = NULL;
3227 get(data->family, MUIA_Family_List, &(ChildList));
3228 if (!ChildList)
3229 return 0;
3231 cstate = (Object *) ChildList->mlh_Head;
3232 while ((child = NextObject(&cstate)))
3234 DoMethodA(child, (Msg) msg);
3237 return 0;
3241 /**************************************************************************
3242 MUIM_Import : to import an object's "contents" from a dataspace object.
3243 **************************************************************************/
3244 static IPTR Group__MUIM_Import(struct IClass *cl, Object *obj,
3245 struct MUIP_Import *msg)
3247 struct MUI_GroupData *data = INST_DATA(cl, obj);
3248 Object *cstate;
3249 Object *child;
3250 struct MinList *ChildList = NULL;
3252 get(data->family, MUIA_Family_List, &(ChildList));
3253 if (!ChildList)
3254 return 0;
3256 cstate = (Object *) ChildList->mlh_Head;
3257 while ((child = NextObject(&cstate)))
3259 DoMethodA(child, (Msg) msg);
3262 return 0;
3265 /**************************************************************************
3266 MUIM_Notify - disabled now because previous Zune versions had a OM_GET
3267 check in MUIM_Notify which is no longer the case
3268 **************************************************************************/
3269 #if 0
3270 STATIC IPTR Group_Notify(struct IClass *cl, Object *obj,
3271 struct MUIP_Notify *msg)
3273 struct MUI_GroupData *data = INST_DATA(cl, obj);
3274 Object *cstate;
3275 Object *child;
3276 struct MinList *ChildList;
3278 /* Try at first if understand the message our self
3279 ** We disable the forwarding of the OM_GET message
3280 ** as the MUIM_Notify otherwise would "think" that
3281 ** the group class actually understands the attribute
3282 ** although a child does this only
3284 data->dont_forward_get = 1;
3285 if (DoSuperMethodA(cl, obj, (Msg) msg))
3287 data->dont_forward_get = 0;
3288 return 1;
3291 /* We ourselves didn't understand the notify tag so we try the
3292 * children now */
3293 data->dont_forward_get = 0;
3295 get(data->family, MUIA_Family_List, (IPTR *) & (ChildList));
3296 cstate = (Object *) ChildList->mlh_Head;
3297 while ((child = NextObject(&cstate)))
3299 if (DoMethodA(child, (Msg) msg))
3300 return 1;
3302 return 0;
3304 #endif
3306 #if 1
3307 /* Notes about Group_Notify() and echo notification problem:
3308 It was discovered that MUI seems to have some special handling for group class
3309 which will drop notifications on the children which are found to not
3310 understand the attribute.
3312 This is done by checking if an OM_GET on the child returns TRUE.
3313 There's a little problem here because it is not known how big the storage
3314 needed for the attribute in question will be. Almost no class uses anything
3315 bigger than one IPTR. For "big" attributes those return a pointer to the data,
3316 not the data itself. Unfortuntely there are some exceptions like colorwheel
3317 class which does not return a pointer, but the data itself. So it's not
3318 enough to use one single IPTR variable (4 Bytes on 32bit machines, 8 bytes
3319 on 64 bit machines) to store the result of the test-OM_Get.
3321 There is no general way to query the size needed so if one wants to change
3322 Zune to work like MUI one needs to choose a size which one hopes will be
3323 big enough to hold all possible attributes of all classes, old, present
3324 and future ones.
3326 STATIC IPTR Group_Notify(struct IClass *cl, Object *obj,
3327 struct MUIP_Notify *msg)
3329 struct MUI_GroupData *data = INST_DATA(cl, obj);
3330 Object *cstate;
3331 Object *child;
3332 struct MinList *ChildList = NULL;
3333 IPTR attr[30];
3335 data->dont_forward_get = 1;
3337 if (GetAttr(msg->TrigAttr, obj, attr))
3339 data->dont_forward_get = 0;
3340 return DoSuperMethodA(cl, obj, (Msg) msg);
3342 data->dont_forward_get = 0;
3344 get(data->family, MUIA_Family_List, &(ChildList));
3345 if (!ChildList)
3346 return TRUE;
3348 cstate = (Object *) ChildList->mlh_Head;
3349 while ((child = NextObject(&cstate)))
3352 if (GetAttr(msg->TrigAttr, child, attr))
3354 DoMethodA(child, (Msg) msg);
3355 /* No return here! */
3358 return TRUE;
3360 #endif
3362 BOOPSI_DISPATCHER(IPTR, Group_Dispatcher, cl, obj, msg)
3364 switch (msg->MethodID)
3366 case OM_NEW:
3367 return Group__OM_NEW(cl, obj, (struct opSet *)msg);
3368 case OM_DISPOSE:
3369 return Group__OM_DISPOSE(cl, obj, msg);
3370 case OM_SET:
3371 return Group__OM_SET(cl, obj, (struct opSet *)msg);
3372 case OM_GET:
3373 return Group__OM_GET(cl, obj, (struct opGet *)msg);
3374 case OM_ADDMEMBER: /* Fall through */
3375 case MUIM_Group_AddTail:
3376 return Group__MUIM_AddTail(cl, obj, (APTR) msg);
3377 case MUIM_Group_AddHead:
3378 return Group__MUIM_AddHead(cl, obj, (APTR) msg);
3379 case MUIM_Group_Insert:
3380 return Group__MUIM_Insert(cl, obj, (APTR) msg);
3381 case OM_REMMEMBER: /* Fall through */
3382 case MUIM_Group_Remove:
3383 return Group__MUIM_Remove(cl, obj, (APTR) msg);
3384 case MUIM_Family_GetChild:
3385 return Group__MUIM_Family_GetChild(cl, obj, (APTR) msg);
3386 case MUIM_AskMinMax:
3387 return Group__MUIM_AskMinMax(cl, obj, (APTR) msg);
3388 case MUIM_Group_ExitChange:
3389 return Group__MUIM_ExitChange(cl, obj, (APTR) msg);
3390 case MUIM_Group_ExitChange2:
3391 return Group__MUIM_ExitChange2(cl, obj, (APTR) msg);
3392 case MUIM_Group_InitChange:
3393 return Group__MUIM_InitChange(cl, obj, (APTR) msg);
3394 case MUIM_Group_Sort:
3395 return Group__MUIM_Sort(cl, obj, (APTR) msg);
3396 case MUIM_Group_DoMethodNoForward:
3397 return Group__MUIM_DoMethodNoForward(cl, obj, (APTR) msg);
3398 case MUIM_ConnectParent:
3399 return Group__MUIM_ConnectParent(cl, obj, (APTR) msg);
3400 case MUIM_DisconnectParent:
3401 return Group__MUIM_DisconnectParent(cl, obj, (APTR) msg);
3402 case MUIM_Layout:
3403 return Group__MUIM_Layout(cl, obj, (APTR) msg);
3404 case MUIM_Setup:
3405 return Group__MUIM_Setup(cl, obj, (APTR) msg);
3406 case MUIM_Cleanup:
3407 return Group__MUIM_Cleanup(cl, obj, (APTR) msg);
3408 case MUIM_Draw:
3409 return Group__MUIM_Draw(cl, obj, (APTR) msg);
3411 case MUIM_FindUData:
3412 return Group__MUIM_FindUData(cl, obj, (APTR) msg);
3413 case MUIM_GetUData:
3414 return Group__MUIM_GetUData(cl, obj, (APTR) msg);
3415 case MUIM_SetUData:
3416 return Group__MUIM_SetUData(cl, obj, (APTR) msg);
3417 case MUIM_SetUDataOnce:
3418 return Group__MUIM_SetUDataOnce(cl, obj, (APTR) msg);
3419 case MUIM_Show:
3420 return Group__MUIM_Show(cl, obj, (APTR) msg);
3421 case MUIM_Hide:
3422 return Group__MUIM_Hide(cl, obj, (APTR) msg);
3423 case MUIM_HandleEvent:
3424 return Group__MUIM_HandleEvent(cl, obj, (APTR) msg);
3425 case MUIM_DrawBackground:
3426 return Group__MUIM_DrawBackground(cl, obj, (APTR) msg);
3427 case MUIM_DragQueryExtended:
3428 return Group__MUIM_DragQueryExtended(cl, obj, (APTR) msg);
3429 case MUIM_FindAreaObject:
3430 return Group__MUIM_FindAreaObject(cl, obj, (APTR) msg);
3431 case MUIM_Export:
3432 return Group__MUIM_Export(cl, obj, (APTR) msg);
3433 case MUIM_Import:
3434 return Group__MUIM_Import(cl, obj, (APTR) msg);
3436 //#if 0
3437 #if 1
3438 /* Disabled. See above */
3439 case MUIM_Notify:
3440 return Group_Notify(cl, obj, (APTR) msg);
3441 #endif
3442 case MUIM_Set:
3443 case MUIM_MultiSet:
3444 case MUIM_CallHook:
3445 case MUIM_DrawParentBackground:
3446 case MUIM_DragBegin:
3447 case MUIM_DragDrop:
3448 case MUIM_DragQuery:
3449 case MUIM_DragFinish:
3450 case MUIM_DoDrag:
3451 case MUIM_CreateDragImage:
3452 case MUIM_DeleteDragImage:
3453 case MUIM_GoActive:
3454 case MUIM_GoInactive:
3455 case MUIM_CreateBubble:
3456 case MUIM_DeleteBubble:
3457 case MUIM_CreateShortHelp:
3458 case MUIM_DeleteShortHelp:
3459 case OM_ADDTAIL:
3460 case OM_REMOVE:
3461 return DoSuperMethodA(cl, obj, (APTR) msg);
3462 /* Needs not to be forwarded? */
3465 /* sometimes you want to call a superclass method,
3466 * but not dispatching to child.
3467 * But what to do with list methods in a listview ?
3469 Group_DispatchMsg(cl, obj, (APTR) msg);
3471 return DoSuperMethodA(cl, obj, msg);
3473 BOOPSI_DISPATCHER_END
3476 * Class descriptor.
3478 const struct __MUIBuiltinClass _MUI_Group_desc =
3480 MUIC_Group,
3481 MUIC_Area,
3482 sizeof(struct MUI_GroupData),
3483 (void *) Group_Dispatcher