muimaster.library: allow children on group/family to know their parents immediately
[AROS.git] / workbench / libs / muimaster / classes / group.c
blobeaed919bac9b37a8f703ec6aa6e0d3fda711e8bc
1 /*
2 Copyright 1999, David Le Corfec.
3 Copyright 2002-2014, 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 /* Some apps (Odyssey) expect _parent() will work before group tree is added to application tree */
276 muiNotifyData(msgint->obj)->mnd_ParentObject = obj;
278 if (_flags(obj) & MADF_SETUP)
280 DoSetupMethod(msgint->obj, muiRenderInfo(obj));
282 /* if (_flags(obj) & MADF_CANDRAW) */
283 /* DoShowMethod(msg->opam_Object); */
285 return TRUE;
288 /**************************************************************************
289 OM_NEW - Constructor
290 **************************************************************************/
291 IPTR Group__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
293 struct MUI_GroupData *data;
294 struct TagItem *tags, *tag;
295 BOOL bad_children = FALSE;
296 IPTR disabled = FALSE;
297 IPTR frame = MUIV_Frame_None;
299 D(bug("[group.mui] OM_NEW, object 0x%p\n", obj));
301 obj = (Object *) DoSuperMethodA(cl, obj, (Msg) msg);
302 if (!obj)
303 return 0;
305 /* Initial local instance data */
306 data = INST_DATA(cl, obj);
308 data->family = MUI_NewObjectA(MUIC_Family, NULL);
309 if (!data->family)
311 CoerceMethod(cl, obj, OM_DISPOSE);
312 return 0;
315 data->horiz_spacing = -1;
316 data->vert_spacing = -1;
317 data->columns = 1;
318 data->rows = 1;
319 data->active_page = 0;
320 get(obj, MUIA_Frame, &frame);
322 /* parse initial taglist */
323 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
325 switch (tag->ti_Tag)
327 case MUIA_Group_Child:
328 D(bug("[group.mui] Adding child 0x%p\n", tag->ti_Data));
329 if (tag->ti_Data)
331 DoMethod(obj, OM_ADDMEMBER, tag->ti_Data);
332 /* Set first child as group title */
333 if ((frame == MUIV_Frame_Register)
334 && (data->titlegroup == NULL))
335 data->titlegroup = (Object *) tag->ti_Data;
337 else
338 bad_children = TRUE;
339 break;
341 case MUIA_Group_ActivePage:
342 change_active_page(cl, obj, (LONG) tag->ti_Data);
343 break;
345 case MUIA_Group_Columns:
346 data->columns = (tag->ti_Data) > 1 ? tag->ti_Data : 1;
347 data->rows = 0;
348 break;
350 case MUIA_Group_Horiz:
351 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_HORIZ);
352 break;
354 case MUIA_Group_HorizSpacing:
355 data->flags |= GROUP_HSPACING;
356 data->horiz_spacing = tag->ti_Data;
357 break;
359 case MUIA_Group_LayoutHook:
360 data->layout_hook = (struct Hook *)tag->ti_Data;
361 break;
363 case MUIA_Group_PageMode:
364 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_PAGEMODE);
365 break;
367 case MUIA_Group_Rows:
368 data->rows = MAX((ULONG) tag->ti_Data, 1);
369 data->columns = 0;
370 break;
372 case MUIA_Group_SameHeight:
373 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_HEIGHT);
374 break;
376 case MUIA_Group_SameSize:
377 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_HEIGHT);
378 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_WIDTH);
379 break;
381 case MUIA_Group_SameWidth:
382 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_WIDTH);
383 break;
385 case MUIA_Group_Spacing:
386 data->flags |= (GROUP_HSPACING | GROUP_VSPACING);
387 data->horiz_spacing = tag->ti_Data;
388 data->vert_spacing = tag->ti_Data;
389 break;
391 case MUIA_Group_VertSpacing:
392 data->flags |= GROUP_VSPACING;
393 data->vert_spacing = tag->ti_Data;
394 break;
396 case MUIA_Group_Virtual:
397 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_VIRTUAL);
398 break;
402 if (bad_children)
404 CoerceMethod(cl, obj, OM_DISPOSE);
405 return 0;
408 /* D(bug("Group_New(0x%lx)\n",obj)); */
410 if (data->flags & GROUP_VIRTUAL)
412 /* This is used by MUI_Render() to determine if group is virtual.
413 * It then installs a clip region.
414 * Also MUI_Layout() uses this. Probably for speed up reason */
415 _flags(obj) |= MADF_ISVIRTUALGROUP;
418 /* will forward MUIA_Disabled to children */
419 get(obj, MUIA_Disabled, &disabled);
420 if (disabled)
422 set(obj, MUIA_Disabled, TRUE);
425 /* This is only used for virtual groups */
426 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS; /* Will be filled on demand */
427 data->ehn.ehn_Priority = 10; /* Will hear the click before all
428 * other normal objects */
429 data->ehn.ehn_Flags = 0;
430 data->ehn.ehn_Object = obj;
431 data->ehn.ehn_Class = cl;
432 return (IPTR) obj;
435 /**************************************************************************
436 OM_DISPOSE
437 **************************************************************************/
438 IPTR Group__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
440 struct MUI_GroupData *data = INST_DATA(cl, obj);
442 if (data->row_infos != NULL)
443 mui_free(data->row_infos);
444 if (data->col_infos != NULL)
445 mui_free(data->col_infos);
446 if (data->family != NULL)
447 MUI_DisposeObject(data->family);
448 return DoSuperMethodA(cl, obj, msg);
451 /**************************************************************************
452 OM_SET
453 **************************************************************************/
454 IPTR Group__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
456 struct MUI_GroupData *data = INST_DATA(cl, obj);
457 struct TagItem *tags = msg->ops_AttrList;
458 struct TagItem *tag;
459 BOOL forward = TRUE;
460 BOOL need_recalc = FALSE;
461 IPTR retval;
463 int virt_offx = data->virt_offx, virt_offy = data->virt_offy;
465 /* There are many ways to find out what tag items provided by set()
466 ** we do know. The best way should be using NextTagItem() and simply
467 ** browsing through the list.
470 /* Parse group attributes before calling DoSuperMethodA(),
471 ** otherwise if an app for example sets up a notification
472 ** on MUIA_Group_ActivePage which calls a hook, and then
473 ** the hook function calls get(obj, MUIA_Group_ActivePage),
474 ** it would get returned the old active page instead of the new
475 ** active page
478 while ((tag = NextTagItem(&tags)) != NULL)
480 switch (tag->ti_Tag)
482 case MUIA_Group_Columns:
483 data->columns = MAX((ULONG) tag->ti_Data, 1);
484 data->rows = 0;
485 need_recalc = TRUE;
486 break;
487 case MUIA_Group_ActivePage:
488 change_active_page(cl, obj, (LONG) tag->ti_Data);
489 break;
490 case MUIA_Group_Forward:
491 forward = tag->ti_Data;
492 break;
493 case MUIA_Group_HorizSpacing:
494 data->flags |= GROUP_HSPACING;
495 data->horiz_spacing = tag->ti_Data;
496 break;
497 case MUIA_Group_Rows:
498 data->rows = MAX((ULONG) tag->ti_Data, 1);
499 data->columns = 0;
500 need_recalc = TRUE;
501 break;
502 case MUIA_Group_Spacing:
503 data->flags |= (GROUP_HSPACING | GROUP_VSPACING);
504 data->horiz_spacing = tag->ti_Data;
505 data->vert_spacing = tag->ti_Data;
506 break;
507 case MUIA_Group_VertSpacing:
508 data->flags |= GROUP_VSPACING;
509 data->vert_spacing = tag->ti_Data;
510 break;
511 case MUIA_Virtgroup_Left:
512 //kprintf("set virtgroup_left: %d\n", tag->ti_Data);
513 virt_offx = tag->ti_Data;
514 break;
516 case MUIA_Group_LayoutHook:
518 [ach] Seems like MUI supports setting this attribute after
519 initialization, even though the documentation states
520 otherwise. At least some programs use it...
522 data->layout_hook = (struct Hook *)tag->ti_Data;
523 break;
525 case MUIA_Virtgroup_Top:
526 //kprintf("set virtgroup_top: %d\n", tag->ti_Data);
527 virt_offy = tag->ti_Data;
528 break;
533 if (muiRenderInfo(obj) && need_recalc)
534 DoMethod(_win(obj), MUIM_Window_RecalcDisplay, (IPTR) obj);
536 retval = DoSuperMethodA(cl, obj, (Msg) msg);
538 /* seems to be the documented behaviour, however it should be slow! */
540 if (forward)
542 /* Attributes which are to be filtered out, so that they are ignored
543 when OM_SET is passed to group's children */
545 tags = msg->ops_AttrList;
546 while ((tag = NextTagItem(&tags)) != NULL)
548 switch (tag->ti_Tag)
550 case MUIA_HelpLine:
551 case MUIA_HelpNode:
552 case MUIA_ObjectID:
553 case MUIA_UserData:
555 case MUIA_ContextMenu:
556 case MUIA_ContextMenuTrigger:
557 case MUIA_ControlChar:
558 case MUIA_CycleChain:
559 case MUIA_Draggable:
560 case MUIA_FillArea:
561 case MUIA_Group_ActivePage:
562 case MUIA_Frame:
563 case MUIA_FrameTitle:
564 case MUIA_HorizWeight:
565 case MUIA_Pressed:
566 case MUIA_ShortHelp:
567 case MUIA_ShowMe:
568 case MUIA_VertWeight:
569 case MUIA_Weight:
570 case MUIA_Virtgroup_Left:
571 case MUIA_Virtgroup_Top:
572 case MUIA_AppMessage:
573 tag->ti_Tag = TAG_IGNORE;
574 break;
575 case MUIA_Selected:
576 /* D(bug("Group_Set(%p) MUIA_Selected forwarded\n", obj)); */
577 /* tag->ti_Tag = TAG_IGNORE; */
578 break;
582 Group_DispatchMsg(cl, obj, (Msg) msg);
586 if (virt_offx != data->virt_offx || virt_offy != data->virt_offy)
588 if (_flags(obj) & MADF_CANDRAW)
589 Group__MUIM_Hide(cl, obj, NULL);
590 data->virt_offx = virt_offx;
591 data->virt_offy = virt_offy;
592 /* Relayout ourself, this will also relayout all the children */
593 DoMethod(obj, MUIM_Layout);
594 if (_flags(obj) & MADF_CANDRAW)
595 Group__MUIM_Show(cl, obj, NULL);
596 data->update = 2;
597 MUI_Redraw(obj, MADF_DRAWUPDATE);
600 return retval;
604 /**************************************************************************
605 OM_GET
606 **************************************************************************/
607 IPTR Group__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
609 /* small macro to simplify return value storage */
610 #define STORE *(msg->opg_Storage)
612 struct MUI_GroupData *data = INST_DATA(cl, obj);
614 switch (msg->opg_AttrID)
616 case MUIA_Version:
617 STORE = __version;
618 return 1;
619 case MUIA_Revision:
620 STORE = __revision;
621 return 1;
622 case MUIA_Group_ActivePage:
623 STORE = data->active_page;
624 return 1;
625 case MUIA_Group_ChildList:
626 return GetAttr(MUIA_Family_List, data->family, msg->opg_Storage);
627 case MUIA_Group_Horiz:
628 STORE = (data->flags & GROUP_HORIZ);
629 return 1;
630 case MUIA_Group_HorizSpacing:
631 STORE = data->horiz_spacing;
632 return 1;
633 case MUIA_Group_VertSpacing:
634 STORE = data->vert_spacing;
635 return 1;
636 case MUIA_Group_ChildCount:
637 STORE = data->num_children;
638 return 1;
639 case MUIA_Virtgroup_Left:
640 STORE = data->virt_offx;
641 return 1;
642 case MUIA_Virtgroup_Top:
643 STORE = data->virt_offy;
644 return 1;
645 case MUIA_Virtgroup_Width:
646 STORE = data->virt_mwidth;
647 return 1;
648 case MUIA_Virtgroup_Height:
649 STORE = data->virt_mheight;
650 return 1;
651 case MUIA_Virtgroup_MinWidth:
652 STORE = data->saved_minwidth;
653 return 1;
654 case MUIA_Virtgroup_MinHeight:
655 STORE = data->saved_minheight;
656 return 1;
659 /* our handler didn't understand the attribute, we simply pass
660 ** it to our superclass now
662 if (DoSuperMethodA(cl, obj, (Msg) msg))
663 return 1;
665 /* seems to be the documented behaviour, however it should be slow! */
666 if (!data->dont_forward_get && !data->dont_forward_methods)
668 Object *cstate;
669 Object *child;
670 struct MinList *ChildList = NULL;
672 get(data->family, MUIA_Family_List, &(ChildList));
673 cstate = (Object *) ChildList->mlh_Head;
674 while ((child = NextObject(&cstate)))
675 if (DoMethodA(child, (Msg) msg))
676 return 1;
678 return 0;
679 #undef STORE
683 /**************************************************************************
684 MUIM_AddTail
685 **************************************************************************/
686 IPTR Group__MUIM_AddTail(struct IClass *cl, Object *obj,
687 struct MUIP_Group_AddTail *msg)
689 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
692 /**************************************************************************
693 MUIM_AddHead
694 **************************************************************************/
695 IPTR Group__MUIM_AddHead(struct IClass *cl, Object *obj,
696 struct MUIP_Group_AddHead *msg)
698 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
701 /**************************************************************************
702 MUIM_Insert
703 **************************************************************************/
704 IPTR Group__MUIM_Insert(struct IClass *cl, Object *obj,
705 struct MUIP_Group_Insert *msg)
707 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
710 /**************************************************************************
711 MUIM_Remove
712 **************************************************************************/
713 IPTR Group__MUIM_Remove(struct IClass *cl, Object *obj,
714 struct MUIP_Group_Remove *msg)
716 struct MUI_GroupData *data = INST_DATA(cl, obj);
718 if (_flags(obj) & MADF_CANDRAW)
719 DoHideMethod(msg->obj);
720 if (_flags(obj) & MADF_SETUP)
721 DoMethod(msg->obj, MUIM_Cleanup);
722 if (muiNotifyData(obj)->mnd_GlobalInfo)
724 DoMethod(msg->obj, MUIM_DisconnectParent);
725 muiNotifyData(msg->obj)->mnd_ParentObject = NULL;
727 _flags(msg->obj) &= ~MADF_INVIRTUALGROUP;
730 if ((data->flags & GROUP_CHANGING) != 0)
731 data->flags |= GROUP_CHANGED;
732 data->num_children--;
733 DoMethodA(data->family, (Msg) msg);
735 return TRUE;
739 /**************************************************************************
740 MUIM_ConnectParent
741 **************************************************************************/
742 IPTR Group__MUIM_ConnectParent(struct IClass *cl, Object *obj,
743 struct MUIP_ConnectParent *msg)
745 struct MUI_GroupData *data = INST_DATA(cl, obj);
746 Object *cstate;
747 Object *child;
748 struct MinList *ChildList = NULL;
750 DoSuperMethodA(cl, obj, (Msg) msg);
752 get(data->family, MUIA_Family_List, &(ChildList));
753 cstate = (Object *) ChildList->mlh_Head;
754 while ((child = NextObject(&cstate)))
756 if ((_flags(obj) & MADF_INVIRTUALGROUP)
757 || (data->flags & GROUP_VIRTUAL))
759 _flags(child) |= MADF_INVIRTUALGROUP;
762 /* Only children of groups can have parents */
763 muiNotifyData(child)->mnd_ParentObject = obj;
765 DoMethod(child, MUIM_ConnectParent, (IPTR) obj);
767 return TRUE;
770 /**************************************************************************
771 MUIM_DisconnectParent
772 **************************************************************************/
773 IPTR Group__MUIM_DisconnectParent(struct IClass *cl, Object *obj,
774 struct MUIP_ConnectParent *msg)
776 struct MUI_GroupData *data = INST_DATA(cl, obj);
777 Object *cstate;
778 Object *child;
779 struct MinList *ChildList = NULL;
781 get(data->family, MUIA_Family_List, &(ChildList));
782 cstate = (Object *) ChildList->mlh_Head;
783 while ((child = NextObject(&cstate)))
785 DoMethodA(child, (Msg) msg);
786 muiNotifyData(child)->mnd_ParentObject = NULL;
787 _flags(child) &= ~MADF_INVIRTUALGROUP;
789 DoSuperMethodA(cl, obj, (Msg) msg);
790 return TRUE;
794 * Put group in exchange state
796 IPTR Group__MUIM_InitChange(struct IClass *cl, Object *obj,
797 struct MUIP_Group_InitChange *msg)
799 struct MUI_GroupData *data = INST_DATA(cl, obj);
801 data->flags &= ~GROUP_CHANGED;
802 data->flags |= GROUP_CHANGING;
803 return TRUE;
808 * Will recalculate display after dynamic adding/removing
810 IPTR Group__MUIM_ExitChange(struct IClass *cl, Object *obj,
811 struct MUIP_Group_ExitChange *msg)
813 struct MUI_GroupData *data = INST_DATA(cl, obj);
815 data->flags &= ~GROUP_CHANGING;
817 if (data->flags & GROUP_CHANGED)
819 data->flags &= ~GROUP_CHANGED;
821 if ((_flags(obj) & MADF_SETUP) && _win(obj))
823 Object *win = _win(obj);
824 Object *parent = obj;
826 /* CHECKME: Don't call RecalcDisplay if one of our parents is
827 in GROUP_CHANGING state to prevent crash with Zune prefs
828 program NListtree page because NList/NListtree when
829 killing tree images in MUIM_Cleanup uses InitChange/
830 ExitChange. Zune prefs program uses InitChange/ExitChange
831 when switching page -> nesting -> mess. */
833 while ((parent = _parent(parent)))
835 struct MUI_GroupData *pdata = INST_DATA(cl, parent);
837 if (parent == win)
838 break;
840 if (pdata->flags & GROUP_CHANGING)
842 return TRUE;
847 DoMethod(win, MUIM_Window_RecalcDisplay, (IPTR) obj);
851 return TRUE;
856 * Will recalculate display after dynamic adding/removing
858 IPTR Group__MUIM_ExitChange2(struct IClass *cl, Object *obj,
859 struct MUIP_Group_ExitChange2 *msg)
861 struct MUI_GroupData *data = INST_DATA(cl, obj);
863 if (data->flags & GROUP_CHANGING)
865 data->flags &= ~(GROUP_CHANGING | GROUP_CHANGED);
867 if ((_flags(obj) & MADF_SETUP) && _win(obj))
869 Object *win = _win(obj);
870 Object *parent = obj;
872 /* CHECKME: Don't call RecalcDisplay if one of our parents is
873 in GROUP_CHANGING state to prevent crash with Zune prefs
874 program NListtree page because NList/NListtree when
875 killing tree images in MUIM_Cleanup uses InitChange/
876 ExitChange. Zune prefs program uses InitChange/ExitChange
877 when switching page -> nesting -> mess. */
879 while ((parent = _parent(parent)))
881 struct MUI_GroupData *pdata = INST_DATA(cl, parent);
883 if (parent == win)
884 break;
886 if (pdata->flags & GROUP_CHANGING)
888 return TRUE;
893 DoMethod(win, MUIM_Window_RecalcDisplay, (IPTR) obj);
897 return TRUE;
902 * Sort the family
904 IPTR Group__MUIM_Sort(struct IClass *cl, Object *obj,
905 struct MUIP_Group_Sort *msg)
907 struct MUI_GroupData *data = INST_DATA(cl, obj);
909 /* modify message */
910 msg->MethodID = MUIM_Family_Sort;
912 DoMethodA(data->family, (APTR) msg);
914 /* restore original message */
915 msg->MethodID = MUIM_Group_Sort;
916 return TRUE;
919 /**************************************************************************
920 MUIM_Group_DoMethodNoForward
922 Executes the given method but does not forward it to the children
923 **************************************************************************/
924 IPTR Group__MUIM_DoMethodNoForward(struct IClass *cl, Object *obj,
925 struct MUIP_Group_DoMethodNoForward *msg)
927 struct MUI_GroupData *data = INST_DATA(cl, obj);
928 IPTR rc;
929 data->dont_forward_methods = 1; /* disable forwarding */
930 rc = DoMethodA(obj, (Msg) & msg->DoMethodID);
931 /* Probably doesn't work correctly on AROS? */
933 data->dont_forward_methods = 0;
934 return rc;
938 * Propagate a method to group children.
940 static ULONG Group_DispatchMsg(struct IClass *cl, Object *obj, Msg msg)
942 struct MUI_GroupData *data = INST_DATA(cl, obj);
943 Object *cstate;
944 Object *child;
945 struct MinList *ChildList = NULL;
947 if (data->dont_forward_methods)
948 return TRUE;
950 get(data->family, MUIA_Family_List, &(ChildList));
951 cstate = (Object *) ChildList->mlh_Head;
952 while ((child = NextObject(&cstate)))
954 DoMethodA(child, (Msg) msg);
956 return TRUE;
960 /**************************************************************************
961 MUIM_Setup
962 **************************************************************************/
963 IPTR Group__MUIM_Setup(struct IClass *cl, Object *obj,
964 struct MUIP_Setup *msg)
966 struct MUI_GroupData *data = INST_DATA(cl, obj);
967 Object *cstate;
968 Object *cstate_copy;
969 Object *child;
970 Object *childFailed;
971 struct MinList *ChildList = NULL;
973 if (!DoSuperMethodA(cl, obj, (Msg) msg))
974 return FALSE;
976 ASSERT_VALID_PTR(muiGlobalInfo(obj));
978 if (!(data->flags & GROUP_HSPACING))
979 data->horiz_spacing = muiGlobalInfo(obj)->mgi_Prefs->group_hspacing;
980 if (!(data->flags & GROUP_VSPACING))
981 data->vert_spacing = muiGlobalInfo(obj)->mgi_Prefs->group_vspacing;
982 get(data->family, MUIA_Family_List, &(ChildList));
983 cstate = cstate_copy = (Object *) ChildList->mlh_Head;
984 while ((child = NextObject(&cstate)))
986 #if 0 /* SHOWME affects only show/hide */
987 if (!(_flags(child) & MADF_SHOWME))
988 continue;
989 #endif
991 if (!DoSetupMethod(child, msg->RenderInfo))
993 /* Send MUIM_Cleanup to all objects that received MUIM_Setup.
995 childFailed = child;
996 cstate = cstate_copy;
997 while ((child = NextObject(&cstate)) && (child != childFailed))
999 #if 0 /* SHOWME affects only show/hide */
1000 if (!(_flags(child) & MADF_SHOWME))
1001 continue;
1002 #endif
1003 DoMethod(child, MUIM_Cleanup);
1005 return FALSE;
1009 if (data->flags & GROUP_VIRTUAL)
1011 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
1012 (IPTR) & data->ehn);
1015 return TRUE;
1019 /**************************************************************************
1020 MUIM_Cleanup
1021 **************************************************************************/
1022 IPTR Group__MUIM_Cleanup(struct IClass *cl, Object *obj, Msg msg)
1024 struct MUI_GroupData *data = INST_DATA(cl, obj);
1025 Object *cstate;
1026 Object *child;
1027 struct MinList *ChildList = NULL;
1029 if (data->flags & GROUP_VIRTUAL)
1031 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
1032 (IPTR) & data->ehn);
1035 get(data->family, MUIA_Family_List, &(ChildList));
1036 cstate = (Object *) ChildList->mlh_Head;
1037 while ((child = NextObject(&cstate)))
1039 #if 0 /* SHOWME affects only show/hide */
1040 if (!(_flags(child) & MADF_SHOWME))
1041 continue;
1042 #endif
1043 DoMethodA(child, (Msg) msg);
1045 return DoSuperMethodA(cl, obj, (Msg) msg);
1050 /**************************************************************************
1051 MUIM_Draw - draw the group
1052 **************************************************************************/
1053 IPTR Group__MUIM_Draw(struct IClass *cl, Object *obj,
1054 struct MUIP_Draw *msg)
1056 struct MUI_GroupData *data = INST_DATA(cl, obj);
1057 Object *cstate;
1058 Object *child;
1059 struct MinList *ChildList = NULL;
1060 struct Rectangle group_rect; /* child_rect; */
1061 int page;
1062 struct Region *region = NULL;
1063 APTR clip = (APTR) - 1;
1065 /* D(bug("Group_Draw(%lx) %ldx%ldx%ldx%ld upd=%d page=%d\n", */
1066 /* obj,_left(obj),_top(obj),_right(obj),_bottom(obj), */
1067 /* data->update, data->active_page)); */
1068 /* D(bug("Group_Draw(%p) msg=0x%08lx flags=0x%08lx\n", */
1069 /* obj, msg->flags, _flags(obj))); */
1071 if (data->flags & GROUP_CHANGING)
1072 return FALSE;
1074 if (muiGlobalInfo(obj)->mgi_Prefs->window_redraw ==
1075 WINDOW_REDRAW_WITHOUT_CLEAR)
1077 region = NewRegion();
1078 if (region)
1080 struct Rectangle rect;
1082 rect.MinX = _left(obj);
1083 rect.MinY = _top(obj);
1084 rect.MaxX = _right(obj);
1085 rect.MaxY = _bottom(obj);
1087 OrRectRegion(region, &rect);
1088 page = -1;
1089 get(data->family, MUIA_Family_List, &(ChildList));
1090 cstate = (Object *) ChildList->mlh_Head;
1091 while ((child = NextObject(&cstate)))
1093 /*page++; */
1094 /* redraw problem with colorwheel in coloradjust register */
1095 if ((data->flags & GROUP_PAGEMODE)
1096 && ((page != data->active_page)
1097 && (child != data->titlegroup)))
1098 continue;
1100 if ((muiAreaData(child)->mad_Flags & MADF_CANDRAW)
1101 && (_width(child) > 0) && (_height(child) > 0))
1103 rect.MinX = MAX(_left(child), _mleft(obj));
1104 rect.MinY = MAX(_top(child), _mtop(obj));
1105 rect.MaxX = MIN(_right(child), _mright(obj));
1106 rect.MaxY = MIN(_bottom(child), _mbottom(obj));
1108 if ((rect.MaxX >= rect.MinX)
1109 && (rect.MaxY >= rect.MinY))
1111 ClearRectRegion(region, &rect);
1116 clip = MUI_AddClipRegion(muiRenderInfo(obj), region);
1120 DoSuperMethodA(cl, obj, (Msg) msg);
1122 if (region)
1124 MUI_RemoveClipRegion(muiRenderInfo(obj), clip);
1125 region = NULL;
1128 else
1130 DoSuperMethodA(cl, obj, (Msg) msg);
1132 /* D(bug("Group_Draw(%p) (after dsma) msg=0x%08lx flags=0x%08lx\n", */
1133 /* obj, msg->flags, _flags(obj))); */
1135 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 1)
1138 * update is set when changing active page of a page group
1139 * need to redraw background ourself
1141 DoMethod(obj, MUIM_DrawBackground,
1142 _mleft(obj), _mtop(obj), _mwidth(obj), _mheight(obj),
1143 _mleft(obj), _mtop(obj), 0);
1145 data->update = 0;
1147 else
1149 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 2)
1151 LONG left, top, right, bottom;
1152 LONG diff_virt_offx = data->virt_offx - data->old_virt_offx;
1153 LONG diff_virt_offy = data->virt_offy - data->old_virt_offy;
1154 struct Rectangle rect;
1155 struct Rectangle *clip_rect = &muiRenderInfo(obj)->mri_ClipRect;
1157 data->update = 0;
1159 if (!diff_virt_offx && !diff_virt_offy)
1161 return 1;
1164 /* sba: I don't know how MUI handle this but ScrollRasterBF() made problems when scrolling
1165 ** a (partly visible) virtual groups in a virtual group, because e.g. _mtop() is then
1166 ** smaller than the region. ScrollRasterBF() on AmigaOS then marks the complete region
1167 ** as damaged. Using ScrollWindowRaster() solved that problem but it flickers then.
1168 ** To avoid this we prevent that the scroll area is out of the region bounds.
1169 ** The region bounds are setted in MUI_Redraw() but should probably should go in the
1170 ** MUI's clip functions
1173 left = MAX(_mleft(obj), clip_rect->MinX);
1174 top = MAX(_mtop(obj), clip_rect->MinY);
1175 right = MIN(_mright(obj), clip_rect->MaxX);
1176 bottom = MIN(_mbottom(obj), clip_rect->MaxY);
1178 /* old code was
1179 ** ScrollRasterBF(_rp(obj), diff_virt_offx, diff_virt_offy, _mleft(obj), _mtop(obj), _mright(obj),_mbottom(obj));
1182 ScrollWindowRaster(_window(obj), diff_virt_offx, diff_virt_offy,
1183 left, top, right, bottom);
1185 if ((region = NewRegion()))
1187 if (diff_virt_offx)
1189 rect.MinY = top;
1190 rect.MaxY = bottom;
1192 if (diff_virt_offx > 0)
1194 rect.MinX = right - diff_virt_offx + 1;
1195 if (rect.MinX < left)
1196 rect.MinX = left;
1197 rect.MaxX = right;
1199 else
1201 rect.MinX = left;
1202 rect.MaxX = left - diff_virt_offx - 1;
1203 if (rect.MaxX > right)
1204 rect.MaxX = right;
1207 if (rect.MinX <= rect.MaxX)
1209 DoMethod(obj, MUIM_DrawBackground,
1210 rect.MinX, rect.MinY,
1211 rect.MaxX - rect.MinX + 1,
1212 rect.MaxY - rect.MinY + 1,
1213 rect.MinX, rect.MinY, 0);
1215 OrRectRegion(region, &rect);
1219 if (diff_virt_offy)
1221 rect.MinX = left;
1222 rect.MaxX = right;
1224 if (diff_virt_offy > 0)
1226 rect.MinY = bottom - diff_virt_offy + 1;
1227 if (rect.MinY < top)
1228 rect.MinY = top;
1229 rect.MaxY = bottom;
1231 else
1233 rect.MinY = top;
1234 rect.MaxY = top - diff_virt_offy - 1;
1235 if (rect.MaxY > bottom)
1236 rect.MaxY = bottom;
1238 if (rect.MinY <= rect.MaxY)
1240 DoMethod(obj, MUIM_DrawBackground,
1241 rect.MinX, rect.MinY,
1242 rect.MaxX - rect.MinX + 1,
1243 rect.MaxY - rect.MinY + 1,
1244 rect.MinX, rect.MinY, 0);
1246 OrRectRegion(region, &rect);
1252 else
1254 if (!(msg->flags & MADF_DRAWOBJECT)
1255 && !(msg->flags & MADF_DRAWALL))
1256 return TRUE;
1260 if (data->flags & GROUP_VIRTUAL && !region)
1262 /* Not really needed if MUI Draws all the objects, maybe that's
1263 * what DRAWALL is for??? */
1264 if ((region = NewRegion()))
1266 struct Rectangle rect;
1267 rect.MinX = _mleft(obj);
1268 rect.MinY = _mtop(obj);
1269 rect.MaxX = _mright(obj);
1270 rect.MaxY = _mbottom(obj);
1271 OrRectRegion(region, &rect);
1275 /* Add clipping region if we have one */
1276 if (region)
1277 clip = MUI_AddClipRegion(muiRenderInfo(obj), region);
1279 group_rect = muiRenderInfo(obj)->mri_ClipRect;
1280 page = -1;
1281 get(data->family, MUIA_Family_List, &(ChildList));
1282 cstate = (Object *) ChildList->mlh_Head;
1283 while ((child = NextObject(&cstate)))
1285 if (!(_flags(child) & MADF_SHOWME))
1286 continue;
1288 if (child != data->titlegroup)
1289 ++page;
1291 if ((data->flags & GROUP_PAGEMODE) && ((page != data->active_page)
1292 && (child != data->titlegroup)))
1294 continue;
1297 // msg->flags |= MADF_DRAWOBJECT; /* yup, do not forget */
1299 // child_rect.MinX = _left(child);
1300 // child_rect.MinY = _top(child);
1301 // child_rect.MaxX = _right(child);
1302 // child_rect.MaxY = _bottom(child);
1303 /* g_print("intersect: a=(%d, %d, %d, %d) b=(%d, %d, %d, %d)\n", */
1304 /* group_rect.x, group_rect.y, */
1305 /* group_rect.width, group_rect.height, */
1306 /* child_rect.x, child_rect.y, */
1307 /* child_rect.width, child_rect.height); */
1309 // if (gdk_rectangle_intersect(&group_rect, &child_rect,
1310 // &muiRenderInfo(obj)->mri_ClipRect))
1311 // DoMethodA(child, (Msg)msg);
1312 /* if (((msg->flags & MADF_DRAWUPDATE) && data->update) */
1313 /* || (data->flags & GROUP_PAGEMODE)) */
1314 MUI_Redraw(child, MADF_DRAWOBJECT);
1315 /* else */
1316 /* MUI_Redraw(child, msg->flags); */
1317 muiRenderInfo(obj)->mri_ClipRect = group_rect;
1318 /* g_print("set back clip to (%d, %d, %d, %d)\n", */
1319 /* group_rect.x, group_rect.y, group_rect.width, group_rect.height); */
1321 /* D(bug("Group_Draw(%p) end\n", obj)); */
1323 if (data->flags & GROUP_VIRTUAL && region && clip != (APTR) - 1)
1325 MUI_RemoveClipRegion(muiRenderInfo(obj), clip);
1328 data->old_virt_offx = data->virt_offx;
1329 data->old_virt_offy = data->virt_offy;
1330 data->update = 0;
1332 return TRUE;
1336 #define END_MINMAX() \
1337 tmp.MaxHeight = MAX(tmp.MaxHeight, tmp.MinHeight); \
1338 tmp.MaxWidth = MAX(tmp.MaxWidth, tmp.MinWidth); \
1339 tmp.DefHeight = CLAMP(tmp.DefHeight, tmp.MinHeight, tmp.MaxHeight); \
1340 tmp.DefWidth = CLAMP(tmp.DefWidth, tmp.MinWidth, tmp.MaxWidth); \
1341 msg->MinMaxInfo->MinWidth += tmp.MinWidth; \
1342 msg->MinMaxInfo->MinHeight += tmp.MinHeight; \
1343 msg->MinMaxInfo->MaxWidth += tmp.MaxWidth; \
1344 msg->MinMaxInfo->MaxHeight += tmp.MaxHeight; \
1345 msg->MinMaxInfo->DefWidth += tmp.DefWidth; \
1346 msg->MinMaxInfo->DefHeight += tmp.DefHeight;
1349 * MinMax calculation function. When this is called,
1350 * the children of your group have already been asked
1351 * about their min/max dimension so you can use their
1352 * dimensions to calculate yours.
1354 * Notes:
1355 * - Init minwidth and maxwidth with size needed for total child spacing.
1356 * - 1st pass to find maximum minimum width, to set minwidth of each child
1357 * if they should have the same width (for a row of buttons ...)
1358 * - Adjust minwidth w/o making object bigger than their max size.
1360 static void group_minmax_horiz(struct IClass *cl, Object *obj,
1361 struct MinList *children, struct MUIP_AskMinMax *msg)
1363 struct MUI_GroupData *data = INST_DATA(cl, obj);
1364 Object *cstate;
1365 Object *child;
1366 struct MUI_MinMax tmp;
1367 WORD maxminwidth = 0;
1368 BOOL found_nonzero_vweight = FALSE;
1370 tmp.MinHeight = 0;
1371 tmp.DefHeight = 0;
1372 tmp.MaxHeight = MUI_MAXMAX;
1373 if (data->num_visible_children > 0)
1375 tmp.MinWidth = tmp.DefWidth = tmp.MaxWidth =
1376 (data->num_visible_children - 1) * data->horiz_spacing;
1378 else
1380 tmp.MinWidth = tmp.DefWidth = 0;
1381 tmp.MaxWidth = MUI_MAXMAX;
1384 if (data->flags & GROUP_SAME_WIDTH)
1386 cstate = (Object *) children->mlh_Head;
1387 while ((child = NextObject(&cstate)))
1389 if (IS_HIDDEN(child))
1390 continue;
1391 maxminwidth = MAX(maxminwidth, _minwidth(child));
1395 data->samesize_maxmin_horiz = maxminwidth;
1396 /* D(bug("group_minmax_horiz(%p) : maxminwidth=%d\n", obj, maxminwidth)); */
1398 data->horiz_weight_sum = 0;
1399 cstate = (Object *) children->mlh_Head;
1400 while ((child = NextObject(&cstate)))
1402 WORD minwidth;
1404 if (IS_HIDDEN(child))
1405 continue;
1406 if (data->flags & GROUP_SAME_WIDTH)
1408 minwidth = MAX(maxminwidth, _minwidth(child));
1409 minwidth = MIN(minwidth, _maxwidth(child));
1411 else
1412 minwidth = _minwidth(child);
1413 tmp.MinWidth += minwidth;
1414 tmp.DefWidth += w0_defwidth(child);
1415 tmp.MaxWidth += w0_maxwidth(child);
1416 tmp.MaxWidth = MIN(tmp.MaxWidth, MUI_MAXMAX);
1417 tmp.MinHeight = MAX(tmp.MinHeight, _minheight(child));
1418 tmp.DefHeight = MAX(tmp.DefHeight, _defheight(child));
1420 if all children have null weight then maxheight=minheight
1421 if all but some children have null weights, the maxheight
1422 is the min of all maxheights
1424 tmp.MaxHeight = MIN(tmp.MaxHeight, _maxheight(child));
1425 data->horiz_weight_sum += _hweight(child);
1426 if (_vweight(child) > 0)
1428 found_nonzero_vweight = TRUE;
1431 if (!found_nonzero_vweight)
1433 tmp.MaxHeight = tmp.MinHeight;
1436 //if (data->flags & GROUP_VIRTUAL)
1438 //kprintf("# min %d x %d def %d x %d max %d x %d\n",
1439 // tmp.MinWidth, tmp.MinHeight,
1440 // tmp.DefWidth, tmp.DefHeight,
1441 // tmp.MaxWidth, tmp.MaxHeight);
1444 END_MINMAX();
1447 /* minmax calculation for vertical groups (see group_minmax_horiz)
1449 static void group_minmax_vert(struct IClass *cl, Object *obj,
1450 struct MinList *children, struct MUIP_AskMinMax *msg)
1452 struct MUI_GroupData *data = INST_DATA(cl, obj);
1453 Object *cstate;
1454 Object *child;
1455 struct MUI_MinMax tmp;
1456 WORD maxminheight = 0;
1457 BOOL found_nonzero_hweight = FALSE;
1459 tmp.MinWidth = 0;
1460 tmp.DefWidth = 0;
1461 tmp.MaxWidth = MUI_MAXMAX;
1462 if (data->num_visible_children > 0)
1464 tmp.MinHeight = tmp.DefHeight = tmp.MaxHeight =
1465 (data->num_visible_children - 1) * data->vert_spacing;
1467 else
1469 tmp.MinHeight = tmp.DefHeight = 0;
1470 tmp.MaxHeight = MUI_MAXMAX;
1473 if (data->flags & GROUP_SAME_HEIGHT)
1475 cstate = (Object *) children->mlh_Head;
1476 while ((child = NextObject(&cstate)))
1478 if (IS_HIDDEN(child))
1479 continue;
1480 maxminheight = MAX(maxminheight, _minheight(child));
1484 data->samesize_maxmin_vert = maxminheight;
1485 data->vert_weight_sum = 0;
1486 cstate = (Object *) children->mlh_Head;
1487 while ((child = NextObject(&cstate)))
1489 if (IS_HIDDEN(child))
1490 continue;
1492 if (data->flags & GROUP_SAME_HEIGHT)
1493 _minheight(child) = MIN(maxminheight, w0_maxheight(child));
1494 tmp.MinHeight += _minheight(child);
1495 tmp.DefHeight += w0_defheight(child);
1496 tmp.MaxHeight += w0_maxheight(child);
1497 tmp.MaxHeight = MIN(tmp.MaxHeight, MUI_MAXMAX);
1498 tmp.MinWidth = MAX(tmp.MinWidth, _minwidth(child));
1499 tmp.DefWidth = MAX(tmp.DefWidth, _defwidth(child));
1500 tmp.MaxWidth = MIN(tmp.MaxWidth, _maxwidth(child));
1501 data->vert_weight_sum += _vweight(child);
1502 if (_hweight(child) > 0)
1504 found_nonzero_hweight = TRUE;
1507 if (!found_nonzero_hweight)
1509 tmp.MaxWidth = tmp.MinWidth;
1512 END_MINMAX();
1516 static void
1517 minmax_2d_rows_pass(struct MUI_GroupData *data, struct MinList *children,
1518 struct MUI_MinMax *req, WORD maxmin_height, WORD maxdef_height)
1520 int i, j;
1521 Object *cstate;
1522 Object *child;
1524 /* do not rewind after the while, to process line by line */
1525 cstate = (Object *) children->mlh_Head;
1526 /* for each row */
1527 for (i = 0; i < data->rows; i++)
1529 /* calculate min and max height of this row */
1530 int min_h = 0, def_h = 0, max_h = MUI_MAXMAX;
1531 BOOL found_nonzero_vweight = FALSE;
1533 data->row_infos[i].weight = 0;
1535 j = 0;
1536 while ((child = NextObject(&cstate)))
1538 if (IS_HIDDEN(child))
1539 continue;
1540 if (data->flags & GROUP_SAME_HEIGHT)
1542 _minheight(child) = MIN(maxmin_height, w0_maxheight(child));
1543 _defheight(child) = MIN(maxdef_height, w0_maxheight(child));
1545 min_h = MAX(min_h, _minheight(child));
1546 def_h = MAX(def_h, w0_defheight(child));
1547 max_h = MIN(max_h, _maxheight(child));
1548 if (_vweight(child) > 0)
1550 found_nonzero_vweight = TRUE;
1551 data->row_infos[i].weight += _vweight(child);
1553 ++j;
1554 if ((j % data->columns) == 0)
1555 break;
1557 if (!found_nonzero_vweight)
1558 max_h = min_h;
1559 else
1560 max_h = MAX(max_h, min_h);
1561 /* D(bug("row %d : min_h=%d max_h=%d\n", i, min_h, max_h)); */
1563 data->row_infos[i].min = min_h;
1564 data->row_infos[i].max = max_h;
1565 data->vert_weight_sum += data->row_infos[i].weight;
1567 req->MinHeight += min_h;
1568 req->DefHeight += def_h;
1569 req->MaxHeight += max_h;
1570 if (req->MaxHeight > MUI_MAXMAX)
1571 req->MaxHeight = MUI_MAXMAX;
1576 static void
1577 minmax_2d_columns_pass(struct MUI_GroupData *data, struct MinList *children,
1578 struct MUI_MinMax *req, WORD maxmin_width, WORD maxdef_width)
1580 int i, j;
1581 Object *cstate;
1582 Object *child;
1584 for (i = 0; i < data->columns; i++)
1586 /* calculate min and max width of this column */
1587 int min_w = 0, def_w = 0, max_w = MUI_MAXMAX;
1588 BOOL found_nonzero_hweight = FALSE;
1590 data->col_infos[i].weight = 0;
1592 j = 0;
1593 /* process all children to get children on a column */
1594 cstate = (Object *) children->mlh_Head;
1595 while ((child = NextObject(&cstate)))
1597 if (IS_HIDDEN(child))
1598 continue;
1599 ++j;
1600 if (((j - 1) % data->columns) != i)
1601 continue;
1602 if (data->flags & GROUP_SAME_WIDTH)
1604 _minwidth(child) = MIN(maxmin_width, w0_maxwidth(child));
1605 _defwidth(child) = MIN(maxdef_width, w0_maxwidth(child));
1607 min_w = MAX(min_w, _minwidth(child));
1608 def_w = MAX(def_w, w0_defwidth(child));
1610 /* this handles the case of null weight children, which limit
1611 * the max size if they're alone, but not if they are with
1612 * non-null weight obj
1614 max_w = MIN(max_w, _maxwidth(child));
1615 if (_hweight(child) > 0)
1617 found_nonzero_hweight = TRUE;
1618 data->col_infos[i].weight += _hweight(child);
1621 if (!found_nonzero_hweight)
1622 max_w = min_w;
1623 else
1624 max_w = MAX(max_w, min_w);
1625 /* D(bug("col %d : min_w=%d max_w=%d\n", i, min_w, max_w)); */
1627 data->col_infos[i].min = min_w;
1628 data->col_infos[i].max = max_w;
1629 data->horiz_weight_sum += data->col_infos[i].weight;
1631 req->MinWidth += min_w;
1632 req->DefWidth += def_w;
1633 req->MaxWidth += max_w;
1634 if (req->MaxWidth > MUI_MAXMAX)
1635 req->MaxWidth = MUI_MAXMAX;
1639 static void
1640 group_minmax_2d(struct IClass *cl, Object *obj,
1641 struct MinList *children, struct MUIP_AskMinMax *msg)
1643 struct MUI_GroupData *data = INST_DATA(cl, obj);
1644 Object *cstate;
1645 Object *child;
1646 struct MUI_MinMax tmp;
1647 WORD maxmin_width;
1648 WORD maxmin_height;
1649 WORD maxdef_width;
1650 WORD maxdef_height;
1652 if (!data->columns)
1654 if (data->num_children % data->rows)
1656 data->columns = 1;
1657 data->rows = data->num_children;
1659 else
1660 data->columns = data->num_children / data->rows;
1662 else
1664 if (data->num_children % data->columns)
1666 data->rows = 1;
1667 data->columns = data->num_children;
1669 else
1670 data->rows = data->num_children / data->columns;
1673 if (data->columns < 1)
1674 data->columns = 1;
1675 if (data->rows < 1)
1676 data->rows = 1;
1678 if (data->row_infos != NULL)
1679 mui_free(data->row_infos);
1681 data->row_infos = mui_alloc(data->rows * sizeof(struct layout2d_elem));
1682 if (NULL == data->row_infos)
1683 return;
1685 if (data->col_infos != NULL)
1686 mui_free(data->col_infos);
1688 data->col_infos =
1689 mui_alloc(data->columns * sizeof(struct layout2d_elem));
1690 if (NULL == data->col_infos)
1691 return;
1693 data->horiz_weight_sum = 0;
1694 data->vert_weight_sum = 0;
1696 tmp.MinHeight = tmp.DefHeight = tmp.MaxHeight =
1697 (data->rows - 1) * data->vert_spacing;
1698 tmp.MinWidth = tmp.DefWidth = tmp.MaxWidth =
1699 (data->columns - 1) * data->horiz_spacing;
1700 /* get minimum dims if same dims for all children are needed */
1701 maxmin_width = 0;
1702 maxmin_height = 0;
1703 maxdef_width = 0;
1704 maxdef_height = 0;
1706 if ((data->flags & GROUP_SAME_WIDTH)
1707 || (data->flags & GROUP_SAME_HEIGHT))
1709 cstate = (Object *) children->mlh_Head;
1710 while ((child = NextObject(&cstate)))
1712 if (!(_flags(child) & MADF_SHOWME))
1713 continue;
1714 maxmin_width = MAX(maxmin_width, _minwidth(child));
1715 maxmin_height = MAX(maxmin_height, _minheight(child));
1716 maxdef_width = MAX(maxdef_width, w0_defwidth(child));
1717 maxdef_height = MAX(maxdef_height, w0_defheight(child));
1719 /* g_print("2d group: mminw=%d mminh=%d\n", */
1720 /* maxmin_width, maxmin_height); */
1722 if (data->flags & GROUP_SAME_HEIGHT)
1723 data->samesize_maxmin_vert = maxmin_height;
1724 else
1725 data->samesize_maxmin_vert = 0;
1727 if (data->flags & GROUP_SAME_WIDTH)
1728 data->samesize_maxmin_horiz = maxmin_width;
1729 else
1730 data->samesize_maxmin_horiz = 0;
1732 minmax_2d_rows_pass(data, children, &tmp, maxmin_height, maxdef_height);
1733 minmax_2d_columns_pass(data, children, &tmp, maxmin_width,
1734 maxdef_width);
1736 END_MINMAX();
1740 static void
1741 group_minmax_pagemode(struct IClass *cl, Object *obj,
1742 struct MinList *children, struct MUIP_AskMinMax *msg)
1744 Object *cstate;
1745 Object *child;
1746 struct MUI_GroupData *data = INST_DATA(cl, obj);
1747 struct MUI_MinMax tmp = { 0, 0, MUI_MAXMAX, MUI_MAXMAX, 0, 0 };
1749 cstate = (Object *) children->mlh_Head;
1751 D(bug("minmax_pagemode(%lx)\n", obj, tmp.DefWidth));
1753 while ((child = NextObject(&cstate)))
1755 if (!(_flags(child) & MADF_SHOWME))
1756 continue;
1758 if (child == data->titlegroup)
1759 continue;
1761 tmp.MinHeight = MAX(tmp.MinHeight, _minheight(child));
1762 D(bug("minmax_pagemode(%p) minh child = %d tmpmin=%d\n", obj,
1763 _minheight(child), tmp.MinHeight));
1764 tmp.MinWidth = MAX(tmp.MinWidth, _minwidth(child));
1765 tmp.MaxHeight = MIN(tmp.MaxHeight, w0_maxheight(child));
1766 tmp.MaxWidth = MIN(tmp.MaxWidth, w0_maxwidth(child));
1767 tmp.DefHeight = MAX(tmp.DefHeight,
1768 ((w0_defheight(child) <
1769 MUI_MAXMAX) ? w0_defheight(child) : tmp.DefHeight));
1770 tmp.DefWidth =
1771 MAX(tmp.DefWidth,
1772 ((w0_defwidth(child) <
1773 MUI_MAXMAX) ? w0_defwidth(child) : tmp.DefWidth));
1774 D(bug("minmax_pagemode(%lx) defw = %ld\n", obj, tmp.DefWidth));
1777 if (data->titlegroup)
1779 tmp.MinHeight += _minheight(data->titlegroup);
1780 tmp.MaxHeight += w0_maxheight(data->titlegroup);
1781 tmp.DefHeight += w0_defheight(data->titlegroup);
1784 END_MINMAX();
1787 /**************************************************************************
1788 MUIM_AskMinMax : ask children about min/max sizes, then
1789 either call a hook, or the builtin method, to calculate our minmax
1790 **************************************************************************/
1791 IPTR Group__MUIM_AskMinMax(struct IClass *cl, Object *obj,
1792 struct MUIP_AskMinMax *msg)
1794 struct MUI_GroupData *data = INST_DATA(cl, obj);
1795 struct MUI_LayoutMsg lm;
1796 struct MUIP_AskMinMax childMsg;
1797 struct MUI_MinMax childMinMax;
1798 Object *cstate;
1799 Object *child;
1800 LONG super_minwidth, super_minheight;
1803 * let our superclass first fill in its size with frame, inner spc etc ...
1805 DoSuperMethodA(cl, obj, (Msg) msg);
1806 super_minwidth = msg->MinMaxInfo->MinWidth;
1807 super_minheight = msg->MinMaxInfo->MinHeight;
1810 * Ask children
1812 childMsg.MethodID = msg->MethodID;
1813 childMsg.MinMaxInfo = &childMinMax;
1814 get(data->family, MUIA_Family_List, &(lm.lm_Children));
1816 cstate = (Object *) lm.lm_Children->mlh_Head;
1818 while ((child = NextObject(&cstate)))
1820 if (!(_flags(child) & MADF_SHOWME))
1821 /* BORDERGADGETs should handle this itself */
1822 continue;
1823 /* Ask child */
1824 DoMethodA(child, (Msg) & childMsg);
1825 /* D(bug("*** group %lx, child %lx min=%ld,%ld\n", */
1826 /* obj, child, childMinMax.MinWidth, childMinMax.MinHeight)); */
1827 __area_finish_minmax(child, childMsg.MinMaxInfo);
1831 * Use children infos to calculate group size
1833 if (data->flags & GROUP_PAGEMODE)
1835 D(bug("minmax_pagemode(%p) minh initial = %d\n", obj,
1836 msg->MinMaxInfo->MinHeight));
1837 group_minmax_pagemode(cl, obj, lm.lm_Children, msg);
1838 D(bug("minmax_pagemode(%p) minh = %d\n", obj,
1839 msg->MinMaxInfo->MinHeight));
1841 else if (data->layout_hook)
1843 lm.lm_Type = MUILM_MINMAX;
1844 CallHookPkt(data->layout_hook, obj, &lm);
1846 if (lm.lm_MinMax.MaxHeight < lm.lm_MinMax.MinHeight)
1847 lm.lm_MinMax.MaxHeight = lm.lm_MinMax.MinHeight;
1848 if (lm.lm_MinMax.DefHeight < lm.lm_MinMax.MinHeight)
1849 lm.lm_MinMax.DefHeight = lm.lm_MinMax.MinHeight;
1850 if (lm.lm_MinMax.MaxWidth < lm.lm_MinMax.MinWidth)
1851 lm.lm_MinMax.MaxWidth = lm.lm_MinMax.MinWidth;
1852 if (lm.lm_MinMax.DefWidth < lm.lm_MinMax.MinWidth)
1853 lm.lm_MinMax.DefWidth = lm.lm_MinMax.MinWidth;
1855 //kprintf("### min %d x %d def %d x %d max %d x %d\n",
1856 // msg->MinMaxInfo->MinWidth,
1857 // msg->MinMaxInfo->MinHeight,
1858 // msg->MinMaxInfo->DefWidth,
1859 // msg->MinMaxInfo->DefHeight,
1860 // msg->MinMaxInfo->MaxWidth,
1861 // msg->MinMaxInfo->MaxHeight);
1863 msg->MinMaxInfo->MinWidth += lm.lm_MinMax.MinWidth;
1864 msg->MinMaxInfo->MinHeight += lm.lm_MinMax.MinHeight;
1865 msg->MinMaxInfo->MaxWidth += lm.lm_MinMax.MaxWidth;
1866 if (msg->MinMaxInfo->MaxWidth > MUI_MAXMAX)
1867 msg->MinMaxInfo->MaxWidth = MUI_MAXMAX;
1868 msg->MinMaxInfo->MaxHeight += lm.lm_MinMax.MaxHeight;
1869 if (msg->MinMaxInfo->MaxHeight > MUI_MAXMAX)
1870 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1871 msg->MinMaxInfo->DefWidth += lm.lm_MinMax.DefWidth;
1872 msg->MinMaxInfo->DefHeight += lm.lm_MinMax.DefHeight;
1874 //kprintf("#### min %d x %d def %d x %d max %d x %d\n",
1875 // msg->MinMaxInfo->MinWidth,
1876 // msg->MinMaxInfo->MinHeight,
1877 // msg->MinMaxInfo->DefWidth,
1878 // msg->MinMaxInfo->DefHeight,
1879 // msg->MinMaxInfo->MaxWidth,
1880 // msg->MinMaxInfo->MaxHeight);
1883 else
1885 if ((data->rows == 1) && (data->columns == 1))
1887 data->num_visible_children =
1888 Group_GetNumVisibleChildren(data, lm.lm_Children);
1889 if (data->flags & GROUP_HORIZ)
1890 group_minmax_horiz(cl, obj, lm.lm_Children, msg);
1891 else
1892 group_minmax_vert(cl, obj, lm.lm_Children, msg);
1894 else
1896 group_minmax_2d(cl, obj, lm.lm_Children, msg);
1900 if (data->flags & GROUP_VIRTUAL)
1902 data->saved_minwidth = msg->MinMaxInfo->MinWidth;
1903 data->saved_minheight = msg->MinMaxInfo->MinHeight;
1904 msg->MinMaxInfo->MinWidth = super_minwidth + 2;
1905 msg->MinMaxInfo->MinHeight = super_minheight + 2;
1907 //kprintf("## min %d x %d def %d x %d max %d x %d\n",
1908 // msg->MinMaxInfo->MinWidth,
1909 // msg->MinMaxInfo->MinHeight,
1910 // msg->MinMaxInfo->DefWidth,
1911 // msg->MinMaxInfo->DefHeight,
1912 // msg->MinMaxInfo->MaxWidth,
1913 // msg->MinMaxInfo->MaxHeight);
1917 return 0;
1922 // enforce minmax constraint, but also update total growable/shrinkable weights
1923 // while we're at it
1924 static void Layout1D_minmax_constraint(WORD *sizep, WORD minsize,
1925 WORD maxsize, WORD *remainp, WORD *sizegrowp, WORD *sizeshrinkp,
1926 ULONG *weightgrowp, ULONG *weightshrinkp, UWORD weight, WORD samesize)
1928 WORD size = *sizep, remain = *remainp,
1929 sizegrow = *sizegrowp, sizeshrink = *sizeshrinkp;
1930 ULONG weightgrow = *weightgrowp, weightshrink = *weightshrinkp;
1932 /* D(bug("L1D_minmax_c size=%d min=%d max=%d w=%d ss=%d\n", */
1933 /* size, minsize, maxsize, */
1934 /* weight, samesize)); */
1936 if ((samesize > 0) && (weight == 0))
1938 remain += size - samesize;
1939 size = samesize;
1940 sizeshrink -= size;
1941 sizegrow -= size;
1944 if (size <= minsize) // too little
1946 remain += size - minsize;
1947 size = minsize;
1948 sizeshrink -= size;
1949 if (size == maxsize)
1950 sizegrow -= size;
1952 else if (size >= maxsize) // too big
1954 remain += size - maxsize;
1955 size = maxsize;
1956 sizegrow -= size;
1959 if (!((samesize > 0) && (weight == 0)))
1961 if (size < maxsize)
1962 weightgrow += weight;
1963 if (size > minsize)
1964 weightshrink += weight;
1967 *sizep = size;
1968 *remainp = remain;
1969 *sizegrowp = sizegrow;
1970 *sizeshrinkp = sizeshrink;
1971 *weightgrowp = weightgrow;
1972 *weightshrinkp = weightshrink;
1976 // redistribute excess size to growable child, or reduce size of a shrinkable
1977 // child
1978 static void Layout1D_redistribution(WORD *sizep, WORD minsize,
1979 WORD maxsize, WORD *remainp, WORD *sizegrowp, WORD *sizeshrinkp,
1980 ULONG *weightgrowp, ULONG *weightshrinkp, UWORD weight)
1982 WORD size = *sizep, remain = *remainp,
1983 sizegrow = *sizegrowp, sizeshrink = *sizeshrinkp;
1984 ULONG weightgrow = *weightgrowp, weightshrink = *weightshrinkp;
1986 if (weight == 0)
1987 return;
1989 if ((remain > 0) && (size < maxsize))
1991 LONG newsize;
1993 newsize = (sizegrow * weight + weightgrow / 2) / weightgrow;
1995 /* D(bug("newsize=%ld == size_growa=%ld * w=%ld / weight_grow=%d\n", */
1996 /* newsize, sizegrow, weight, weightgrow)); */
1998 /* take care of off-by-1 errors that may toggle remainder sign
1999 * by ensuring convergence to 0
2001 if (remain - newsize + size < 0)
2003 /* D(bug("adding remainder=%d => size = %d\n", */
2004 /* remain, size + remain)); */
2005 size += remain;
2006 remain = 0;
2008 else
2010 remain -= newsize - size;
2011 size = newsize;
2012 sizegrow -= size;
2013 weightgrow -= weight;
2016 else if ((remain < 0) && (size > minsize))
2018 LONG newsize;
2020 newsize = (sizeshrink * weight + weightshrink / 2) / weightshrink;
2022 /* D(bug("newsize=%ld == size_shrinkables=%ld * w=%ld " */
2023 /* "/ weight_shrinkables=%d\n", */
2024 /* newsize, sizeshrink, weight, weightshrink)); */
2026 if (remain - newsize + size > 0)
2028 /* D(bug("adding remainder=%d => size = %d\n", */
2029 /* remain, size + remain)); */
2030 size += remain;
2031 remain = 0;
2033 else
2035 remain -= newsize - size;
2036 size = newsize;
2037 sizeshrink -= size;
2038 weightshrink -= weight;
2042 *sizep = size;
2043 *remainp = remain;
2044 *sizegrowp = sizegrow;
2045 *sizeshrinkp = sizeshrink;
2046 *weightgrowp = weightgrow;
2047 *weightshrinkp = weightshrink;
2051 // 2 passes at most, less on average (0.5 or 1.5), each does
2052 // - a minmax clamping, evenutally adding to a remainder
2053 // (remainder = missing (underflow) or remaining (overflow) space compared
2054 // to ideal sizes where children fill the whole group)
2055 // - a redistribution of the remainder, by growing (pos. remainder) or
2056 // shrinking (neg. remainder) children able to support it.
2058 // Occasionnaly the first time the redistribution is done, the minmax
2059 // constraint can be broken, thus the extra pass to check and eventually
2060 // redistribute. The second redistribution never breaks minmax constraint
2061 // (there should be a mathematical proof, but feel free to prove me wrong
2062 // with an example)
2063 static void Layout1D_minmax_constraints_and_redistrib(struct MinList
2064 *children, WORD total_size, WORD remainder, WORD samesize,
2065 BOOL group_horiz)
2067 Object *cstate;
2068 Object *child;
2069 int i;
2071 for (i = 0; i < 2; i++)
2073 WORD size_growables = total_size;
2074 WORD size_shrinkables = total_size;
2075 ULONG weight_growables = 0;
2076 ULONG weight_shrinkables = 0;
2078 /* D(bug("start : rem=%ld, A=%ld, size_growables=%ld, " */
2079 /* "size_shrinkables=%ld\n", */
2080 /* remainder, total_size, size_growables, size_shrinkables)); */
2082 // minmax constraints
2083 cstate = (Object *) children->mlh_Head;
2084 while ((child = NextObject(&cstate)))
2086 /* WORD old_size; */
2088 if (IS_HIDDEN(child))
2089 continue;
2091 if (group_horiz)
2093 /* old_size = _width(child); */
2095 Layout1D_minmax_constraint(&_width(child), _minwidth(child),
2096 _maxwidth(child), &remainder, &size_growables,
2097 &size_shrinkables, &weight_growables,
2098 &weight_shrinkables, _hweight(child), samesize);
2100 /* D(bug("loop1 on %p : width=%d was %d, rem=%d, A=%d, " */
2101 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2102 /* child, _width(child), old_size, remainder, total_size, */
2103 /* size_growables, size_shrinkables, _hweight(child), */
2104 /* _minwidth(child), _maxwidth(child))); */
2106 else // ! group_horiz
2108 /* old_size = _height(child); */
2110 Layout1D_minmax_constraint(&_height(child),
2111 _minheight(child), _maxheight(child), &remainder,
2112 &size_growables, &size_shrinkables, &weight_growables,
2113 &weight_shrinkables, _vweight(child), samesize);
2115 /* D(bug("loop1 on %p : h=%ld was %d, rem=%d, A=%d, " */
2116 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2117 /* child, _height(child), old_size, remainder, total_size,*/
2118 /* size_growables, size_shrinkables, _vweight(child), */
2119 /* _minheight(child), _maxheight(child))); */
2120 } // if (group_horiz)
2121 } // while child, minmax constraints
2123 // mid-pass break
2124 if (remainder == 0)
2125 break;
2127 /* D(bug("mid : rem=%d, A=%d, size_grow=%d, size_shrink=%d, " */
2128 /* "wg=%ld, ws=%ld\n", remainder, total_size, size_growables, */
2129 /* size_shrinkables, weight_growables, weight_shrinkables)); */
2131 // distribute remaining space to possible candidates
2132 cstate = (Object *) children->mlh_Head;
2133 while (((child = NextObject(&cstate)) != NULL) && (remainder != 0))
2135 /* WORD old_size; */
2137 if (IS_HIDDEN(child))
2138 continue;
2140 if (group_horiz)
2142 /* old_size = _width(child); */
2144 Layout1D_redistribution(&_width(child), _minwidth(child),
2145 _maxwidth(child), &remainder, &size_growables,
2146 &size_shrinkables, &weight_growables,
2147 &weight_shrinkables, _hweight(child));
2149 /* D(bug("loop2 on %p : h=%d was %d, rem=%d, A=%d, " */
2150 /* "size_grow=%d, size_shrink=%d\n", child, */
2151 /* _width(child), old_size, remainder, total_size, */
2152 /* size_growables, size_shrinkables)); */
2154 else // ! group_horiz
2156 /* old_size = _height(child); */
2158 Layout1D_redistribution(&_height(child), _minheight(child),
2159 _maxheight(child), &remainder, &size_growables,
2160 &size_shrinkables, &weight_growables,
2161 &weight_shrinkables, _vweight(child));
2163 /* D(bug("loop2 on %p : h=%d was %d, rem=%d, A=%d, " */
2164 /* "size_grow=%d, size_shrink=%d\n", child, */
2165 /* _height(child), old_size, remainder, total_size, */
2166 /* size_growables, size_shrinkables)); */
2167 } // if (group_horiz)
2169 } // while child, redistribution
2171 /* if (remainder != 0) */
2172 /* { */
2173 /* D(bug("end : rem=%ld, A=%ld, size_grow=%ld, size_shrink=%ld\n", */
2174 /* remainder, total_size, size_growables, size_shrinkables)); */
2175 /* } */
2176 // dont break here if remainder == 0, some minmax constraints
2177 // may not be respected
2178 } // end for
2180 // to easily spot layout bugs, nothing like a (division by zero) exception
2181 /* if (remainder != 0) */
2182 /* { */
2183 /* ASSERT(remainder != 0); */
2184 /* D(bug("gonna crash, remainder = %d\n", remainder)); */
2185 /* remainder /= 0; */
2186 /* } */
2190 static void Layout1D_weight_constraint(WORD *total_sizep,
2191 WORD *total_init_sizep,
2192 ULONG *total_weightp, WORD *sizep, UWORD weight, WORD minsize)
2194 if (*total_weightp > 0)
2195 *sizep = (*total_sizep * weight + *total_weightp / 2)
2196 / *total_weightp;
2197 else
2198 *sizep = minsize;
2200 *total_weightp -= weight;
2201 *total_sizep -= *sizep;
2202 *total_init_sizep += *sizep;
2206 static void group_layout_vert(struct IClass *cl, Object *obj,
2207 struct MinList *children)
2209 struct MUI_GroupData *data = INST_DATA(cl, obj);
2210 Object *cstate;
2211 Object *child;
2212 ULONG total_weight;
2213 WORD remainder; /* must converge to 0 to successfully end layout */
2214 WORD total_size;
2215 WORD total_size_backup;
2216 WORD total_init_size; /* total size of the ideally sized children */
2217 WORD width;
2218 WORD left = 0;
2219 WORD top = 0;
2220 WORD layout_width;
2221 WORD layout_height;
2223 //kprintf("group_layout_vert: virtoff = %d,%d\n",
2224 // data->virt_offx, data->virt_offy);
2226 if (data->flags & GROUP_VIRTUAL)
2228 data->virt_mwidth =
2229 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2230 _maxwidth(obj) - _subwidth(obj));
2231 data->virt_mheight =
2232 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2233 _maxheight(obj) - _subheight(obj));
2235 layout_width = data->virt_mwidth;
2236 layout_height = data->virt_mheight;
2238 else
2240 layout_width = _mwidth(obj);
2241 layout_height = _mheight(obj);
2244 total_weight = data->vert_weight_sum;
2245 total_init_size = 0;
2246 total_size =
2247 layout_height - (data->num_visible_children -
2248 1) * data->vert_spacing;
2249 total_size_backup = total_size;
2251 /* D(bug("\nvert layout for %p, A=%d W=%ld\n", */
2252 /* obj, total_size, total_weight)); */
2254 // weight constraints
2255 // calculate ideal size for each object, and total ideal size
2256 cstate = (Object *) children->mlh_Head;
2257 while ((child = NextObject(&cstate)))
2259 if (IS_HIDDEN(child))
2260 continue;
2262 Layout1D_weight_constraint(&total_size, &total_init_size,
2263 &total_weight, &_height(child), _vweight(child),
2264 _minheight(child));
2265 /* D(bug("child %p : ideal=%d w=%ld\n", */
2266 /* child, _height(child), _vweight(child))); */
2267 } // while child, weight constraints
2269 total_size = total_size_backup;
2270 remainder = total_size - total_init_size;
2272 if (data->flags & GROUP_VIRTUAL)
2274 /* This is also true for non virtual groups, but if this would be the
2275 ** case then there is a bug in the layout function
2277 if (total_size < 0)
2278 total_size = 0;
2281 Layout1D_minmax_constraints_and_redistrib(children,
2282 total_size,
2283 remainder,
2284 (data->flags & GROUP_SAME_HEIGHT) ? data->samesize_maxmin_vert : 0,
2285 FALSE);
2287 // do the layout
2288 cstate = (Object *) children->mlh_Head;
2289 while ((child = NextObject(&cstate)))
2291 if (IS_HIDDEN(child))
2292 continue;
2294 width = MIN(_maxwidth(child), layout_width);
2295 width = MAX(width, _minwidth(child));
2296 left = (layout_width - width) / 2;
2298 /* D(bug("child %p -> layout %d x %d\n", */
2299 /* child, width, _height(child))); */
2300 if (!MUI_Layout(child, left, top, width, _height(child), 0))
2301 return;
2302 top += data->vert_spacing + _height(child);
2308 static void group_layout_horiz(struct IClass *cl, Object *obj,
2309 struct MinList *children)
2311 struct MUI_GroupData *data = INST_DATA(cl, obj);
2312 Object *cstate;
2313 Object *child;
2314 ULONG total_weight;
2315 WORD remainder; /* must converge to 0 to succesfully end layout */
2316 WORD total_size;
2317 WORD total_size_backup;
2318 WORD total_init_size; /* total size of the ideally sized children */
2319 WORD height;
2320 WORD top = 0;
2321 WORD left = 0;
2322 WORD layout_width;
2323 WORD layout_height;
2325 //kprintf("group_layout_horiz: virtoff = %d,%d\n",
2326 // data->virt_offx, data->virt_offy);
2328 if (data->flags & GROUP_VIRTUAL)
2330 data->virt_mwidth =
2331 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2332 _maxwidth(obj) - _subwidth(obj));
2333 data->virt_mheight =
2334 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2335 _maxheight(obj) - _subheight(obj));
2337 layout_width = data->virt_mwidth;
2338 layout_height = data->virt_mheight;
2340 //kprintf("group_layout_horiz: layoutsize %d x %d "
2341 // " virtsize %d x %d msize %d x %d\n",
2342 // layout_width, layout_height, data->virt_mwidth,
2343 // data->virt_mheight,
2344 // _mwidth(obj), _mheight(obj));
2346 else
2348 layout_width = _mwidth(obj);
2349 layout_height = _mheight(obj);
2352 total_weight = data->horiz_weight_sum;
2353 total_init_size = 0;
2354 total_size =
2355 layout_width - (data->num_visible_children -
2356 1) * data->horiz_spacing;
2357 total_size_backup = total_size;
2359 /* D(bug("\nhoriz layout for %p, A=%d W=%ld\n", */
2360 /* obj, total_size, total_weight)); */
2362 // weight constraints
2363 // calculate ideal size for each object, and total ideal size
2364 cstate = (Object *) children->mlh_Head;
2365 while ((child = NextObject(&cstate)))
2367 if (IS_HIDDEN(child))
2368 continue;
2370 Layout1D_weight_constraint(&total_size, &total_init_size,
2371 &total_weight, &_width(child), _hweight(child),
2372 _minwidth(child));
2373 /* D(bug("child %p : ideal=%d w=%ld\n", */
2374 /* child, _width(child), _hweight(child))); */
2375 } // while child, weight constraints
2377 total_size = total_size_backup;
2378 if (data->horiz_weight_sum > 0)
2379 remainder = total_size - total_init_size;
2380 else
2381 remainder = 0;
2383 if (data->flags & GROUP_VIRTUAL)
2385 /* This is also true for non virtual groups, but if this would be the
2386 ** case then there is a bug in the layout function
2388 if (total_size < 0)
2389 total_size = 0;
2392 Layout1D_minmax_constraints_and_redistrib(children,
2393 total_size,
2394 remainder,
2395 (data->flags & GROUP_SAME_WIDTH) ? data->samesize_maxmin_horiz : 0,
2396 TRUE);
2398 // do the layout
2399 cstate = (Object *) children->mlh_Head;
2400 while ((child = NextObject(&cstate)))
2402 if (IS_HIDDEN(child))
2403 continue;
2405 height = MIN(_maxheight(child), layout_height);
2406 height = MAX(height, _minheight(child));
2407 top = (layout_height - height) / 2;
2409 /* D(bug("child %p -> layout %d x %d\n", */
2410 /* child, _width(child), height)); */
2411 if (!MUI_Layout(child, left, top, _width(child), height, 0))
2412 return;
2413 left += data->horiz_spacing + _width(child);
2419 static void Layout2D_weight_constraint(struct MUI_GroupData *data,
2420 struct layout2d_elem *row_infos,
2421 struct layout2d_elem *col_infos,
2422 WORD total_size_height, WORD total_size_width,
2423 WORD *total_init_height, WORD *total_init_width)
2425 int i;
2426 ULONG total_weight_vert = data->vert_weight_sum;
2427 ULONG total_weight_horiz = data->horiz_weight_sum;
2429 *total_init_height = 0;
2430 *total_init_width = 0;
2432 /* calc row heights */
2433 for (i = 0; i < data->rows; i++)
2435 if (total_weight_vert > 0)
2436 row_infos[i].dim =
2437 (total_size_height * row_infos[i].weight +
2438 total_weight_vert / 2) / total_weight_vert;
2439 else
2440 row_infos[i].dim = row_infos[i].min;
2442 /* D(bug("l2 row %d : ideal = %d with w=%d, A=%d, W=%d\n", */
2443 /* i, row_infos[i].dim, */
2444 /* row_infos[i].weight, total_size_height, total_weight_vert)); */
2446 total_weight_vert -= row_infos[i].weight;
2447 total_size_height -= row_infos[i].dim;
2448 *total_init_height += row_infos[i].dim;
2451 /* calc columns widths */
2452 for (i = 0; i < data->columns; i++)
2454 if (total_weight_horiz)
2455 col_infos[i].dim =
2456 (total_size_width * col_infos[i].weight +
2457 total_weight_horiz / 2) / total_weight_horiz;
2458 else
2459 col_infos[i].dim = col_infos[i].min;
2461 /* D(bug("l2 col %d : ideal = %d with w=%d, A=%d, W=%d\n", */
2462 /* i, col_infos[i].dim, */
2463 /* col_infos[i].weight, total_size_width, total_weight_horiz)); */
2465 total_weight_horiz -= col_infos[i].weight;
2466 total_size_width -= col_infos[i].dim;
2467 *total_init_width += col_infos[i].dim;
2473 static void Layout2D_minmax_constraints_and_redistrib(struct layout2d_elem
2474 *infos, WORD nitems, WORD total_size, WORD samesize, WORD remainder)
2476 int j;
2478 /* D(bug("L2D mc&r n=%d A=%d ss=%d rem=%d\n", */
2479 /* nitems, total_size, samesize, remainder)); */
2481 for (j = 0; j < 2; j++)
2483 WORD size_growables = total_size;
2484 WORD size_shrinkables = total_size;
2485 ULONG weight_growables = 0;
2486 ULONG weight_shrinkables = 0;
2487 /* WORD old_size; */
2488 int i;
2490 // minmax constraints
2491 for (i = 0; i < nitems; i++)
2493 /* old_size = infos[i].dim; */
2495 /* D(bug("bef loop1 on %d : size=%d, rem=%d, A=%d, " */
2496 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2497 /* i, infos[i].dim, remainder, total_size, */
2498 /* size_growables, size_shrinkables, infos[i].weight, */
2499 /* infos[i].min, infos[i].max)); */
2501 Layout1D_minmax_constraint(&infos[i].dim, infos[i].min,
2502 infos[i].max, &remainder, &size_growables,
2503 &size_shrinkables, &weight_growables, &weight_shrinkables,
2504 infos[i].weight, samesize);
2506 /* D(bug("loop1 on %d : size=%d was %d, rem=%d, A=%d, " */
2507 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2508 /* i, infos[i].dim, old_size, remainder, total_size, */
2509 /* size_growables, size_shrinkables, infos[i].weight, */
2510 /* infos[i].min, infos[i].max)); */
2513 if (remainder == 0)
2514 break;
2516 for (i = 0; i < nitems; i++)
2518 /* old_size = infos[i].dim; */
2520 /* D(bug("bef loop2 on %d : size=%d, rem=%d, A=%d, " */
2521 /* "size_grow=%d, size_shrink=%d\n", i, */
2522 /* infos[i].dim, remainder, total_size, */
2523 /* size_growables, size_shrinkables)); */
2525 Layout1D_redistribution(&infos[i].dim, infos[i].min,
2526 infos[i].max, &remainder, &size_growables,
2527 &size_shrinkables, &weight_growables, &weight_shrinkables,
2528 infos[i].weight);
2530 /* D(bug("loop2 on %d : size=%d was %d, rem=%d, A=%d, " */
2531 /* "size_grow=%d, size_shrink=%d\n", i, */
2532 /* infos[i].dim, old_size, remainder, total_size, */
2533 /* size_growables, size_shrinkables)); */
2538 static void
2539 layout_2d_distribute_space(struct MUI_GroupData *data,
2540 struct layout2d_elem *row_infos,
2541 struct layout2d_elem *col_infos,
2542 struct MinList *children, LONG left_start, LONG top_start)
2544 Object *cstate;
2545 Object *child;
2546 LONG left;
2547 LONG top;
2548 LONG col_width;
2549 LONG row_height;
2550 int i, j;
2553 * pass 2 : distribute space
2555 cstate = (Object *) children->mlh_Head;
2556 top = top_start;
2557 /* for each row */
2558 for (i = 0; i < data->rows; i++)
2560 /* left start for child layout in this row */
2561 left = left_start;
2563 /* max height for children in this row */
2564 row_height = row_infos[i].dim;
2565 j = 0;
2566 /* for each column */
2567 while ((child = NextObject(&cstate)))
2569 LONG cleft;
2570 LONG ctop;
2571 LONG cwidth;
2572 LONG cheight;
2574 if (IS_HIDDEN(child))
2575 continue;
2576 /* max width for children in this column */
2577 col_width = col_infos[j].dim;
2579 /* center child if col width is bigger than child maxwidth */
2580 cwidth = MIN(_maxwidth(child), col_width);
2581 cwidth = MAX(cwidth, _minwidth(child));
2582 cleft = left + (col_width - cwidth) / 2;
2584 /* center child if row height is bigger than child maxheight */
2585 cheight = MIN(_maxheight(child), row_height);
2586 cheight = MAX(cheight, _minheight(child));
2587 ctop = top + (row_height - cheight) / 2;
2589 /* g_print("layout %d %d %d %d\n", cleft, ctop, cwidth, cheight); */
2590 /* D(bug("2DL/child %p -> layout %d x %d\n", */
2591 /* child, cwidth, cheight)); */
2592 if (!MUI_Layout(child, cleft, ctop, cwidth, cheight, 0))
2593 return;
2595 left += data->horiz_spacing + col_width;
2597 ++j;
2598 if ((j % data->columns) == 0)
2599 break;
2602 top += data->vert_spacing + row_height;
2609 * all children in the same row have the same maximum height
2610 * all children in the same column have the same maximum height
2611 * if a child maximum size is smaller than the biggest minimum size,
2612 * the chid will be centered in the remaining space.
2614 * for each row, determine its height allocation
2615 * weight ? the vertical weight of a row, if no fixed-height child
2616 * in the row, is the sum of all vertical weights of children
2617 * all row members will have the same height
2619 * for each column, determine its width allocation
2620 * all column members will have the same width
2622 /* Write a proper hook function */
2623 static void
2624 group_layout_2d(struct IClass *cl, Object *obj, struct MinList *children)
2626 struct MUI_GroupData *data = INST_DATA(cl, obj);
2627 WORD left_start = 0;
2628 WORD top_start = 0;
2629 WORD total_size_height =
2630 _mheight(obj) - (data->rows - 1) * data->vert_spacing;
2631 WORD total_size_width =
2632 _mwidth(obj) - (data->columns - 1) * data->horiz_spacing;
2633 WORD total_init_height;
2634 WORD total_init_width;
2635 WORD remainder_height;
2636 WORD remainder_width;
2637 WORD layout_width;
2638 WORD layout_height;
2640 if (data->rows == 0 || data->columns == 0)
2641 return;
2642 if (data->num_children % data->rows
2643 || data->num_children % data->columns)
2644 return;
2645 if (data->row_infos == NULL || data->col_infos == NULL)
2646 return;
2648 //kprintf("group_layout_horiz: virtoff = %d,%d\n",
2649 // data->virt_offx, data->virt_offy);
2651 if (data->flags & GROUP_VIRTUAL)
2653 data->virt_mwidth =
2654 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2655 _maxwidth(obj) - _subwidth(obj));
2656 data->virt_mheight =
2657 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2658 _maxheight(obj) - _subheight(obj));
2660 layout_width = data->virt_mwidth;
2661 layout_height = data->virt_mheight;
2663 else
2665 layout_width = _mwidth(obj);
2666 layout_height = _mheight(obj);
2669 total_size_height =
2670 layout_height - (data->rows - 1) * data->vert_spacing;
2671 total_size_width =
2672 layout_width - (data->columns - 1) * data->horiz_spacing;
2674 // fix left/top ?
2676 // weight constraints
2677 Layout2D_weight_constraint(data, data->row_infos, data->col_infos,
2678 total_size_height, total_size_width,
2679 &total_init_height, &total_init_width);
2681 remainder_height = total_size_height - total_init_height;
2682 remainder_width = total_size_width - total_init_width;
2684 Layout2D_minmax_constraints_and_redistrib(data->row_infos,
2685 data->rows, total_size_height,
2686 /* (data->flags & GROUP_SAME_HEIGHT) ? data->samesize_maxmin_vert : 0, */
2687 0, remainder_height);
2689 Layout2D_minmax_constraints_and_redistrib(data->col_infos,
2690 data->columns, total_size_width,
2691 /* (data->flags & GROUP_SAME_WIDTH) ? data->samesize_maxmin_horiz : 0, */
2692 0, remainder_width);
2694 layout_2d_distribute_space(data, data->row_infos, data->col_infos,
2695 children, left_start, top_start);
2699 /* Write a proper hook function */
2700 static void group_layout_pagemode(struct IClass *cl, Object *obj,
2701 struct MinList *children)
2703 struct MUI_GroupData *data = INST_DATA(cl, obj);
2704 Object *cstate;
2705 Object *child;
2706 WORD layout_width;
2707 WORD layout_height;
2708 int w, h, yoffset = 0;
2710 if (data->flags & GROUP_VIRTUAL)
2712 data->virt_mwidth =
2713 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2714 _maxwidth(obj) - _subwidth(obj));
2715 data->virt_mheight =
2716 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2717 _maxheight(obj) - _subheight(obj));
2719 layout_width = data->virt_mwidth;
2720 layout_height = data->virt_mheight;
2722 else
2724 layout_width = _mwidth(obj);
2725 layout_height = _mheight(obj);
2728 if (data->titlegroup)
2730 yoffset = _minheight(data->titlegroup);
2731 layout_height -= yoffset;
2734 cstate = (Object *) children->mlh_Head;
2735 while ((child = NextObject(&cstate)))
2737 w = MIN(layout_width, _maxwidth(child));
2738 h = MIN(layout_height, _maxheight(child));
2740 if (child == data->titlegroup)
2742 MUI_Layout(child, (layout_width - w) / 2, 0, w, yoffset, 0);
2744 else
2746 D(bug("PM/child %p -> layout %d x %d\n", child, w, h));
2747 MUI_Layout(child, (layout_width - w) / 2,
2748 yoffset + (layout_height - h) / 2, w, h, 0);
2754 /**************************************************************************
2755 MUIM_Layout
2756 Either use a given layout hook, or the builtin method.
2757 **************************************************************************/
2758 IPTR Group__MUIM_Layout(struct IClass *cl, Object *obj,
2759 struct MUIP_Layout *msg)
2761 struct MUI_GroupData *data = INST_DATA(cl, obj);
2762 struct MUI_LayoutMsg lm = { 0 };
2764 get(data->family, MUIA_Family_List, &(lm.lm_Children));
2765 if (data->flags & GROUP_PAGEMODE)
2767 group_layout_pagemode(cl, obj, lm.lm_Children);
2769 else if (data->layout_hook)
2771 lm.lm_Type = MUILM_LAYOUT;
2772 lm.lm_Layout.Width = _mwidth(obj);
2773 lm.lm_Layout.Height = _mheight(obj);
2775 CallHookPkt(data->layout_hook, obj, &lm);
2777 if (data->flags & GROUP_VIRTUAL)
2779 data->virt_mwidth = lm.lm_Layout.Width;
2780 data->virt_mheight = lm.lm_Layout.Height;
2783 else
2785 if ((data->rows == 1) && (data->columns == 1))
2787 if (data->flags & GROUP_HORIZ)
2788 group_layout_horiz(cl, obj, lm.lm_Children);
2789 else
2790 group_layout_vert(cl, obj, lm.lm_Children);
2792 else
2793 group_layout_2d(cl, obj, lm.lm_Children);
2796 if (data->flags & GROUP_VIRTUAL)
2798 WORD new_virt_offx, new_virt_offy;
2800 new_virt_offx = data->virt_offx;
2801 new_virt_offy = data->virt_offy;
2803 if (new_virt_offx + _mwidth(obj) > data->virt_mwidth)
2805 new_virt_offx = data->virt_mwidth - _mwidth(obj);
2807 if (new_virt_offx < 0)
2808 new_virt_offx = 0;
2810 if (new_virt_offy + _mheight(obj) > data->virt_mheight)
2812 new_virt_offy = data->virt_mheight - _mheight(obj);
2814 if (new_virt_offy < 0)
2815 new_virt_offy = 0;
2817 if (new_virt_offx != data->virt_offx)
2819 nfset(obj, MUIA_Virtgroup_Left, new_virt_offx);
2822 if (new_virt_offy != data->virt_offy)
2824 nfset(obj, MUIA_Virtgroup_Top, new_virt_offy);
2829 return 0;
2832 /**************************************************************************
2833 MUIM_Show
2834 **************************************************************************/
2835 IPTR Group__MUIM_Show(struct IClass *cl, Object *obj,
2836 struct MUIP_Show *msg)
2838 struct MUI_GroupData *data = INST_DATA(cl, obj);
2839 Object *cstate;
2840 Object *child;
2841 struct MinList *ChildList = NULL;
2843 /* If msg is NULL, we won't want that the super method actually gets
2844 * this call */
2845 if (msg)
2846 DoSuperMethodA(cl, obj, (Msg) msg);
2848 get(data->family, MUIA_Family_List, &(ChildList));
2849 cstate = (Object *) ChildList->mlh_Head;
2851 if (data->flags & GROUP_PAGEMODE)
2853 int page = 0;
2854 while ((child = NextObject(&cstate)))
2856 if (child == data->titlegroup)
2858 DoShowMethod(child);
2859 continue; /* Title group is not counted as page */
2862 if (page == data->active_page)
2864 DoShowMethod(child);
2865 break;
2867 page++;
2870 else
2872 while ((child = NextObject(&cstate)))
2874 if (!(data->flags & GROUP_VIRTUAL) ||
2875 IsObjectVisible(child, MUIMasterBase))
2877 if (_flags(child) & MADF_SHOWME)
2878 DoShowMethod(child);
2882 return TRUE;
2885 /**************************************************************************
2886 MUIM_Hide
2887 **************************************************************************/
2888 IPTR Group__MUIM_Hide(struct IClass *cl, Object *obj,
2889 struct MUIP_Hide *msg)
2891 struct MUI_GroupData *data = INST_DATA(cl, obj);
2892 Object *cstate;
2893 Object *child;
2894 struct MinList *ChildList = NULL;
2896 get(data->family, MUIA_Family_List, &(ChildList));
2897 cstate = (Object *) ChildList->mlh_Head;
2899 if (data->flags & GROUP_PAGEMODE)
2901 int page = 0;
2902 while ((child = NextObject(&cstate)))
2904 if (child == data->titlegroup)
2906 DoHideMethod(child);
2907 continue; /* Title group is not counted as page */
2910 if (page == data->active_page)
2912 DoHideMethod(child);
2913 break;
2915 page++;
2918 else
2920 while ((child = NextObject(&cstate)))
2922 if (_flags(child) & MADF_CANDRAW)
2923 DoHideMethod(child);
2927 /* If msg is NULL, we won't want that the super method actually gets
2928 * this call */
2929 if (msg)
2930 return DoSuperMethodA(cl, obj, (Msg) msg);
2931 return 1;
2935 * MUIM_FindUData : tests if the MUIA_UserData of the object
2936 * contains the given <udata> and returns the object pointer in this case.
2938 IPTR Group__MUIM_FindUData(struct IClass *cl, Object *obj,
2939 struct MUIP_FindUData *msg)
2941 struct MUI_GroupData *data = INST_DATA(cl, obj);
2943 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2944 return (IPTR) obj;
2946 return DoMethodA(data->family, (Msg) msg);
2951 * MUIM_GetUData : This method tests if the MUIA_UserData of the object
2952 * contains the given <udata> and gets <attr> to <storage> for itself
2953 * in this case.
2955 IPTR Group__MUIM_GetUData(struct IClass *cl, Object *obj,
2956 struct MUIP_GetUData *msg)
2958 struct MUI_GroupData *data = INST_DATA(cl, obj);
2960 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2962 get(obj, msg->attr, msg->storage);
2963 return TRUE;
2966 return DoMethodA(data->family, (Msg) msg);
2971 * MUIM_SetUData : This method tests if the MUIA_UserData of the object
2972 * contains the given <udata> and sets <attr> to <val> for itself in this case.
2974 IPTR Group__MUIM_SetUData(struct IClass *cl, Object *obj,
2975 struct MUIP_SetUData *msg)
2977 struct MUI_GroupData *data = INST_DATA(cl, obj);
2979 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2980 set(obj, msg->attr, msg->val);
2982 DoMethodA(data->family, (Msg) msg);
2983 return TRUE;
2988 * MUIM_SetUDataOnce : This method tests if the MUIA_UserData of the object
2989 * contains the given <udata> and sets <attr> to <val> for itself in this case.
2990 * Stop after the first udata found.
2992 IPTR Group__MUIM_SetUDataOnce(struct IClass *cl, Object *obj,
2993 struct MUIP_SetUData *msg)
2995 struct MUI_GroupData *data = INST_DATA(cl, obj);
2997 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2999 set(obj, msg->attr, msg->val);
3000 return TRUE;
3002 return DoMethodA(data->family, (Msg) msg);
3005 /**************************************************************************
3006 MUIM_DragQueryExtented
3007 **************************************************************************/
3008 IPTR Group__MUIM_DragQueryExtended(struct IClass *cl, Object *obj,
3009 struct MUIP_DragQueryExtended *msg)
3011 struct MUI_GroupData *data = INST_DATA(cl, obj);
3012 Object *cstate;
3013 Object *child;
3014 Object *found_obj;
3015 struct MinList *ChildList = NULL;
3017 get(data->family, MUIA_Family_List, &(ChildList));
3018 cstate = (Object *) ChildList->mlh_Head;
3019 while ((child = NextObject(&cstate)))
3021 if (!(_flags(child) & MADF_CANDRAW))
3022 continue;
3024 if ((found_obj = (Object *) DoMethodA(child, (Msg) msg)))
3025 return (IPTR) found_obj;
3027 return DoSuperMethodA(cl, obj, (Msg) msg);
3030 /**************************************************************************
3031 MUIM_HandleEvent
3032 **************************************************************************/
3033 IPTR Group__MUIM_HandleEvent(struct IClass *cl, Object *obj,
3034 struct MUIP_HandleEvent *msg)
3036 struct MUI_GroupData *data = INST_DATA(cl, obj);
3038 /* check this, otherwise a superclass who has IDCMP_MOUSEBUTTONS
3039 eventhandler might call DoSuperMethod, and this function gets
3040 called even when he have not added any eventhandler */
3042 if ((data->flags & GROUP_VIRTUAL) && msg->imsg)
3044 switch (msg->imsg->Class)
3046 case IDCMP_MOUSEBUTTONS:
3047 /* For virtual groups */
3048 if (msg->imsg->Code == SELECTDOWN)
3050 if (_between(_mleft(obj), msg->imsg->MouseX, _mright(obj))
3051 && _between(_mtop(obj), msg->imsg->MouseY,
3052 _mbottom(obj)))
3054 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
3055 (IPTR) & data->ehn);
3056 data->ehn.ehn_Events |= IDCMP_INTUITICKS;
3057 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
3058 (IPTR) & data->ehn);
3061 else
3063 if (data->ehn.ehn_Events & IDCMP_INTUITICKS)
3065 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
3066 (IPTR) & data->ehn);
3067 data->ehn.ehn_Events &= ~IDCMP_INTUITICKS;
3068 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
3069 (IPTR) & data->ehn);
3072 break;
3074 case IDCMP_INTUITICKS:
3075 if (!(data->ehn.ehn_Events & IDCMP_INTUITICKS))
3076 break;
3078 if (!(_between(_mleft(obj), msg->imsg->MouseX, _mright(obj))
3079 && _between(_mtop(obj), msg->imsg->MouseY,
3080 _mbottom(obj))))
3082 LONG new_virt_offx = data->virt_offx;
3083 LONG new_virt_offy = data->virt_offy;
3085 if (msg->imsg->MouseX < _mleft(obj))
3087 /* scroll left */
3088 if (new_virt_offx >= 4)
3089 new_virt_offx -= 4;
3090 else
3091 new_virt_offx = 0;
3093 else if (msg->imsg->MouseX > _mright(obj))
3095 /* scroll right */
3096 new_virt_offx += 4;
3097 if (new_virt_offx > data->virt_mwidth - _mwidth(obj))
3098 new_virt_offx = data->virt_mwidth - _mwidth(obj);
3099 if (new_virt_offx < 0)
3100 new_virt_offx = 0;
3103 if (msg->imsg->MouseY < _mtop(obj))
3105 /* scroll top */
3106 if (new_virt_offy >= 4)
3107 new_virt_offy -= 4;
3108 else
3109 new_virt_offy = 0;
3111 else if (msg->imsg->MouseY > _mbottom(obj))
3113 /* scroll bottom */
3114 new_virt_offy += 4;
3115 if (new_virt_offy > data->virt_mheight - _mheight(obj))
3116 new_virt_offy = data->virt_mheight - _mheight(obj);
3117 if (new_virt_offy < 0)
3118 new_virt_offy = 0;
3121 if (new_virt_offx != data->virt_offx
3122 || new_virt_offy != data->virt_offy)
3124 SetAttrs(obj,
3125 MUIA_Virtgroup_Left, new_virt_offx,
3126 MUIA_Virtgroup_Top, new_virt_offy,
3127 MUIA_Group_Forward, FALSE, TAG_DONE);
3130 break;
3134 return 0;
3137 /**************************************************************************
3138 MUIM_DrawBackground
3139 **************************************************************************/
3140 IPTR Group__MUIM_DrawBackground(struct IClass *cl, Object *obj,
3141 struct MUIP_DrawBackground *msg)
3143 struct MUI_GroupData *data = INST_DATA(cl, obj);
3145 if (data->flags & GROUP_VIRTUAL)
3147 struct MUIP_DrawBackground msg2 = *msg;
3149 msg2.xoffset += data->virt_offx;
3150 msg2.yoffset += data->virt_offy;
3152 return DoSuperMethodA(cl, obj, (Msg) & msg2);
3155 return DoSuperMethodA(cl, obj, (Msg) msg);
3158 /**************************************************************************
3159 MUIM_FindAreaObject
3160 Find the given object or return NULL
3161 **************************************************************************/
3162 IPTR Group__MUIM_FindAreaObject(struct IClass *cl, Object *obj,
3163 struct MUIP_FindAreaObject *msg)
3165 struct MUI_GroupData *data = INST_DATA(cl, obj);
3166 Object *cstate;
3167 Object *child;
3168 struct MinList *ChildList = NULL;
3170 // it's me ?
3171 if (msg->obj == obj)
3172 return (IPTR) obj;
3174 // it's one of my children ?
3175 get(data->family, MUIA_Family_List, &(ChildList));
3176 cstate = (Object *) ChildList->mlh_Head;
3177 while ((child = NextObject(&cstate)))
3179 if (msg->obj == child)
3180 return (IPTR) child;
3183 // let the children find it
3184 get(data->family, MUIA_Family_List, &(ChildList));
3185 cstate = (Object *) ChildList->mlh_Head;
3186 while ((child = NextObject(&cstate)))
3188 Object *res = (Object *) DoMethodA(child, (Msg) msg);
3189 if (res != NULL)
3190 return (IPTR) res;
3193 return (IPTR) NULL;
3196 /**************************************************************************
3197 MUIM_Export : to export an object's "contents" to a dataspace object.
3198 **************************************************************************/
3199 static IPTR Group__MUIM_Export(struct IClass *cl, Object *obj,
3200 struct MUIP_Export *msg)
3202 struct MUI_GroupData *data = INST_DATA(cl, obj);
3203 Object *cstate;
3204 Object *child;
3205 struct MinList *ChildList = NULL;
3207 get(data->family, MUIA_Family_List, &(ChildList));
3208 if (!ChildList)
3209 return 0;
3211 cstate = (Object *) ChildList->mlh_Head;
3212 while ((child = NextObject(&cstate)))
3214 DoMethodA(child, (Msg) msg);
3217 return 0;
3221 /**************************************************************************
3222 MUIM_Import : to import an object's "contents" from a dataspace object.
3223 **************************************************************************/
3224 static IPTR Group__MUIM_Import(struct IClass *cl, Object *obj,
3225 struct MUIP_Import *msg)
3227 struct MUI_GroupData *data = INST_DATA(cl, obj);
3228 Object *cstate;
3229 Object *child;
3230 struct MinList *ChildList = NULL;
3232 get(data->family, MUIA_Family_List, &(ChildList));
3233 if (!ChildList)
3234 return 0;
3236 cstate = (Object *) ChildList->mlh_Head;
3237 while ((child = NextObject(&cstate)))
3239 DoMethodA(child, (Msg) msg);
3242 return 0;
3245 /**************************************************************************
3246 MUIM_Notify - disabled now because previous Zune versions had a OM_GET
3247 check in MUIM_Notify which is no longer the case
3248 **************************************************************************/
3249 #if 0
3250 STATIC IPTR Group_Notify(struct IClass *cl, Object *obj,
3251 struct MUIP_Notify *msg)
3253 struct MUI_GroupData *data = INST_DATA(cl, obj);
3254 Object *cstate;
3255 Object *child;
3256 struct MinList *ChildList;
3258 /* Try at first if understand the message our self
3259 ** We disable the forwarding of the OM_GET message
3260 ** as the MUIM_Notify otherwise would "think" that
3261 ** the group class actually understands the attribute
3262 ** although a child does this only
3264 data->dont_forward_get = 1;
3265 if (DoSuperMethodA(cl, obj, (Msg) msg))
3267 data->dont_forward_get = 0;
3268 return 1;
3271 /* We ourselves didn't understand the notify tag so we try the
3272 * children now */
3273 data->dont_forward_get = 0;
3275 get(data->family, MUIA_Family_List, (IPTR *) & (ChildList));
3276 cstate = (Object *) ChildList->mlh_Head;
3277 while ((child = NextObject(&cstate)))
3279 if (DoMethodA(child, (Msg) msg))
3280 return 1;
3282 return 0;
3284 #endif
3286 #if 1
3287 /* Notes about Group_Notify() and echo notification problem:
3288 It was discovered that MUI seems to have some special handling for group class
3289 which will drop notifications on the children which are found to not
3290 understand the attribute.
3292 This is done by checking if an OM_GET on the child returns TRUE.
3293 There's a little problem here because it is not known how big the storage
3294 needed for the attribute in question will be. Almost no class uses anything
3295 bigger than one IPTR. For "big" attributes those return a pointer to the data,
3296 not the data itself. Unfortuntely there are some exceptions like colorwheel
3297 class which does not return a pointer, but the data itself. So it's not
3298 enough to use one single IPTR variable (4 Bytes on 32bit machines, 8 bytes
3299 on 64 bit machines) to store the result of the test-OM_Get.
3301 There is no general way to query the size needed so if one wants to change
3302 Zune to work like MUI one needs to choose a size which one hopes will be
3303 big enough to hold all possible attributes of all classes, old, present
3304 and future ones.
3306 STATIC IPTR Group_Notify(struct IClass *cl, Object *obj,
3307 struct MUIP_Notify *msg)
3309 struct MUI_GroupData *data = INST_DATA(cl, obj);
3310 Object *cstate;
3311 Object *child;
3312 struct MinList *ChildList = NULL;
3313 IPTR attr[30];
3315 data->dont_forward_get = 1;
3317 if (GetAttr(msg->TrigAttr, obj, attr))
3319 data->dont_forward_get = 0;
3320 return DoSuperMethodA(cl, obj, (Msg) msg);
3322 data->dont_forward_get = 0;
3324 get(data->family, MUIA_Family_List, &(ChildList));
3325 if (!ChildList)
3326 return TRUE;
3328 cstate = (Object *) ChildList->mlh_Head;
3329 while ((child = NextObject(&cstate)))
3332 if (GetAttr(msg->TrigAttr, child, attr))
3334 DoMethodA(child, (Msg) msg);
3335 /* No return here! */
3338 return TRUE;
3340 #endif
3342 BOOPSI_DISPATCHER(IPTR, Group_Dispatcher, cl, obj, msg)
3344 switch (msg->MethodID)
3346 case OM_NEW:
3347 return Group__OM_NEW(cl, obj, (struct opSet *)msg);
3348 case OM_DISPOSE:
3349 return Group__OM_DISPOSE(cl, obj, msg);
3350 case OM_SET:
3351 return Group__OM_SET(cl, obj, (struct opSet *)msg);
3352 case OM_GET:
3353 return Group__OM_GET(cl, obj, (struct opGet *)msg);
3354 case OM_ADDMEMBER: /* Fall through */
3355 case MUIM_Group_AddTail:
3356 return Group__MUIM_AddTail(cl, obj, (APTR) msg);
3357 case MUIM_Group_AddHead:
3358 return Group__MUIM_AddHead(cl, obj, (APTR) msg);
3359 case MUIM_Group_Insert:
3360 return Group__MUIM_Insert(cl, obj, (APTR) msg);
3361 case OM_REMMEMBER: /* Fall through */
3362 case MUIM_Group_Remove:
3363 return Group__MUIM_Remove(cl, obj, (APTR) msg);
3364 case MUIM_AskMinMax:
3365 return Group__MUIM_AskMinMax(cl, obj, (APTR) msg);
3366 case MUIM_Group_ExitChange:
3367 return Group__MUIM_ExitChange(cl, obj, (APTR) msg);
3368 case MUIM_Group_ExitChange2:
3369 return Group__MUIM_ExitChange2(cl, obj, (APTR) msg);
3370 case MUIM_Group_InitChange:
3371 return Group__MUIM_InitChange(cl, obj, (APTR) msg);
3372 case MUIM_Group_Sort:
3373 return Group__MUIM_Sort(cl, obj, (APTR) msg);
3374 case MUIM_Group_DoMethodNoForward:
3375 return Group__MUIM_DoMethodNoForward(cl, obj, (APTR) msg);
3376 case MUIM_ConnectParent:
3377 return Group__MUIM_ConnectParent(cl, obj, (APTR) msg);
3378 case MUIM_DisconnectParent:
3379 return Group__MUIM_DisconnectParent(cl, obj, (APTR) msg);
3380 case MUIM_Layout:
3381 return Group__MUIM_Layout(cl, obj, (APTR) msg);
3382 case MUIM_Setup:
3383 return Group__MUIM_Setup(cl, obj, (APTR) msg);
3384 case MUIM_Cleanup:
3385 return Group__MUIM_Cleanup(cl, obj, (APTR) msg);
3386 case MUIM_Draw:
3387 return Group__MUIM_Draw(cl, obj, (APTR) msg);
3389 case MUIM_FindUData:
3390 return Group__MUIM_FindUData(cl, obj, (APTR) msg);
3391 case MUIM_GetUData:
3392 return Group__MUIM_GetUData(cl, obj, (APTR) msg);
3393 case MUIM_SetUData:
3394 return Group__MUIM_SetUData(cl, obj, (APTR) msg);
3395 case MUIM_SetUDataOnce:
3396 return Group__MUIM_SetUDataOnce(cl, obj, (APTR) msg);
3397 case MUIM_Show:
3398 return Group__MUIM_Show(cl, obj, (APTR) msg);
3399 case MUIM_Hide:
3400 return Group__MUIM_Hide(cl, obj, (APTR) msg);
3401 case MUIM_HandleEvent:
3402 return Group__MUIM_HandleEvent(cl, obj, (APTR) msg);
3403 case MUIM_DrawBackground:
3404 return Group__MUIM_DrawBackground(cl, obj, (APTR) msg);
3405 case MUIM_DragQueryExtended:
3406 return Group__MUIM_DragQueryExtended(cl, obj, (APTR) msg);
3407 case MUIM_FindAreaObject:
3408 return Group__MUIM_FindAreaObject(cl, obj, (APTR) msg);
3409 case MUIM_Export:
3410 return Group__MUIM_Export(cl, obj, (APTR) msg);
3411 case MUIM_Import:
3412 return Group__MUIM_Import(cl, obj, (APTR) msg);
3414 //#if 0
3415 #if 1
3416 /* Disabled. See above */
3417 case MUIM_Notify:
3418 return Group_Notify(cl, obj, (APTR) msg);
3419 #endif
3420 case MUIM_Set:
3421 case MUIM_MultiSet:
3422 case MUIM_CallHook:
3423 case MUIM_DrawParentBackground:
3424 case MUIM_DragBegin:
3425 case MUIM_DragDrop:
3426 case MUIM_DragQuery:
3427 case MUIM_DragFinish:
3428 case MUIM_DoDrag:
3429 case MUIM_CreateDragImage:
3430 case MUIM_DeleteDragImage:
3431 case MUIM_GoActive:
3432 case MUIM_GoInactive:
3433 case MUIM_CreateBubble:
3434 case MUIM_DeleteBubble:
3435 case MUIM_CreateShortHelp:
3436 case MUIM_DeleteShortHelp:
3437 case OM_ADDTAIL:
3438 case OM_REMOVE:
3439 return DoSuperMethodA(cl, obj, (APTR) msg);
3440 /* Needs not to be forwarded? */
3443 /* sometimes you want to call a superclass method,
3444 * but not dispatching to child.
3445 * But what to do with list methods in a listview ?
3447 Group_DispatchMsg(cl, obj, (APTR) msg);
3449 return DoSuperMethodA(cl, obj, msg);
3451 BOOPSI_DISPATCHER_END
3454 * Class descriptor.
3456 const struct __MUIBuiltinClass _MUI_Group_desc =
3458 MUIC_Group,
3459 MUIC_Area,
3460 sizeof(struct MUI_GroupData),
3461 (void *) Group_Dispatcher