muimaster.library: allow again for groups in page mode not to flicker when redrawing
[AROS.git] / workbench / libs / muimaster / classes / group.c
blobf7913ff23b9e2a6494dde7b36a6b6803523c6c3b
1 /*
2 Copyright 1999, David Le Corfec.
3 Copyright 2002-2015, The AROS Development Team.
4 All rights reserved.
6 $Id$
7 */
9 #include <exec/types.h>
11 #include <clib/alib_protos.h>
12 #include <proto/exec.h>
13 #include <proto/intuition.h>
14 #include <proto/utility.h>
15 #include <proto/graphics.h>
16 #include <proto/muimaster.h>
18 extern struct Library *MUIMasterBase;
20 #include "muimaster_intern.h"
21 #include "mui.h"
22 #include "support.h"
23 #include "prefs.h"
25 /* #define MYDEBUG 1 */
26 #include "debug.h"
28 #define ROUND(x) ((int)(x + 0.5))
29 #define IS_HIDDEN(obj) (! (_flags(obj) & MADF_SHOWME) \
30 || (_flags(obj) & MADF_BORDERGADGET))
32 /* Attributes filtered out in OM_SET, before OM_SET gets passed to children.
33 Tested with MUI under UAE/AOS.
35 notifyclass:
37 MUIA_HelpLine
38 MUIA_HelpNode
39 MUIA_ObjectID
40 MUIA_UserData
42 areaclass:
44 MUIA_ContextMenu
45 MUIA_ContextMenuTrigger
46 MUIA_ControlChar
47 MUIA_CycleChain
48 MUIA_Draggable
49 MUIA_FillArea
50 MUIA_Frame
51 MUIA_FrameTitle
52 MUIA_HorizWeight
53 MUIA_Pressed
54 MUIA_Selected
55 MUIA_ShortHelp
56 MUIA_ShowMe
57 MUIA_VertWeight
58 MUIA_Weight
62 /* Private attribute/method definitions */
63 #define MUIM_Group_Insert (MUIB_MUI|0x00424d34) /* MUI: V20 */
64 struct MUIP_Group_Insert
66 STACKED ULONG MethodID;
67 STACKED Object *obj;
68 STACKED Object *pred;
71 #define MUIA_Group_ChildCount 0x80420322 /* MUI: V20 isg LONG */
73 struct layout2d_elem
75 WORD min;
76 WORD max;
77 WORD dim;
78 ULONG weight;
82 struct MUI_GroupData
84 Object *family;
85 struct Hook *layout_hook;
86 ULONG flags;
87 ULONG columns;
88 ULONG rows;
89 struct layout2d_elem *row_infos;
90 struct layout2d_elem *col_infos;
91 LONG active_page;
92 ULONG horiz_spacing;
93 ULONG vert_spacing;
94 ULONG num_children;
95 ULONG num_visible_children; /* for horiz/vert group only */
96 ULONG horiz_weight_sum;
97 ULONG vert_weight_sum;
98 ULONG samesize_maxmin_horiz;
99 ULONG samesize_maxmin_vert;
100 ULONG update; /* for MUI_Redraw() 1 - do not redraw the frame
101 * 2 - the virtual pos has changed */
102 struct MUI_EventHandlerNode ehn;
103 LONG virt_offx, virt_offy; /* diplay offsets */
104 LONG old_virt_offx, old_virt_offy; /* Saved virtual positions,
105 * used for update == 2 */
106 LONG virt_mwidth, virt_mheight; /* The complete width */
107 LONG saved_minwidth, saved_minheight;
108 LONG dont_forward_get; /* Set temporarily to 1 so that the get method
109 * is not forwarded */
110 LONG dont_forward_methods; /* Set temporarily to 1, meaning that the
111 * methods are not forwarded to the group's
112 * children */
113 /* MUI4 group with tabs */
114 Object *titlegroup;
117 /* Note:
118 * The MUI4 feature of group with tabs is implemented based on behaviour of
119 * one application. What this application codes suggest it seems that passing
120 * MUIV_Frame_Register together with MUIA_Group_PageMode, TRUE activates this
121 * mode.
122 * In such mode, the first passed group is used to register tab "titles" and
123 * is always visible. The selection of object in this group selects the
124 * matching (by position) group to be displayed
127 #define GROUP_HORIZ (1<<1)
128 #define GROUP_SAME_WIDTH (1<<2)
129 #define GROUP_SAME_HEIGHT (1<<3)
130 #define GROUP_CHANGING (1<<4)
131 #define GROUP_PAGEMODE (1<<5)
132 #define GROUP_VIRTUAL (1<<6)
133 #define GROUP_HSPACING (1<<7)
134 #define GROUP_VSPACING (1<<8)
135 #define GROUP_CHANGED (1<<9)
138 /* During minmax calculations objects with a weight of 0 shall
139 be treated like they had identical min/def/max size, ie. fixed size.
141 During layout objects with 0 weight must be treated like fixed-sized
142 too, but for hgroups only in x direction, and for vgroups only in
143 y direction. I think ... */
145 #define w0_defwidth(x) (_hweight(x) ? _defwidth(x) : _minwidth(x))
146 #define w0_maxwidth(x) (_hweight(x) ? _maxwidth(x) : _minwidth(x))
148 #define w0_defheight(x) (_vweight(x) ? _defheight(x) : _minheight(x))
149 #define w0_maxheight(x) (_vweight(x) ? _maxheight(x) : _minheight(x))
151 static const int __version = 1;
152 static const int __revision = 1;
154 IPTR Group__MUIM_Show(struct IClass *cl, Object *obj,
155 struct MUIP_Show *msg);
156 IPTR Group__MUIM_Hide(struct IClass *cl, Object *obj,
157 struct MUIP_Hide *msg);
159 /*****************************************************************************/
160 /*****************************************************************************/
163 static ULONG Group_DispatchMsg(struct IClass *cl, Object *obj, Msg msg);
165 static void change_active_page(struct IClass *cl, Object *obj, LONG page)
167 struct MUI_GroupData *data = INST_DATA(cl, obj);
168 LONG newpage, num_children = data->num_children;
170 if (!(data->flags & GROUP_PAGEMODE))
171 return;
173 if (data->titlegroup != NULL)
174 num_children--;
176 switch (page)
178 case MUIV_Group_ActivePage_First:
179 newpage = 0;
180 break;
181 case MUIV_Group_ActivePage_Last:
182 newpage = num_children - 1;
183 break;
184 case MUIV_Group_ActivePage_Prev:
185 newpage = data->active_page - 1;
186 if (newpage == -1)
187 newpage = num_children - 1;
188 break;
189 case MUIV_Group_ActivePage_Next:
190 case MUIV_Group_ActivePage_Advance:
191 newpage = (data->active_page + 1) % num_children;
192 break;
193 default:
194 newpage = page;
195 break;
198 if (newpage != data->active_page)
200 if (_flags(obj) & MADF_CANDRAW)
201 Group__MUIM_Hide(cl, obj, NULL);
203 data->active_page = newpage;
205 if (_flags(obj) & MADF_CANDRAW)
207 DoMethod(obj, MUIM_Layout);
208 Group__MUIM_Show(cl, obj, NULL);
209 data->update = 1;
210 MUI_Redraw(obj, MADF_DRAWUPDATE);
213 if (data->titlegroup)
214 set(data->titlegroup, MUIA_Group_ActivePage, newpage);
218 /**************************************************************************
219 Returns the number of visible children. Visible children are all children
220 that have MADF_SHOWME and not MADF_BORDERGADGET set.
221 **************************************************************************/
222 static int Group_GetNumVisibleChildren(struct MUI_GroupData *data,
223 struct MinList *children)
225 int num_visible_children = data->num_children;
226 Object *cstate;
227 Object *child;
229 /* As there can be invisible children we have to subtract those from
230 * the total number of children */
231 cstate = (Object *) children->mlh_Head;
232 while ((child = NextObject(&cstate)))
234 if (IS_HIDDEN(child))
235 num_visible_children--;
237 return num_visible_children;
240 /**************************************************************************
241 Handles insertion of objects - works based on fact that IDs of all methods
242 that use it are the same for Group and Family and that struct share obj at
243 same offset.
244 **************************************************************************/
245 struct MUIP_StructWithObj
247 STACKED ULONG MethodID;
248 STACKED Object *obj;
251 static IPTR Group__MUIM_AddObject(struct IClass *cl, Object *obj, Msg msg)
253 struct MUI_GroupData *data = INST_DATA(cl, obj);
254 struct MUIP_StructWithObj *msgint = (struct MUIP_StructWithObj *)msg;
256 DoMethodA(data->family, (Msg) msg);
257 data->num_children++;
258 if ((data->flags & GROUP_CHANGING) != 0)
259 data->flags |= GROUP_CHANGED;
261 /* if we are in an application tree, propagate pointers */
262 if (muiNotifyData(obj)->mnd_GlobalInfo)
264 /* Only children of groups can have parents */
266 if ((_flags(obj) & MADF_INVIRTUALGROUP)
267 || (data->flags & GROUP_VIRTUAL))
269 _flags(msgint->obj) |= MADF_INVIRTUALGROUP;
272 DoMethod(msgint->obj, MUIM_ConnectParent, (IPTR) obj);
275 /* Ensure new children are disabled if their parent is */
276 if (XGET(obj, MUIA_Disabled))
277 nnset(obj, MUIA_Disabled, TRUE);
279 /* Some apps (Odyssey) expect _parent() will work before group tree is added to application tree */
280 muiNotifyData(msgint->obj)->mnd_ParentObject = obj;
282 if (_flags(obj) & MADF_SETUP)
284 DoSetupMethod(msgint->obj, muiRenderInfo(obj));
286 /* if (_flags(obj) & MADF_CANDRAW) */
287 /* DoShowMethod(msg->opam_Object); */
289 return TRUE;
292 /**************************************************************************
293 OM_NEW - Constructor
294 **************************************************************************/
295 IPTR Group__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
297 struct MUI_GroupData *data;
298 struct TagItem *tags, *tag;
299 BOOL bad_children = FALSE;
300 IPTR disabled = FALSE;
301 IPTR frame = MUIV_Frame_None;
303 D(bug("[group.mui] OM_NEW, object 0x%p\n", obj));
305 obj = (Object *) DoSuperMethodA(cl, obj, (Msg) msg);
306 if (!obj)
307 return 0;
309 /* Initial local instance data */
310 data = INST_DATA(cl, obj);
312 data->family = MUI_NewObjectA(MUIC_Family, NULL);
313 if (!data->family)
315 CoerceMethod(cl, obj, OM_DISPOSE);
316 return 0;
319 data->horiz_spacing = -1;
320 data->vert_spacing = -1;
321 data->columns = 1;
322 data->rows = 1;
323 data->active_page = 0;
324 get(obj, MUIA_Frame, &frame);
326 /* parse initial taglist */
327 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
329 switch (tag->ti_Tag)
331 case MUIA_Group_Child:
332 D(bug("[group.mui] Adding child 0x%p\n", tag->ti_Data));
333 if (tag->ti_Data)
335 DoMethod(obj, OM_ADDMEMBER, tag->ti_Data);
336 /* Set first child as group title */
337 if ((frame == MUIV_Frame_Register)
338 && (data->titlegroup == NULL))
339 data->titlegroup = (Object *) tag->ti_Data;
341 else
342 bad_children = TRUE;
343 break;
345 case MUIA_Group_ActivePage:
346 change_active_page(cl, obj, (LONG) tag->ti_Data);
347 break;
349 case MUIA_Group_Columns:
350 data->columns = (tag->ti_Data) > 1 ? tag->ti_Data : 1;
351 data->rows = 0;
352 break;
354 case MUIA_Group_Horiz:
355 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_HORIZ);
356 break;
358 case MUIA_Group_HorizSpacing:
359 data->flags |= GROUP_HSPACING;
360 data->horiz_spacing = tag->ti_Data;
361 break;
363 case MUIA_Group_LayoutHook:
364 data->layout_hook = (struct Hook *)tag->ti_Data;
365 break;
367 case MUIA_Group_PageMode:
368 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_PAGEMODE);
369 break;
371 case MUIA_Group_Rows:
372 data->rows = MAX((ULONG) tag->ti_Data, 1);
373 data->columns = 0;
374 break;
376 case MUIA_Group_SameHeight:
377 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_HEIGHT);
378 break;
380 case MUIA_Group_SameSize:
381 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_HEIGHT);
382 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_WIDTH);
383 break;
385 case MUIA_Group_SameWidth:
386 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_WIDTH);
387 break;
389 case MUIA_Group_Spacing:
390 data->flags |= (GROUP_HSPACING | GROUP_VSPACING);
391 data->horiz_spacing = tag->ti_Data;
392 data->vert_spacing = tag->ti_Data;
393 break;
395 case MUIA_Group_VertSpacing:
396 data->flags |= GROUP_VSPACING;
397 data->vert_spacing = tag->ti_Data;
398 break;
400 case MUIA_Group_Virtual:
401 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_VIRTUAL);
402 break;
406 if (bad_children)
408 CoerceMethod(cl, obj, OM_DISPOSE);
409 return 0;
412 /* D(bug("Group_New(0x%lx)\n",obj)); */
414 if (data->flags & GROUP_VIRTUAL)
416 /* This is used by MUI_Render() to determine if group is virtual.
417 * It then installs a clip region.
418 * Also MUI_Layout() uses this. Probably for speed up reason */
419 _flags(obj) |= MADF_ISVIRTUALGROUP;
422 /* will forward MUIA_Disabled to children */
423 get(obj, MUIA_Disabled, &disabled);
424 if (disabled)
426 set(obj, MUIA_Disabled, TRUE);
429 /* This is only used for virtual groups */
430 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS; /* Will be filled on demand */
431 data->ehn.ehn_Priority = 10; /* Will hear the click before all
432 * other normal objects */
433 data->ehn.ehn_Flags = 0;
434 data->ehn.ehn_Object = obj;
435 data->ehn.ehn_Class = cl;
436 return (IPTR) obj;
439 /**************************************************************************
440 OM_DISPOSE
441 **************************************************************************/
442 IPTR Group__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
444 struct MUI_GroupData *data = INST_DATA(cl, obj);
446 if (data->row_infos != NULL)
447 mui_free(data->row_infos);
448 if (data->col_infos != NULL)
449 mui_free(data->col_infos);
450 if (data->family != NULL)
451 MUI_DisposeObject(data->family);
452 return DoSuperMethodA(cl, obj, msg);
455 /**************************************************************************
456 OM_SET
457 **************************************************************************/
458 IPTR Group__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
460 struct MUI_GroupData *data = INST_DATA(cl, obj);
461 struct TagItem *tags = msg->ops_AttrList;
462 struct TagItem *tag;
463 BOOL forward = TRUE;
464 BOOL need_recalc = FALSE;
465 IPTR retval;
467 int virt_offx = data->virt_offx, virt_offy = data->virt_offy;
469 /* There are many ways to find out what tag items provided by set()
470 ** we do know. The best way should be using NextTagItem() and simply
471 ** browsing through the list.
474 /* Parse group attributes before calling DoSuperMethodA(),
475 ** otherwise if an app for example sets up a notification
476 ** on MUIA_Group_ActivePage which calls a hook, and then
477 ** the hook function calls get(obj, MUIA_Group_ActivePage),
478 ** it would get returned the old active page instead of the new
479 ** active page
482 while ((tag = NextTagItem(&tags)) != NULL)
484 switch (tag->ti_Tag)
486 case MUIA_Group_Columns:
487 data->columns = MAX((ULONG) tag->ti_Data, 1);
488 data->rows = 0;
489 need_recalc = TRUE;
490 break;
491 case MUIA_Group_ActivePage:
492 change_active_page(cl, obj, (LONG) tag->ti_Data);
493 break;
494 case MUIA_Group_Forward:
495 forward = tag->ti_Data;
496 break;
497 case MUIA_Group_HorizSpacing:
498 data->flags |= GROUP_HSPACING;
499 data->horiz_spacing = tag->ti_Data;
500 break;
501 case MUIA_Group_Rows:
502 data->rows = MAX((ULONG) tag->ti_Data, 1);
503 data->columns = 0;
504 need_recalc = TRUE;
505 break;
506 case MUIA_Group_Spacing:
507 data->flags |= (GROUP_HSPACING | GROUP_VSPACING);
508 data->horiz_spacing = tag->ti_Data;
509 data->vert_spacing = tag->ti_Data;
510 break;
511 case MUIA_Group_VertSpacing:
512 data->flags |= GROUP_VSPACING;
513 data->vert_spacing = tag->ti_Data;
514 break;
515 case MUIA_Virtgroup_Left:
516 //kprintf("set virtgroup_left: %d\n", tag->ti_Data);
517 virt_offx = tag->ti_Data;
518 break;
520 case MUIA_Group_LayoutHook:
522 [ach] Seems like MUI supports setting this attribute after
523 initialization, even though the documentation states
524 otherwise. At least some programs use it...
526 data->layout_hook = (struct Hook *)tag->ti_Data;
527 break;
529 case MUIA_Virtgroup_Top:
530 //kprintf("set virtgroup_top: %d\n", tag->ti_Data);
531 virt_offy = tag->ti_Data;
532 break;
537 if (muiRenderInfo(obj) && need_recalc)
538 DoMethod(_win(obj), MUIM_Window_RecalcDisplay, (IPTR) obj);
540 retval = DoSuperMethodA(cl, obj, (Msg) msg);
542 /* seems to be the documented behaviour, however it should be slow! */
544 if (forward)
546 /* Attributes which are to be filtered out, so that they are ignored
547 when OM_SET is passed to group's children */
549 tags = msg->ops_AttrList;
550 while ((tag = NextTagItem(&tags)) != NULL)
552 switch (tag->ti_Tag)
554 case MUIA_HelpLine:
555 case MUIA_HelpNode:
556 case MUIA_ObjectID:
557 case MUIA_UserData:
559 case MUIA_ContextMenu:
560 case MUIA_ContextMenuTrigger:
561 case MUIA_ControlChar:
562 case MUIA_CycleChain:
563 case MUIA_Draggable:
564 case MUIA_FillArea:
565 case MUIA_Group_ActivePage:
566 case MUIA_Frame:
567 case MUIA_FrameTitle:
568 case MUIA_HorizWeight:
569 case MUIA_Pressed:
570 case MUIA_ShortHelp:
571 case MUIA_ShowMe:
572 case MUIA_VertWeight:
573 case MUIA_Weight:
574 case MUIA_Virtgroup_Left:
575 case MUIA_Virtgroup_Top:
576 case MUIA_AppMessage:
577 case MUIA_Timer:
578 tag->ti_Tag = TAG_IGNORE;
579 break;
580 case MUIA_Selected:
581 /* D(bug("Group_Set(%p) MUIA_Selected forwarded\n", obj)); */
582 /* tag->ti_Tag = TAG_IGNORE; */
583 break;
587 Group_DispatchMsg(cl, obj, (Msg) msg);
591 if (virt_offx != data->virt_offx || virt_offy != data->virt_offy)
593 if (_flags(obj) & MADF_CANDRAW)
594 Group__MUIM_Hide(cl, obj, NULL);
595 data->virt_offx = virt_offx;
596 data->virt_offy = virt_offy;
597 /* Relayout ourself, this will also relayout all the children */
598 DoMethod(obj, MUIM_Layout);
599 if (_flags(obj) & MADF_CANDRAW)
600 Group__MUIM_Show(cl, obj, NULL);
601 data->update = 2;
602 MUI_Redraw(obj, MADF_DRAWUPDATE);
605 return retval;
609 /**************************************************************************
610 OM_GET
611 **************************************************************************/
612 IPTR Group__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
614 /* small macro to simplify return value storage */
615 #define STORE *(msg->opg_Storage)
617 struct MUI_GroupData *data = INST_DATA(cl, obj);
619 switch (msg->opg_AttrID)
621 case MUIA_Version:
622 STORE = __version;
623 return 1;
624 case MUIA_Revision:
625 STORE = __revision;
626 return 1;
627 case MUIA_Group_ActivePage:
628 STORE = data->active_page;
629 return 1;
630 case MUIA_Group_ChildList:
631 return GetAttr(MUIA_Family_List, data->family, msg->opg_Storage);
632 case MUIA_Group_Horiz:
633 STORE = (data->flags & GROUP_HORIZ);
634 return 1;
635 case MUIA_Group_HorizSpacing:
636 STORE = data->horiz_spacing;
637 return 1;
638 case MUIA_Group_VertSpacing:
639 STORE = data->vert_spacing;
640 return 1;
641 case MUIA_Group_ChildCount:
642 STORE = data->num_children;
643 return 1;
644 case MUIA_Virtgroup_Left:
645 STORE = data->virt_offx;
646 return 1;
647 case MUIA_Virtgroup_Top:
648 STORE = data->virt_offy;
649 return 1;
650 case MUIA_Virtgroup_Width:
651 STORE = data->virt_mwidth;
652 return 1;
653 case MUIA_Virtgroup_Height:
654 STORE = data->virt_mheight;
655 return 1;
656 case MUIA_Virtgroup_MinWidth:
657 STORE = data->saved_minwidth;
658 return 1;
659 case MUIA_Virtgroup_MinHeight:
660 STORE = data->saved_minheight;
661 return 1;
664 /* our handler didn't understand the attribute, we simply pass
665 ** it to our superclass now
667 if (DoSuperMethodA(cl, obj, (Msg) msg))
668 return 1;
670 /* seems to be the documented behaviour, however it should be slow! */
671 if (!data->dont_forward_get && !data->dont_forward_methods)
673 Object *cstate;
674 Object *child;
675 struct MinList *ChildList = NULL;
677 get(data->family, MUIA_Family_List, &(ChildList));
678 cstate = (Object *) ChildList->mlh_Head;
679 while ((child = NextObject(&cstate)))
680 if (DoMethodA(child, (Msg) msg))
681 return 1;
683 return 0;
684 #undef STORE
688 /**************************************************************************
689 MUIM_AddTail
690 **************************************************************************/
691 IPTR Group__MUIM_AddTail(struct IClass *cl, Object *obj,
692 struct MUIP_Group_AddTail *msg)
694 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
697 /**************************************************************************
698 MUIM_AddHead
699 **************************************************************************/
700 IPTR Group__MUIM_AddHead(struct IClass *cl, Object *obj,
701 struct MUIP_Group_AddHead *msg)
703 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
706 /**************************************************************************
707 MUIM_Insert
708 **************************************************************************/
709 IPTR Group__MUIM_Insert(struct IClass *cl, Object *obj,
710 struct MUIP_Group_Insert *msg)
712 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
715 /**************************************************************************
716 MUIM_Remove
717 **************************************************************************/
718 IPTR Group__MUIM_Remove(struct IClass *cl, Object *obj,
719 struct MUIP_Group_Remove *msg)
721 struct MUI_GroupData *data = INST_DATA(cl, obj);
723 if (_flags(obj) & MADF_CANDRAW)
724 DoHideMethod(msg->obj);
725 if (_flags(obj) & MADF_SETUP)
726 DoMethod(msg->obj, MUIM_Cleanup);
727 if (muiNotifyData(obj)->mnd_GlobalInfo)
729 DoMethod(msg->obj, MUIM_DisconnectParent);
730 muiNotifyData(msg->obj)->mnd_ParentObject = NULL;
732 _flags(msg->obj) &= ~MADF_INVIRTUALGROUP;
735 if ((data->flags & GROUP_CHANGING) != 0)
736 data->flags |= GROUP_CHANGED;
737 data->num_children--;
738 DoMethodA(data->family, (Msg) msg);
740 return TRUE;
744 /**************************************************************************
745 MUIM_ConnectParent
746 **************************************************************************/
747 IPTR Group__MUIM_ConnectParent(struct IClass *cl, Object *obj,
748 struct MUIP_ConnectParent *msg)
750 struct MUI_GroupData *data = INST_DATA(cl, obj);
751 Object *cstate;
752 Object *child;
753 struct MinList *ChildList = NULL;
755 DoSuperMethodA(cl, obj, (Msg) msg);
757 get(data->family, MUIA_Family_List, &(ChildList));
758 cstate = (Object *) ChildList->mlh_Head;
759 while ((child = NextObject(&cstate)))
761 if ((_flags(obj) & MADF_INVIRTUALGROUP)
762 || (data->flags & GROUP_VIRTUAL))
764 _flags(child) |= MADF_INVIRTUALGROUP;
767 /* Only children of groups can have parents */
768 muiNotifyData(child)->mnd_ParentObject = obj;
770 DoMethod(child, MUIM_ConnectParent, (IPTR) obj);
772 return TRUE;
775 /**************************************************************************
776 MUIM_DisconnectParent
777 **************************************************************************/
778 IPTR Group__MUIM_DisconnectParent(struct IClass *cl, Object *obj,
779 struct MUIP_ConnectParent *msg)
781 struct MUI_GroupData *data = INST_DATA(cl, obj);
782 Object *cstate;
783 Object *child;
784 struct MinList *ChildList = NULL;
786 get(data->family, MUIA_Family_List, &(ChildList));
787 cstate = (Object *) ChildList->mlh_Head;
788 while ((child = NextObject(&cstate)))
790 DoMethodA(child, (Msg) msg);
791 muiNotifyData(child)->mnd_ParentObject = NULL;
792 _flags(child) &= ~MADF_INVIRTUALGROUP;
794 DoSuperMethodA(cl, obj, (Msg) msg);
795 return TRUE;
799 * Put group in exchange state
801 IPTR Group__MUIM_InitChange(struct IClass *cl, Object *obj,
802 struct MUIP_Group_InitChange *msg)
804 struct MUI_GroupData *data = INST_DATA(cl, obj);
806 data->flags &= ~GROUP_CHANGED;
807 data->flags |= GROUP_CHANGING;
808 return TRUE;
813 * Will recalculate display after dynamic adding/removing
815 IPTR Group__MUIM_ExitChange(struct IClass *cl, Object *obj,
816 struct MUIP_Group_ExitChange *msg)
818 struct MUI_GroupData *data = INST_DATA(cl, obj);
820 data->flags &= ~GROUP_CHANGING;
822 if (data->flags & GROUP_CHANGED)
824 data->flags &= ~GROUP_CHANGED;
826 if ((_flags(obj) & MADF_SETUP) && _win(obj))
828 Object *win = _win(obj);
829 Object *parent = obj;
831 /* CHECKME: Don't call RecalcDisplay if one of our parents is
832 in GROUP_CHANGING state to prevent crash with Zune prefs
833 program NListtree page because NList/NListtree when
834 killing tree images in MUIM_Cleanup uses InitChange/
835 ExitChange. Zune prefs program uses InitChange/ExitChange
836 when switching page -> nesting -> mess. */
838 while ((parent = _parent(parent)))
840 struct MUI_GroupData *pdata = INST_DATA(cl, parent);
842 if (parent == win)
843 break;
845 if (pdata->flags & GROUP_CHANGING)
847 return TRUE;
852 DoMethod(win, MUIM_Window_RecalcDisplay, (IPTR) obj);
856 return TRUE;
861 * Will recalculate display after dynamic adding/removing
863 IPTR Group__MUIM_ExitChange2(struct IClass *cl, Object *obj,
864 struct MUIP_Group_ExitChange2 *msg)
866 struct MUI_GroupData *data = INST_DATA(cl, obj);
868 if (data->flags & GROUP_CHANGING)
870 data->flags &= ~(GROUP_CHANGING | GROUP_CHANGED);
872 if ((_flags(obj) & MADF_SETUP) && _win(obj))
874 Object *win = _win(obj);
875 Object *parent = obj;
877 /* CHECKME: Don't call RecalcDisplay if one of our parents is
878 in GROUP_CHANGING state to prevent crash with Zune prefs
879 program NListtree page because NList/NListtree when
880 killing tree images in MUIM_Cleanup uses InitChange/
881 ExitChange. Zune prefs program uses InitChange/ExitChange
882 when switching page -> nesting -> mess. */
884 while ((parent = _parent(parent)))
886 struct MUI_GroupData *pdata = INST_DATA(cl, parent);
888 if (parent == win)
889 break;
891 if (pdata->flags & GROUP_CHANGING)
893 return TRUE;
898 DoMethod(win, MUIM_Window_RecalcDisplay, (IPTR) obj);
902 return TRUE;
907 * Sort the family
909 IPTR Group__MUIM_Sort(struct IClass *cl, Object *obj,
910 struct MUIP_Group_Sort *msg)
912 struct MUI_GroupData *data = INST_DATA(cl, obj);
914 /* modify message */
915 msg->MethodID = MUIM_Family_Sort;
917 DoMethodA(data->family, (APTR) msg);
919 /* restore original message */
920 msg->MethodID = MUIM_Group_Sort;
921 return TRUE;
924 /**************************************************************************
925 MUIM_Group_DoMethodNoForward
927 Executes the given method but does not forward it to the children
928 **************************************************************************/
929 IPTR Group__MUIM_DoMethodNoForward(struct IClass *cl, Object *obj,
930 struct MUIP_Group_DoMethodNoForward *msg)
932 struct MUI_GroupData *data = INST_DATA(cl, obj);
933 IPTR rc;
934 data->dont_forward_methods = 1; /* disable forwarding */
935 rc = DoMethodA(obj, (Msg) & msg->DoMethodID);
936 /* Probably doesn't work correctly on AROS? */
938 data->dont_forward_methods = 0;
939 return rc;
943 * Propagate a method to group children.
945 static ULONG Group_DispatchMsg(struct IClass *cl, Object *obj, Msg msg)
947 struct MUI_GroupData *data = INST_DATA(cl, obj);
948 Object *cstate;
949 Object *child;
950 struct MinList *ChildList = NULL;
952 if (data->dont_forward_methods)
953 return TRUE;
955 get(data->family, MUIA_Family_List, &(ChildList));
956 cstate = (Object *) ChildList->mlh_Head;
957 while ((child = NextObject(&cstate)))
959 DoMethodA(child, (Msg) msg);
961 return TRUE;
965 /**************************************************************************
966 MUIM_Setup
967 **************************************************************************/
968 IPTR Group__MUIM_Setup(struct IClass *cl, Object *obj,
969 struct MUIP_Setup *msg)
971 struct MUI_GroupData *data = INST_DATA(cl, obj);
972 Object *cstate;
973 Object *cstate_copy;
974 Object *child;
975 Object *childFailed;
976 struct MinList *ChildList = NULL;
978 if (!DoSuperMethodA(cl, obj, (Msg) msg))
979 return FALSE;
981 ASSERT_VALID_PTR(muiGlobalInfo(obj));
983 if (!(data->flags & GROUP_HSPACING))
984 data->horiz_spacing = muiGlobalInfo(obj)->mgi_Prefs->group_hspacing;
985 if (!(data->flags & GROUP_VSPACING))
986 data->vert_spacing = muiGlobalInfo(obj)->mgi_Prefs->group_vspacing;
987 get(data->family, MUIA_Family_List, &(ChildList));
988 cstate = cstate_copy = (Object *) ChildList->mlh_Head;
989 while ((child = NextObject(&cstate)))
991 #if 0 /* SHOWME affects only show/hide */
992 if (!(_flags(child) & MADF_SHOWME))
993 continue;
994 #endif
996 if (!DoSetupMethod(child, msg->RenderInfo))
998 /* Send MUIM_Cleanup to all objects that received MUIM_Setup.
1000 childFailed = child;
1001 cstate = cstate_copy;
1002 while ((child = NextObject(&cstate)) && (child != childFailed))
1004 #if 0 /* SHOWME affects only show/hide */
1005 if (!(_flags(child) & MADF_SHOWME))
1006 continue;
1007 #endif
1008 DoMethod(child, MUIM_Cleanup);
1010 return FALSE;
1014 if (data->flags & GROUP_VIRTUAL)
1016 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
1017 (IPTR) & data->ehn);
1020 return TRUE;
1024 /**************************************************************************
1025 MUIM_Cleanup
1026 **************************************************************************/
1027 IPTR Group__MUIM_Cleanup(struct IClass *cl, Object *obj, Msg msg)
1029 struct MUI_GroupData *data = INST_DATA(cl, obj);
1030 Object *cstate;
1031 Object *child;
1032 struct MinList *ChildList = NULL;
1034 if (data->flags & GROUP_VIRTUAL)
1036 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
1037 (IPTR) & data->ehn);
1040 get(data->family, MUIA_Family_List, &(ChildList));
1041 cstate = (Object *) ChildList->mlh_Head;
1042 while ((child = NextObject(&cstate)))
1044 #if 0 /* SHOWME affects only show/hide */
1045 if (!(_flags(child) & MADF_SHOWME))
1046 continue;
1047 #endif
1048 DoMethodA(child, (Msg) msg);
1050 return DoSuperMethodA(cl, obj, (Msg) msg);
1055 /**************************************************************************
1056 MUIM_Draw - draw the group
1057 **************************************************************************/
1058 IPTR Group__MUIM_Draw(struct IClass *cl, Object *obj,
1059 struct MUIP_Draw *msg)
1061 struct MUI_GroupData *data = INST_DATA(cl, obj);
1062 Object *cstate;
1063 Object *child;
1064 struct MinList *ChildList = NULL;
1065 struct Rectangle group_rect; /* child_rect; */
1066 int page;
1067 struct Region *region = NULL;
1068 APTR clip = (APTR) - 1;
1070 /* D(bug("Group_Draw(%lx) %ldx%ldx%ldx%ld upd=%d page=%d\n", */
1071 /* obj,_left(obj),_top(obj),_right(obj),_bottom(obj), */
1072 /* data->update, data->active_page)); */
1073 /* D(bug("Group_Draw(%p) msg=0x%08lx flags=0x%08lx\n", */
1074 /* obj, msg->flags, _flags(obj))); */
1076 if (data->flags & GROUP_CHANGING)
1077 return FALSE;
1079 if (muiGlobalInfo(obj)->mgi_Prefs->window_redraw ==
1080 WINDOW_REDRAW_WITHOUT_CLEAR)
1082 region = NewRegion();
1083 if (region)
1085 struct Rectangle rect;
1087 rect.MinX = _left(obj);
1088 rect.MinY = _top(obj);
1089 rect.MaxX = _right(obj);
1090 rect.MaxY = _bottom(obj);
1092 OrRectRegion(region, &rect);
1093 page = -1;
1094 get(data->family, MUIA_Family_List, &(ChildList));
1095 cstate = (Object *) ChildList->mlh_Head;
1096 while ((child = NextObject(&cstate)))
1098 if (child != data->titlegroup)
1099 ++page;
1101 if ((data->flags & GROUP_PAGEMODE) && ((page != data->active_page)
1102 && (child != data->titlegroup)))
1103 continue;
1105 if ((muiAreaData(child)->mad_Flags & MADF_CANDRAW)
1106 && (_width(child) > 0) && (_height(child) > 0))
1108 rect.MinX = MAX(_left(child), _mleft(obj));
1109 rect.MinY = MAX(_top(child), _mtop(obj));
1110 rect.MaxX = MIN(_right(child), _mright(obj));
1111 rect.MaxY = MIN(_bottom(child), _mbottom(obj));
1113 if ((rect.MaxX >= rect.MinX)
1114 && (rect.MaxY >= rect.MinY))
1116 ClearRectRegion(region, &rect);
1121 clip = MUI_AddClipRegion(muiRenderInfo(obj), region);
1125 DoSuperMethodA(cl, obj, (Msg) msg);
1127 if (region)
1129 MUI_RemoveClipRegion(muiRenderInfo(obj), clip);
1130 region = NULL;
1133 else
1135 DoSuperMethodA(cl, obj, (Msg) msg);
1137 /* D(bug("Group_Draw(%p) (after dsma) msg=0x%08lx flags=0x%08lx\n", */
1138 /* obj, msg->flags, _flags(obj))); */
1140 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 1)
1143 * update is set when changing active page of a page group
1144 * need to redraw background ourself
1146 DoMethod(obj, MUIM_DrawBackground,
1147 _mleft(obj), _mtop(obj), _mwidth(obj), _mheight(obj),
1148 _mleft(obj), _mtop(obj), 0);
1150 data->update = 0;
1152 else
1154 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 2)
1156 LONG left, top, right, bottom;
1157 LONG diff_virt_offx = data->virt_offx - data->old_virt_offx;
1158 LONG diff_virt_offy = data->virt_offy - data->old_virt_offy;
1159 struct Rectangle rect;
1160 struct Rectangle *clip_rect = &muiRenderInfo(obj)->mri_ClipRect;
1162 data->update = 0;
1164 if (!diff_virt_offx && !diff_virt_offy)
1166 return 1;
1169 /* sba: I don't know how MUI handle this but ScrollRasterBF() made problems when scrolling
1170 ** a (partly visible) virtual groups in a virtual group, because e.g. _mtop() is then
1171 ** smaller than the region. ScrollRasterBF() on AmigaOS then marks the complete region
1172 ** as damaged. Using ScrollWindowRaster() solved that problem but it flickers then.
1173 ** To avoid this we prevent that the scroll area is out of the region bounds.
1174 ** The region bounds are setted in MUI_Redraw() but should probably should go in the
1175 ** MUI's clip functions
1178 left = MAX(_mleft(obj), clip_rect->MinX);
1179 top = MAX(_mtop(obj), clip_rect->MinY);
1180 right = MIN(_mright(obj), clip_rect->MaxX);
1181 bottom = MIN(_mbottom(obj), clip_rect->MaxY);
1183 /* old code was
1184 ** ScrollRasterBF(_rp(obj), diff_virt_offx, diff_virt_offy, _mleft(obj), _mtop(obj), _mright(obj),_mbottom(obj));
1187 ScrollWindowRaster(_window(obj), diff_virt_offx, diff_virt_offy,
1188 left, top, right, bottom);
1190 if ((region = NewRegion()))
1192 if (diff_virt_offx)
1194 rect.MinY = top;
1195 rect.MaxY = bottom;
1197 if (diff_virt_offx > 0)
1199 rect.MinX = right - diff_virt_offx + 1;
1200 if (rect.MinX < left)
1201 rect.MinX = left;
1202 rect.MaxX = right;
1204 else
1206 rect.MinX = left;
1207 rect.MaxX = left - diff_virt_offx - 1;
1208 if (rect.MaxX > right)
1209 rect.MaxX = right;
1212 if (rect.MinX <= rect.MaxX)
1214 DoMethod(obj, MUIM_DrawBackground,
1215 rect.MinX, rect.MinY,
1216 rect.MaxX - rect.MinX + 1,
1217 rect.MaxY - rect.MinY + 1,
1218 rect.MinX, rect.MinY, 0);
1220 OrRectRegion(region, &rect);
1224 if (diff_virt_offy)
1226 rect.MinX = left;
1227 rect.MaxX = right;
1229 if (diff_virt_offy > 0)
1231 rect.MinY = bottom - diff_virt_offy + 1;
1232 if (rect.MinY < top)
1233 rect.MinY = top;
1234 rect.MaxY = bottom;
1236 else
1238 rect.MinY = top;
1239 rect.MaxY = top - diff_virt_offy - 1;
1240 if (rect.MaxY > bottom)
1241 rect.MaxY = bottom;
1243 if (rect.MinY <= rect.MaxY)
1245 DoMethod(obj, MUIM_DrawBackground,
1246 rect.MinX, rect.MinY,
1247 rect.MaxX - rect.MinX + 1,
1248 rect.MaxY - rect.MinY + 1,
1249 rect.MinX, rect.MinY, 0);
1251 OrRectRegion(region, &rect);
1257 else
1259 if (!(msg->flags & MADF_DRAWOBJECT)
1260 && !(msg->flags & MADF_DRAWALL))
1261 return TRUE;
1265 if (data->flags & GROUP_VIRTUAL && !region)
1267 /* Not really needed if MUI Draws all the objects, maybe that's
1268 * what DRAWALL is for??? */
1269 if ((region = NewRegion()))
1271 struct Rectangle rect;
1272 rect.MinX = _mleft(obj);
1273 rect.MinY = _mtop(obj);
1274 rect.MaxX = _mright(obj);
1275 rect.MaxY = _mbottom(obj);
1276 OrRectRegion(region, &rect);
1280 /* Add clipping region if we have one */
1281 if (region)
1282 clip = MUI_AddClipRegion(muiRenderInfo(obj), region);
1284 group_rect = muiRenderInfo(obj)->mri_ClipRect;
1285 page = -1;
1286 get(data->family, MUIA_Family_List, &(ChildList));
1287 cstate = (Object *) ChildList->mlh_Head;
1288 while ((child = NextObject(&cstate)))
1290 if (!(_flags(child) & MADF_SHOWME))
1291 continue;
1293 if (child != data->titlegroup)
1294 ++page;
1296 if ((data->flags & GROUP_PAGEMODE) && ((page != data->active_page)
1297 && (child != data->titlegroup)))
1299 continue;
1302 // msg->flags |= MADF_DRAWOBJECT; /* yup, do not forget */
1304 // child_rect.MinX = _left(child);
1305 // child_rect.MinY = _top(child);
1306 // child_rect.MaxX = _right(child);
1307 // child_rect.MaxY = _bottom(child);
1308 /* g_print("intersect: a=(%d, %d, %d, %d) b=(%d, %d, %d, %d)\n", */
1309 /* group_rect.x, group_rect.y, */
1310 /* group_rect.width, group_rect.height, */
1311 /* child_rect.x, child_rect.y, */
1312 /* child_rect.width, child_rect.height); */
1314 // if (gdk_rectangle_intersect(&group_rect, &child_rect,
1315 // &muiRenderInfo(obj)->mri_ClipRect))
1316 // DoMethodA(child, (Msg)msg);
1317 /* if (((msg->flags & MADF_DRAWUPDATE) && data->update) */
1318 /* || (data->flags & GROUP_PAGEMODE)) */
1319 MUI_Redraw(child, MADF_DRAWOBJECT);
1320 /* else */
1321 /* MUI_Redraw(child, msg->flags); */
1322 muiRenderInfo(obj)->mri_ClipRect = group_rect;
1323 /* g_print("set back clip to (%d, %d, %d, %d)\n", */
1324 /* group_rect.x, group_rect.y, group_rect.width, group_rect.height); */
1326 /* D(bug("Group_Draw(%p) end\n", obj)); */
1328 if (data->flags & GROUP_VIRTUAL && region && clip != (APTR) - 1)
1330 MUI_RemoveClipRegion(muiRenderInfo(obj), clip);
1333 data->old_virt_offx = data->virt_offx;
1334 data->old_virt_offy = data->virt_offy;
1335 data->update = 0;
1337 return TRUE;
1341 #define END_MINMAX() \
1342 tmp.MaxHeight = MAX(tmp.MaxHeight, tmp.MinHeight); \
1343 tmp.MaxWidth = MAX(tmp.MaxWidth, tmp.MinWidth); \
1344 tmp.DefHeight = CLAMP(tmp.DefHeight, tmp.MinHeight, tmp.MaxHeight); \
1345 tmp.DefWidth = CLAMP(tmp.DefWidth, tmp.MinWidth, tmp.MaxWidth); \
1346 msg->MinMaxInfo->MinWidth += tmp.MinWidth; \
1347 msg->MinMaxInfo->MinHeight += tmp.MinHeight; \
1348 msg->MinMaxInfo->MaxWidth += tmp.MaxWidth; \
1349 msg->MinMaxInfo->MaxHeight += tmp.MaxHeight; \
1350 msg->MinMaxInfo->DefWidth += tmp.DefWidth; \
1351 msg->MinMaxInfo->DefHeight += tmp.DefHeight;
1354 * MinMax calculation function. When this is called,
1355 * the children of your group have already been asked
1356 * about their min/max dimension so you can use their
1357 * dimensions to calculate yours.
1359 * Notes:
1360 * - Init minwidth and maxwidth with size needed for total child spacing.
1361 * - 1st pass to find maximum minimum width, to set minwidth of each child
1362 * if they should have the same width (for a row of buttons ...)
1363 * - Adjust minwidth w/o making object bigger than their max size.
1365 static void group_minmax_horiz(struct IClass *cl, Object *obj,
1366 struct MinList *children, struct MUIP_AskMinMax *msg)
1368 struct MUI_GroupData *data = INST_DATA(cl, obj);
1369 Object *cstate;
1370 Object *child;
1371 struct MUI_MinMax tmp;
1372 WORD maxminwidth = 0;
1373 BOOL found_nonzero_vweight = FALSE;
1375 tmp.MinHeight = 0;
1376 tmp.DefHeight = 0;
1377 tmp.MaxHeight = MUI_MAXMAX;
1378 if (data->num_visible_children > 0)
1380 tmp.MinWidth = tmp.DefWidth = tmp.MaxWidth =
1381 (data->num_visible_children - 1) * data->horiz_spacing;
1383 else
1385 tmp.MinWidth = tmp.DefWidth = 0;
1386 tmp.MaxWidth = MUI_MAXMAX;
1389 if (data->flags & GROUP_SAME_WIDTH)
1391 cstate = (Object *) children->mlh_Head;
1392 while ((child = NextObject(&cstate)))
1394 if (IS_HIDDEN(child))
1395 continue;
1396 maxminwidth = MAX(maxminwidth, _minwidth(child));
1400 data->samesize_maxmin_horiz = maxminwidth;
1401 /* D(bug("group_minmax_horiz(%p) : maxminwidth=%d\n", obj, maxminwidth)); */
1403 data->horiz_weight_sum = 0;
1404 cstate = (Object *) children->mlh_Head;
1405 while ((child = NextObject(&cstate)))
1407 WORD minwidth;
1409 if (IS_HIDDEN(child))
1410 continue;
1411 if (data->flags & GROUP_SAME_WIDTH)
1413 minwidth = MAX(maxminwidth, _minwidth(child));
1414 minwidth = MIN(minwidth, _maxwidth(child));
1416 else
1417 minwidth = _minwidth(child);
1418 tmp.MinWidth += minwidth;
1419 tmp.DefWidth += w0_defwidth(child);
1420 tmp.MaxWidth += w0_maxwidth(child);
1421 tmp.MaxWidth = MIN(tmp.MaxWidth, MUI_MAXMAX);
1422 tmp.MinHeight = MAX(tmp.MinHeight, _minheight(child));
1423 tmp.DefHeight = MAX(tmp.DefHeight, _defheight(child));
1425 if all children have null weight then maxheight=minheight
1426 if all but some children have null weights, the maxheight
1427 is the min of all maxheights
1429 tmp.MaxHeight = MIN(tmp.MaxHeight, _maxheight(child));
1430 data->horiz_weight_sum += _hweight(child);
1431 if (_vweight(child) > 0)
1433 found_nonzero_vweight = TRUE;
1436 if (!found_nonzero_vweight)
1438 tmp.MaxHeight = tmp.MinHeight;
1441 //if (data->flags & GROUP_VIRTUAL)
1443 //kprintf("# min %d x %d def %d x %d max %d x %d\n",
1444 // tmp.MinWidth, tmp.MinHeight,
1445 // tmp.DefWidth, tmp.DefHeight,
1446 // tmp.MaxWidth, tmp.MaxHeight);
1449 END_MINMAX();
1452 /* minmax calculation for vertical groups (see group_minmax_horiz)
1454 static void group_minmax_vert(struct IClass *cl, Object *obj,
1455 struct MinList *children, struct MUIP_AskMinMax *msg)
1457 struct MUI_GroupData *data = INST_DATA(cl, obj);
1458 Object *cstate;
1459 Object *child;
1460 struct MUI_MinMax tmp;
1461 WORD maxminheight = 0;
1462 BOOL found_nonzero_hweight = FALSE;
1464 tmp.MinWidth = 0;
1465 tmp.DefWidth = 0;
1466 tmp.MaxWidth = MUI_MAXMAX;
1467 if (data->num_visible_children > 0)
1469 tmp.MinHeight = tmp.DefHeight = tmp.MaxHeight =
1470 (data->num_visible_children - 1) * data->vert_spacing;
1472 else
1474 tmp.MinHeight = tmp.DefHeight = 0;
1475 tmp.MaxHeight = MUI_MAXMAX;
1478 if (data->flags & GROUP_SAME_HEIGHT)
1480 cstate = (Object *) children->mlh_Head;
1481 while ((child = NextObject(&cstate)))
1483 if (IS_HIDDEN(child))
1484 continue;
1485 maxminheight = MAX(maxminheight, _minheight(child));
1489 data->samesize_maxmin_vert = maxminheight;
1490 data->vert_weight_sum = 0;
1491 cstate = (Object *) children->mlh_Head;
1492 while ((child = NextObject(&cstate)))
1494 if (IS_HIDDEN(child))
1495 continue;
1497 if (data->flags & GROUP_SAME_HEIGHT)
1498 _minheight(child) = MIN(maxminheight, w0_maxheight(child));
1499 tmp.MinHeight += _minheight(child);
1500 tmp.DefHeight += w0_defheight(child);
1501 tmp.MaxHeight += w0_maxheight(child);
1502 tmp.MaxHeight = MIN(tmp.MaxHeight, MUI_MAXMAX);
1503 tmp.MinWidth = MAX(tmp.MinWidth, _minwidth(child));
1504 tmp.DefWidth = MAX(tmp.DefWidth, _defwidth(child));
1505 tmp.MaxWidth = MIN(tmp.MaxWidth, _maxwidth(child));
1506 data->vert_weight_sum += _vweight(child);
1507 if (_hweight(child) > 0)
1509 found_nonzero_hweight = TRUE;
1512 if (!found_nonzero_hweight)
1514 tmp.MaxWidth = tmp.MinWidth;
1517 END_MINMAX();
1521 static void
1522 minmax_2d_rows_pass(struct MUI_GroupData *data, struct MinList *children,
1523 struct MUI_MinMax *req, WORD maxmin_height, WORD maxdef_height)
1525 int i, j;
1526 Object *cstate;
1527 Object *child;
1529 /* do not rewind after the while, to process line by line */
1530 cstate = (Object *) children->mlh_Head;
1531 /* for each row */
1532 for (i = 0; i < data->rows; i++)
1534 /* calculate min and max height of this row */
1535 int min_h = 0, def_h = 0, max_h = MUI_MAXMAX;
1536 BOOL found_nonzero_vweight = FALSE;
1538 data->row_infos[i].weight = 0;
1540 j = 0;
1541 while ((child = NextObject(&cstate)))
1543 if (IS_HIDDEN(child))
1544 continue;
1545 if (data->flags & GROUP_SAME_HEIGHT)
1547 _minheight(child) = MIN(maxmin_height, w0_maxheight(child));
1548 _defheight(child) = MIN(maxdef_height, w0_maxheight(child));
1550 min_h = MAX(min_h, _minheight(child));
1551 def_h = MAX(def_h, w0_defheight(child));
1552 max_h = MIN(max_h, _maxheight(child));
1553 if (_vweight(child) > 0)
1555 found_nonzero_vweight = TRUE;
1556 data->row_infos[i].weight += _vweight(child);
1558 ++j;
1559 if ((j % data->columns) == 0)
1560 break;
1562 if (!found_nonzero_vweight)
1563 max_h = min_h;
1564 else
1565 max_h = MAX(max_h, min_h);
1566 /* D(bug("row %d : min_h=%d max_h=%d\n", i, min_h, max_h)); */
1568 data->row_infos[i].min = min_h;
1569 data->row_infos[i].max = max_h;
1570 data->vert_weight_sum += data->row_infos[i].weight;
1572 req->MinHeight += min_h;
1573 req->DefHeight += def_h;
1574 req->MaxHeight += max_h;
1575 if (req->MaxHeight > MUI_MAXMAX)
1576 req->MaxHeight = MUI_MAXMAX;
1581 static void
1582 minmax_2d_columns_pass(struct MUI_GroupData *data, struct MinList *children,
1583 struct MUI_MinMax *req, WORD maxmin_width, WORD maxdef_width)
1585 int i, j;
1586 Object *cstate;
1587 Object *child;
1589 for (i = 0; i < data->columns; i++)
1591 /* calculate min and max width of this column */
1592 int min_w = 0, def_w = 0, max_w = MUI_MAXMAX;
1593 BOOL found_nonzero_hweight = FALSE;
1595 data->col_infos[i].weight = 0;
1597 j = 0;
1598 /* process all children to get children on a column */
1599 cstate = (Object *) children->mlh_Head;
1600 while ((child = NextObject(&cstate)))
1602 if (IS_HIDDEN(child))
1603 continue;
1604 ++j;
1605 if (((j - 1) % data->columns) != i)
1606 continue;
1607 if (data->flags & GROUP_SAME_WIDTH)
1609 _minwidth(child) = MIN(maxmin_width, w0_maxwidth(child));
1610 _defwidth(child) = MIN(maxdef_width, w0_maxwidth(child));
1612 min_w = MAX(min_w, _minwidth(child));
1613 def_w = MAX(def_w, w0_defwidth(child));
1615 /* this handles the case of null weight children, which limit
1616 * the max size if they're alone, but not if they are with
1617 * non-null weight obj
1619 max_w = MIN(max_w, _maxwidth(child));
1620 if (_hweight(child) > 0)
1622 found_nonzero_hweight = TRUE;
1623 data->col_infos[i].weight += _hweight(child);
1626 if (!found_nonzero_hweight)
1627 max_w = min_w;
1628 else
1629 max_w = MAX(max_w, min_w);
1630 /* D(bug("col %d : min_w=%d max_w=%d\n", i, min_w, max_w)); */
1632 data->col_infos[i].min = min_w;
1633 data->col_infos[i].max = max_w;
1634 data->horiz_weight_sum += data->col_infos[i].weight;
1636 req->MinWidth += min_w;
1637 req->DefWidth += def_w;
1638 req->MaxWidth += max_w;
1639 if (req->MaxWidth > MUI_MAXMAX)
1640 req->MaxWidth = MUI_MAXMAX;
1644 static void
1645 group_minmax_2d(struct IClass *cl, Object *obj,
1646 struct MinList *children, struct MUIP_AskMinMax *msg)
1648 struct MUI_GroupData *data = INST_DATA(cl, obj);
1649 Object *cstate;
1650 Object *child;
1651 struct MUI_MinMax tmp;
1652 WORD maxmin_width;
1653 WORD maxmin_height;
1654 WORD maxdef_width;
1655 WORD maxdef_height;
1657 if (!data->columns)
1659 if (data->num_children % data->rows)
1661 data->columns = 1;
1662 data->rows = data->num_children;
1664 else
1665 data->columns = data->num_children / data->rows;
1667 else
1669 if (data->num_children % data->columns)
1671 data->rows = 1;
1672 data->columns = data->num_children;
1674 else
1675 data->rows = data->num_children / data->columns;
1678 if (data->columns < 1)
1679 data->columns = 1;
1680 if (data->rows < 1)
1681 data->rows = 1;
1683 if (data->row_infos != NULL)
1684 mui_free(data->row_infos);
1686 data->row_infos = mui_alloc(data->rows * sizeof(struct layout2d_elem));
1687 if (NULL == data->row_infos)
1688 return;
1690 if (data->col_infos != NULL)
1691 mui_free(data->col_infos);
1693 data->col_infos =
1694 mui_alloc(data->columns * sizeof(struct layout2d_elem));
1695 if (NULL == data->col_infos)
1696 return;
1698 data->horiz_weight_sum = 0;
1699 data->vert_weight_sum = 0;
1701 tmp.MinHeight = tmp.DefHeight = tmp.MaxHeight =
1702 (data->rows - 1) * data->vert_spacing;
1703 tmp.MinWidth = tmp.DefWidth = tmp.MaxWidth =
1704 (data->columns - 1) * data->horiz_spacing;
1705 /* get minimum dims if same dims for all children are needed */
1706 maxmin_width = 0;
1707 maxmin_height = 0;
1708 maxdef_width = 0;
1709 maxdef_height = 0;
1711 if ((data->flags & GROUP_SAME_WIDTH)
1712 || (data->flags & GROUP_SAME_HEIGHT))
1714 cstate = (Object *) children->mlh_Head;
1715 while ((child = NextObject(&cstate)))
1717 if (!(_flags(child) & MADF_SHOWME))
1718 continue;
1719 maxmin_width = MAX(maxmin_width, _minwidth(child));
1720 maxmin_height = MAX(maxmin_height, _minheight(child));
1721 maxdef_width = MAX(maxdef_width, w0_defwidth(child));
1722 maxdef_height = MAX(maxdef_height, w0_defheight(child));
1724 /* g_print("2d group: mminw=%d mminh=%d\n", */
1725 /* maxmin_width, maxmin_height); */
1727 if (data->flags & GROUP_SAME_HEIGHT)
1728 data->samesize_maxmin_vert = maxmin_height;
1729 else
1730 data->samesize_maxmin_vert = 0;
1732 if (data->flags & GROUP_SAME_WIDTH)
1733 data->samesize_maxmin_horiz = maxmin_width;
1734 else
1735 data->samesize_maxmin_horiz = 0;
1737 minmax_2d_rows_pass(data, children, &tmp, maxmin_height, maxdef_height);
1738 minmax_2d_columns_pass(data, children, &tmp, maxmin_width,
1739 maxdef_width);
1741 END_MINMAX();
1745 static void
1746 group_minmax_pagemode(struct IClass *cl, Object *obj,
1747 struct MinList *children, struct MUIP_AskMinMax *msg)
1749 Object *cstate;
1750 Object *child;
1751 struct MUI_GroupData *data = INST_DATA(cl, obj);
1752 struct MUI_MinMax tmp = { 0, 0, MUI_MAXMAX, MUI_MAXMAX, 0, 0 };
1754 cstate = (Object *) children->mlh_Head;
1756 D(bug("minmax_pagemode(%lx)\n", obj, tmp.DefWidth));
1758 while ((child = NextObject(&cstate)))
1760 if (!(_flags(child) & MADF_SHOWME))
1761 continue;
1763 if (child == data->titlegroup)
1764 continue;
1766 tmp.MinHeight = MAX(tmp.MinHeight, _minheight(child));
1767 D(bug("minmax_pagemode(%p) minh child = %d tmpmin=%d\n", obj,
1768 _minheight(child), tmp.MinHeight));
1769 tmp.MinWidth = MAX(tmp.MinWidth, _minwidth(child));
1770 tmp.MaxHeight = MIN(tmp.MaxHeight, w0_maxheight(child));
1771 tmp.MaxWidth = MIN(tmp.MaxWidth, w0_maxwidth(child));
1772 tmp.DefHeight = MAX(tmp.DefHeight,
1773 ((w0_defheight(child) <
1774 MUI_MAXMAX) ? w0_defheight(child) : tmp.DefHeight));
1775 tmp.DefWidth =
1776 MAX(tmp.DefWidth,
1777 ((w0_defwidth(child) <
1778 MUI_MAXMAX) ? w0_defwidth(child) : tmp.DefWidth));
1779 D(bug("minmax_pagemode(%lx) defw = %ld\n", obj, tmp.DefWidth));
1782 if (data->titlegroup)
1784 tmp.MinHeight += _minheight(data->titlegroup);
1785 tmp.MaxHeight += w0_maxheight(data->titlegroup);
1786 tmp.DefHeight += w0_defheight(data->titlegroup);
1789 END_MINMAX();
1792 /**************************************************************************
1793 MUIM_AskMinMax : ask children about min/max sizes, then
1794 either call a hook, or the builtin method, to calculate our minmax
1795 **************************************************************************/
1796 IPTR Group__MUIM_AskMinMax(struct IClass *cl, Object *obj,
1797 struct MUIP_AskMinMax *msg)
1799 struct MUI_GroupData *data = INST_DATA(cl, obj);
1800 struct MUI_LayoutMsg lm;
1801 struct MUIP_AskMinMax childMsg;
1802 struct MUI_MinMax childMinMax;
1803 Object *cstate;
1804 Object *child;
1805 LONG super_minwidth, super_minheight;
1808 * let our superclass first fill in its size with frame, inner spc etc ...
1810 DoSuperMethodA(cl, obj, (Msg) msg);
1811 super_minwidth = msg->MinMaxInfo->MinWidth;
1812 super_minheight = msg->MinMaxInfo->MinHeight;
1815 * Ask children
1817 childMsg.MethodID = msg->MethodID;
1818 childMsg.MinMaxInfo = &childMinMax;
1819 get(data->family, MUIA_Family_List, &(lm.lm_Children));
1821 cstate = (Object *) lm.lm_Children->mlh_Head;
1823 while ((child = NextObject(&cstate)))
1825 if (!(_flags(child) & MADF_SHOWME))
1826 /* BORDERGADGETs should handle this itself */
1827 continue;
1828 /* Ask child */
1829 DoMethodA(child, (Msg) & childMsg);
1830 /* D(bug("*** group %lx, child %lx min=%ld,%ld\n", */
1831 /* obj, child, childMinMax.MinWidth, childMinMax.MinHeight)); */
1832 __area_finish_minmax(child, childMsg.MinMaxInfo);
1836 * Use children infos to calculate group size
1838 if (data->flags & GROUP_PAGEMODE)
1840 D(bug("minmax_pagemode(%p) minh initial = %d\n", obj,
1841 msg->MinMaxInfo->MinHeight));
1842 group_minmax_pagemode(cl, obj, lm.lm_Children, msg);
1843 D(bug("minmax_pagemode(%p) minh = %d\n", obj,
1844 msg->MinMaxInfo->MinHeight));
1846 else if (data->layout_hook)
1848 lm.lm_Type = MUILM_MINMAX;
1849 CallHookPkt(data->layout_hook, obj, &lm);
1851 if (lm.lm_MinMax.MaxHeight < lm.lm_MinMax.MinHeight)
1852 lm.lm_MinMax.MaxHeight = lm.lm_MinMax.MinHeight;
1853 if (lm.lm_MinMax.DefHeight < lm.lm_MinMax.MinHeight)
1854 lm.lm_MinMax.DefHeight = lm.lm_MinMax.MinHeight;
1855 if (lm.lm_MinMax.MaxWidth < lm.lm_MinMax.MinWidth)
1856 lm.lm_MinMax.MaxWidth = lm.lm_MinMax.MinWidth;
1857 if (lm.lm_MinMax.DefWidth < lm.lm_MinMax.MinWidth)
1858 lm.lm_MinMax.DefWidth = lm.lm_MinMax.MinWidth;
1860 //kprintf("### min %d x %d def %d x %d max %d x %d\n",
1861 // msg->MinMaxInfo->MinWidth,
1862 // msg->MinMaxInfo->MinHeight,
1863 // msg->MinMaxInfo->DefWidth,
1864 // msg->MinMaxInfo->DefHeight,
1865 // msg->MinMaxInfo->MaxWidth,
1866 // msg->MinMaxInfo->MaxHeight);
1868 msg->MinMaxInfo->MinWidth += lm.lm_MinMax.MinWidth;
1869 msg->MinMaxInfo->MinHeight += lm.lm_MinMax.MinHeight;
1870 msg->MinMaxInfo->MaxWidth += lm.lm_MinMax.MaxWidth;
1871 if (msg->MinMaxInfo->MaxWidth > MUI_MAXMAX)
1872 msg->MinMaxInfo->MaxWidth = MUI_MAXMAX;
1873 msg->MinMaxInfo->MaxHeight += lm.lm_MinMax.MaxHeight;
1874 if (msg->MinMaxInfo->MaxHeight > MUI_MAXMAX)
1875 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1876 msg->MinMaxInfo->DefWidth += lm.lm_MinMax.DefWidth;
1877 msg->MinMaxInfo->DefHeight += lm.lm_MinMax.DefHeight;
1879 //kprintf("#### min %d x %d def %d x %d max %d x %d\n",
1880 // msg->MinMaxInfo->MinWidth,
1881 // msg->MinMaxInfo->MinHeight,
1882 // msg->MinMaxInfo->DefWidth,
1883 // msg->MinMaxInfo->DefHeight,
1884 // msg->MinMaxInfo->MaxWidth,
1885 // msg->MinMaxInfo->MaxHeight);
1888 else
1890 if ((data->rows == 1) && (data->columns == 1))
1892 data->num_visible_children =
1893 Group_GetNumVisibleChildren(data, lm.lm_Children);
1894 if (data->flags & GROUP_HORIZ)
1895 group_minmax_horiz(cl, obj, lm.lm_Children, msg);
1896 else
1897 group_minmax_vert(cl, obj, lm.lm_Children, msg);
1899 else
1901 group_minmax_2d(cl, obj, lm.lm_Children, msg);
1905 if (data->flags & GROUP_VIRTUAL)
1907 data->saved_minwidth = msg->MinMaxInfo->MinWidth;
1908 data->saved_minheight = msg->MinMaxInfo->MinHeight;
1909 msg->MinMaxInfo->MinWidth = super_minwidth + 2;
1910 msg->MinMaxInfo->MinHeight = super_minheight + 2;
1912 //kprintf("## min %d x %d def %d x %d max %d x %d\n",
1913 // msg->MinMaxInfo->MinWidth,
1914 // msg->MinMaxInfo->MinHeight,
1915 // msg->MinMaxInfo->DefWidth,
1916 // msg->MinMaxInfo->DefHeight,
1917 // msg->MinMaxInfo->MaxWidth,
1918 // msg->MinMaxInfo->MaxHeight);
1922 return 0;
1927 // enforce minmax constraint, but also update total growable/shrinkable weights
1928 // while we're at it
1929 static void Layout1D_minmax_constraint(WORD *sizep, WORD minsize,
1930 WORD maxsize, WORD *remainp, WORD *sizegrowp, WORD *sizeshrinkp,
1931 ULONG *weightgrowp, ULONG *weightshrinkp, UWORD weight, WORD samesize)
1933 WORD size = *sizep, remain = *remainp,
1934 sizegrow = *sizegrowp, sizeshrink = *sizeshrinkp;
1935 ULONG weightgrow = *weightgrowp, weightshrink = *weightshrinkp;
1937 /* D(bug("L1D_minmax_c size=%d min=%d max=%d w=%d ss=%d\n", */
1938 /* size, minsize, maxsize, */
1939 /* weight, samesize)); */
1941 if ((samesize > 0) && (weight == 0))
1943 remain += size - samesize;
1944 size = samesize;
1945 sizeshrink -= size;
1946 sizegrow -= size;
1949 if (size <= minsize) // too little
1951 remain += size - minsize;
1952 size = minsize;
1953 sizeshrink -= size;
1954 if (size == maxsize)
1955 sizegrow -= size;
1957 else if (size >= maxsize) // too big
1959 remain += size - maxsize;
1960 size = maxsize;
1961 sizegrow -= size;
1964 if (!((samesize > 0) && (weight == 0)))
1966 if (size < maxsize)
1967 weightgrow += weight;
1968 if (size > minsize)
1969 weightshrink += weight;
1972 *sizep = size;
1973 *remainp = remain;
1974 *sizegrowp = sizegrow;
1975 *sizeshrinkp = sizeshrink;
1976 *weightgrowp = weightgrow;
1977 *weightshrinkp = weightshrink;
1981 // redistribute excess size to growable child, or reduce size of a shrinkable
1982 // child
1983 static void Layout1D_redistribution(WORD *sizep, WORD minsize,
1984 WORD maxsize, WORD *remainp, WORD *sizegrowp, WORD *sizeshrinkp,
1985 ULONG *weightgrowp, ULONG *weightshrinkp, UWORD weight)
1987 WORD size = *sizep, remain = *remainp,
1988 sizegrow = *sizegrowp, sizeshrink = *sizeshrinkp;
1989 ULONG weightgrow = *weightgrowp, weightshrink = *weightshrinkp;
1991 if (weight == 0)
1992 return;
1994 if ((remain > 0) && (size < maxsize))
1996 LONG newsize;
1998 newsize = (sizegrow * weight + weightgrow / 2) / weightgrow;
2000 /* D(bug("newsize=%ld == size_growa=%ld * w=%ld / weight_grow=%d\n", */
2001 /* newsize, sizegrow, weight, weightgrow)); */
2003 /* take care of off-by-1 errors that may toggle remainder sign
2004 * by ensuring convergence to 0
2006 if (remain - newsize + size < 0)
2008 /* D(bug("adding remainder=%d => size = %d\n", */
2009 /* remain, size + remain)); */
2010 size += remain;
2011 remain = 0;
2013 else
2015 remain -= newsize - size;
2016 size = newsize;
2017 sizegrow -= size;
2018 weightgrow -= weight;
2021 else if ((remain < 0) && (size > minsize))
2023 LONG newsize;
2025 newsize = (sizeshrink * weight + weightshrink / 2) / weightshrink;
2027 /* D(bug("newsize=%ld == size_shrinkables=%ld * w=%ld " */
2028 /* "/ weight_shrinkables=%d\n", */
2029 /* newsize, sizeshrink, weight, weightshrink)); */
2031 if (remain - newsize + size > 0)
2033 /* D(bug("adding remainder=%d => size = %d\n", */
2034 /* remain, size + remain)); */
2035 size += remain;
2036 remain = 0;
2038 else
2040 remain -= newsize - size;
2041 size = newsize;
2042 sizeshrink -= size;
2043 weightshrink -= weight;
2047 *sizep = size;
2048 *remainp = remain;
2049 *sizegrowp = sizegrow;
2050 *sizeshrinkp = sizeshrink;
2051 *weightgrowp = weightgrow;
2052 *weightshrinkp = weightshrink;
2056 // 2 passes at most, less on average (0.5 or 1.5), each does
2057 // - a minmax clamping, evenutally adding to a remainder
2058 // (remainder = missing (underflow) or remaining (overflow) space compared
2059 // to ideal sizes where children fill the whole group)
2060 // - a redistribution of the remainder, by growing (pos. remainder) or
2061 // shrinking (neg. remainder) children able to support it.
2063 // Occasionnaly the first time the redistribution is done, the minmax
2064 // constraint can be broken, thus the extra pass to check and eventually
2065 // redistribute. The second redistribution never breaks minmax constraint
2066 // (there should be a mathematical proof, but feel free to prove me wrong
2067 // with an example)
2068 static void Layout1D_minmax_constraints_and_redistrib(struct MinList
2069 *children, WORD total_size, WORD remainder, WORD samesize,
2070 BOOL group_horiz)
2072 Object *cstate;
2073 Object *child;
2074 int i;
2076 for (i = 0; i < 2; i++)
2078 WORD size_growables = total_size;
2079 WORD size_shrinkables = total_size;
2080 ULONG weight_growables = 0;
2081 ULONG weight_shrinkables = 0;
2083 /* D(bug("start : rem=%ld, A=%ld, size_growables=%ld, " */
2084 /* "size_shrinkables=%ld\n", */
2085 /* remainder, total_size, size_growables, size_shrinkables)); */
2087 // minmax constraints
2088 cstate = (Object *) children->mlh_Head;
2089 while ((child = NextObject(&cstate)))
2091 /* WORD old_size; */
2093 if (IS_HIDDEN(child))
2094 continue;
2096 if (group_horiz)
2098 /* old_size = _width(child); */
2100 Layout1D_minmax_constraint(&_width(child), _minwidth(child),
2101 _maxwidth(child), &remainder, &size_growables,
2102 &size_shrinkables, &weight_growables,
2103 &weight_shrinkables, _hweight(child), samesize);
2105 /* D(bug("loop1 on %p : width=%d was %d, rem=%d, A=%d, " */
2106 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2107 /* child, _width(child), old_size, remainder, total_size, */
2108 /* size_growables, size_shrinkables, _hweight(child), */
2109 /* _minwidth(child), _maxwidth(child))); */
2111 else // ! group_horiz
2113 /* old_size = _height(child); */
2115 Layout1D_minmax_constraint(&_height(child),
2116 _minheight(child), _maxheight(child), &remainder,
2117 &size_growables, &size_shrinkables, &weight_growables,
2118 &weight_shrinkables, _vweight(child), samesize);
2120 /* D(bug("loop1 on %p : h=%ld was %d, rem=%d, A=%d, " */
2121 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2122 /* child, _height(child), old_size, remainder, total_size,*/
2123 /* size_growables, size_shrinkables, _vweight(child), */
2124 /* _minheight(child), _maxheight(child))); */
2125 } // if (group_horiz)
2126 } // while child, minmax constraints
2128 // mid-pass break
2129 if (remainder == 0)
2130 break;
2132 /* D(bug("mid : rem=%d, A=%d, size_grow=%d, size_shrink=%d, " */
2133 /* "wg=%ld, ws=%ld\n", remainder, total_size, size_growables, */
2134 /* size_shrinkables, weight_growables, weight_shrinkables)); */
2136 // distribute remaining space to possible candidates
2137 cstate = (Object *) children->mlh_Head;
2138 while (((child = NextObject(&cstate)) != NULL) && (remainder != 0))
2140 /* WORD old_size; */
2142 if (IS_HIDDEN(child))
2143 continue;
2145 if (group_horiz)
2147 /* old_size = _width(child); */
2149 Layout1D_redistribution(&_width(child), _minwidth(child),
2150 _maxwidth(child), &remainder, &size_growables,
2151 &size_shrinkables, &weight_growables,
2152 &weight_shrinkables, _hweight(child));
2154 /* D(bug("loop2 on %p : h=%d was %d, rem=%d, A=%d, " */
2155 /* "size_grow=%d, size_shrink=%d\n", child, */
2156 /* _width(child), old_size, remainder, total_size, */
2157 /* size_growables, size_shrinkables)); */
2159 else // ! group_horiz
2161 /* old_size = _height(child); */
2163 Layout1D_redistribution(&_height(child), _minheight(child),
2164 _maxheight(child), &remainder, &size_growables,
2165 &size_shrinkables, &weight_growables,
2166 &weight_shrinkables, _vweight(child));
2168 /* D(bug("loop2 on %p : h=%d was %d, rem=%d, A=%d, " */
2169 /* "size_grow=%d, size_shrink=%d\n", child, */
2170 /* _height(child), old_size, remainder, total_size, */
2171 /* size_growables, size_shrinkables)); */
2172 } // if (group_horiz)
2174 } // while child, redistribution
2176 /* if (remainder != 0) */
2177 /* { */
2178 /* D(bug("end : rem=%ld, A=%ld, size_grow=%ld, size_shrink=%ld\n", */
2179 /* remainder, total_size, size_growables, size_shrinkables)); */
2180 /* } */
2181 // dont break here if remainder == 0, some minmax constraints
2182 // may not be respected
2183 } // end for
2185 // to easily spot layout bugs, nothing like a (division by zero) exception
2186 /* if (remainder != 0) */
2187 /* { */
2188 /* ASSERT(remainder != 0); */
2189 /* D(bug("gonna crash, remainder = %d\n", remainder)); */
2190 /* remainder /= 0; */
2191 /* } */
2195 static void Layout1D_weight_constraint(WORD *total_sizep,
2196 WORD *total_init_sizep,
2197 ULONG *total_weightp, WORD *sizep, UWORD weight, WORD minsize)
2199 if (*total_weightp > 0)
2200 *sizep = (*total_sizep * weight + *total_weightp / 2)
2201 / *total_weightp;
2202 else
2203 *sizep = minsize;
2205 *total_weightp -= weight;
2206 *total_sizep -= *sizep;
2207 *total_init_sizep += *sizep;
2211 static void group_layout_vert(struct IClass *cl, Object *obj,
2212 struct MinList *children)
2214 struct MUI_GroupData *data = INST_DATA(cl, obj);
2215 Object *cstate;
2216 Object *child;
2217 ULONG total_weight;
2218 WORD remainder; /* must converge to 0 to successfully end layout */
2219 WORD total_size;
2220 WORD total_size_backup;
2221 WORD total_init_size; /* total size of the ideally sized children */
2222 WORD width;
2223 WORD left = 0;
2224 WORD top = 0;
2225 WORD layout_width;
2226 WORD layout_height;
2228 //kprintf("group_layout_vert: virtoff = %d,%d\n",
2229 // data->virt_offx, data->virt_offy);
2231 if (data->flags & GROUP_VIRTUAL)
2233 data->virt_mwidth =
2234 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2235 _maxwidth(obj) - _subwidth(obj));
2236 data->virt_mheight =
2237 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2238 _maxheight(obj) - _subheight(obj));
2240 layout_width = data->virt_mwidth;
2241 layout_height = data->virt_mheight;
2243 else
2245 layout_width = _mwidth(obj);
2246 layout_height = _mheight(obj);
2249 total_weight = data->vert_weight_sum;
2250 total_init_size = 0;
2251 total_size =
2252 layout_height - (data->num_visible_children -
2253 1) * data->vert_spacing;
2254 total_size_backup = total_size;
2256 /* D(bug("\nvert layout for %p, A=%d W=%ld\n", */
2257 /* obj, total_size, total_weight)); */
2259 // weight constraints
2260 // calculate ideal size for each object, and total ideal size
2261 cstate = (Object *) children->mlh_Head;
2262 while ((child = NextObject(&cstate)))
2264 if (IS_HIDDEN(child))
2265 continue;
2267 Layout1D_weight_constraint(&total_size, &total_init_size,
2268 &total_weight, &_height(child), _vweight(child),
2269 _minheight(child));
2270 /* D(bug("child %p : ideal=%d w=%ld\n", */
2271 /* child, _height(child), _vweight(child))); */
2272 } // while child, weight constraints
2274 total_size = total_size_backup;
2275 remainder = total_size - total_init_size;
2277 if (data->flags & GROUP_VIRTUAL)
2279 /* This is also true for non virtual groups, but if this would be the
2280 ** case then there is a bug in the layout function
2282 if (total_size < 0)
2283 total_size = 0;
2286 Layout1D_minmax_constraints_and_redistrib(children,
2287 total_size,
2288 remainder,
2289 (data->flags & GROUP_SAME_HEIGHT) ? data->samesize_maxmin_vert : 0,
2290 FALSE);
2292 // do the layout
2293 cstate = (Object *) children->mlh_Head;
2294 while ((child = NextObject(&cstate)))
2296 if (IS_HIDDEN(child))
2297 continue;
2299 width = MIN(_maxwidth(child), layout_width);
2300 width = MAX(width, _minwidth(child));
2301 left = (layout_width - width) / 2;
2303 /* D(bug("child %p -> layout %d x %d\n", */
2304 /* child, width, _height(child))); */
2305 if (!MUI_Layout(child, left, top, width, _height(child), 0))
2306 return;
2307 top += data->vert_spacing + _height(child);
2313 static void group_layout_horiz(struct IClass *cl, Object *obj,
2314 struct MinList *children)
2316 struct MUI_GroupData *data = INST_DATA(cl, obj);
2317 Object *cstate;
2318 Object *child;
2319 ULONG total_weight;
2320 WORD remainder; /* must converge to 0 to succesfully end layout */
2321 WORD total_size;
2322 WORD total_size_backup;
2323 WORD total_init_size; /* total size of the ideally sized children */
2324 WORD height;
2325 WORD top = 0;
2326 WORD left = 0;
2327 WORD layout_width;
2328 WORD layout_height;
2330 //kprintf("group_layout_horiz: virtoff = %d,%d\n",
2331 // data->virt_offx, data->virt_offy);
2333 if (data->flags & GROUP_VIRTUAL)
2335 data->virt_mwidth =
2336 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2337 _maxwidth(obj) - _subwidth(obj));
2338 data->virt_mheight =
2339 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2340 _maxheight(obj) - _subheight(obj));
2342 layout_width = data->virt_mwidth;
2343 layout_height = data->virt_mheight;
2345 //kprintf("group_layout_horiz: layoutsize %d x %d "
2346 // " virtsize %d x %d msize %d x %d\n",
2347 // layout_width, layout_height, data->virt_mwidth,
2348 // data->virt_mheight,
2349 // _mwidth(obj), _mheight(obj));
2351 else
2353 layout_width = _mwidth(obj);
2354 layout_height = _mheight(obj);
2357 total_weight = data->horiz_weight_sum;
2358 total_init_size = 0;
2359 total_size =
2360 layout_width - (data->num_visible_children -
2361 1) * data->horiz_spacing;
2362 total_size_backup = total_size;
2364 /* D(bug("\nhoriz layout for %p, A=%d W=%ld\n", */
2365 /* obj, total_size, total_weight)); */
2367 // weight constraints
2368 // calculate ideal size for each object, and total ideal size
2369 cstate = (Object *) children->mlh_Head;
2370 while ((child = NextObject(&cstate)))
2372 if (IS_HIDDEN(child))
2373 continue;
2375 Layout1D_weight_constraint(&total_size, &total_init_size,
2376 &total_weight, &_width(child), _hweight(child),
2377 _minwidth(child));
2378 /* D(bug("child %p : ideal=%d w=%ld\n", */
2379 /* child, _width(child), _hweight(child))); */
2380 } // while child, weight constraints
2382 total_size = total_size_backup;
2383 if (data->horiz_weight_sum > 0)
2384 remainder = total_size - total_init_size;
2385 else
2386 remainder = 0;
2388 if (data->flags & GROUP_VIRTUAL)
2390 /* This is also true for non virtual groups, but if this would be the
2391 ** case then there is a bug in the layout function
2393 if (total_size < 0)
2394 total_size = 0;
2397 Layout1D_minmax_constraints_and_redistrib(children,
2398 total_size,
2399 remainder,
2400 (data->flags & GROUP_SAME_WIDTH) ? data->samesize_maxmin_horiz : 0,
2401 TRUE);
2403 // do the layout
2404 cstate = (Object *) children->mlh_Head;
2405 while ((child = NextObject(&cstate)))
2407 if (IS_HIDDEN(child))
2408 continue;
2410 height = MIN(_maxheight(child), layout_height);
2411 height = MAX(height, _minheight(child));
2412 top = (layout_height - height) / 2;
2414 /* D(bug("child %p -> layout %d x %d\n", */
2415 /* child, _width(child), height)); */
2416 if (!MUI_Layout(child, left, top, _width(child), height, 0))
2417 return;
2418 left += data->horiz_spacing + _width(child);
2424 static void Layout2D_weight_constraint(struct MUI_GroupData *data,
2425 struct layout2d_elem *row_infos,
2426 struct layout2d_elem *col_infos,
2427 WORD total_size_height, WORD total_size_width,
2428 WORD *total_init_height, WORD *total_init_width)
2430 int i;
2431 ULONG total_weight_vert = data->vert_weight_sum;
2432 ULONG total_weight_horiz = data->horiz_weight_sum;
2434 *total_init_height = 0;
2435 *total_init_width = 0;
2437 /* calc row heights */
2438 for (i = 0; i < data->rows; i++)
2440 if (total_weight_vert > 0)
2441 row_infos[i].dim =
2442 (total_size_height * row_infos[i].weight +
2443 total_weight_vert / 2) / total_weight_vert;
2444 else
2445 row_infos[i].dim = row_infos[i].min;
2447 /* D(bug("l2 row %d : ideal = %d with w=%d, A=%d, W=%d\n", */
2448 /* i, row_infos[i].dim, */
2449 /* row_infos[i].weight, total_size_height, total_weight_vert)); */
2451 total_weight_vert -= row_infos[i].weight;
2452 total_size_height -= row_infos[i].dim;
2453 *total_init_height += row_infos[i].dim;
2456 /* calc columns widths */
2457 for (i = 0; i < data->columns; i++)
2459 if (total_weight_horiz)
2460 col_infos[i].dim =
2461 (total_size_width * col_infos[i].weight +
2462 total_weight_horiz / 2) / total_weight_horiz;
2463 else
2464 col_infos[i].dim = col_infos[i].min;
2466 /* D(bug("l2 col %d : ideal = %d with w=%d, A=%d, W=%d\n", */
2467 /* i, col_infos[i].dim, */
2468 /* col_infos[i].weight, total_size_width, total_weight_horiz)); */
2470 total_weight_horiz -= col_infos[i].weight;
2471 total_size_width -= col_infos[i].dim;
2472 *total_init_width += col_infos[i].dim;
2478 static void Layout2D_minmax_constraints_and_redistrib(struct layout2d_elem
2479 *infos, WORD nitems, WORD total_size, WORD samesize, WORD remainder)
2481 int j;
2483 /* D(bug("L2D mc&r n=%d A=%d ss=%d rem=%d\n", */
2484 /* nitems, total_size, samesize, remainder)); */
2486 for (j = 0; j < 2; j++)
2488 WORD size_growables = total_size;
2489 WORD size_shrinkables = total_size;
2490 ULONG weight_growables = 0;
2491 ULONG weight_shrinkables = 0;
2492 /* WORD old_size; */
2493 int i;
2495 // minmax constraints
2496 for (i = 0; i < nitems; i++)
2498 /* old_size = infos[i].dim; */
2500 /* D(bug("bef loop1 on %d : size=%d, rem=%d, A=%d, " */
2501 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2502 /* i, infos[i].dim, remainder, total_size, */
2503 /* size_growables, size_shrinkables, infos[i].weight, */
2504 /* infos[i].min, infos[i].max)); */
2506 Layout1D_minmax_constraint(&infos[i].dim, infos[i].min,
2507 infos[i].max, &remainder, &size_growables,
2508 &size_shrinkables, &weight_growables, &weight_shrinkables,
2509 infos[i].weight, samesize);
2511 /* D(bug("loop1 on %d : size=%d was %d, rem=%d, A=%d, " */
2512 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2513 /* i, infos[i].dim, old_size, remainder, total_size, */
2514 /* size_growables, size_shrinkables, infos[i].weight, */
2515 /* infos[i].min, infos[i].max)); */
2518 if (remainder == 0)
2519 break;
2521 for (i = 0; i < nitems; i++)
2523 /* old_size = infos[i].dim; */
2525 /* D(bug("bef loop2 on %d : size=%d, rem=%d, A=%d, " */
2526 /* "size_grow=%d, size_shrink=%d\n", i, */
2527 /* infos[i].dim, remainder, total_size, */
2528 /* size_growables, size_shrinkables)); */
2530 Layout1D_redistribution(&infos[i].dim, infos[i].min,
2531 infos[i].max, &remainder, &size_growables,
2532 &size_shrinkables, &weight_growables, &weight_shrinkables,
2533 infos[i].weight);
2535 /* D(bug("loop2 on %d : size=%d was %d, rem=%d, A=%d, " */
2536 /* "size_grow=%d, size_shrink=%d\n", i, */
2537 /* infos[i].dim, old_size, remainder, total_size, */
2538 /* size_growables, size_shrinkables)); */
2543 static void
2544 layout_2d_distribute_space(struct MUI_GroupData *data,
2545 struct layout2d_elem *row_infos,
2546 struct layout2d_elem *col_infos,
2547 struct MinList *children, LONG left_start, LONG top_start)
2549 Object *cstate;
2550 Object *child;
2551 LONG left;
2552 LONG top;
2553 LONG col_width;
2554 LONG row_height;
2555 int i, j;
2558 * pass 2 : distribute space
2560 cstate = (Object *) children->mlh_Head;
2561 top = top_start;
2562 /* for each row */
2563 for (i = 0; i < data->rows; i++)
2565 /* left start for child layout in this row */
2566 left = left_start;
2568 /* max height for children in this row */
2569 row_height = row_infos[i].dim;
2570 j = 0;
2571 /* for each column */
2572 while ((child = NextObject(&cstate)))
2574 LONG cleft;
2575 LONG ctop;
2576 LONG cwidth;
2577 LONG cheight;
2579 if (IS_HIDDEN(child))
2580 continue;
2581 /* max width for children in this column */
2582 col_width = col_infos[j].dim;
2584 /* center child if col width is bigger than child maxwidth */
2585 cwidth = MIN(_maxwidth(child), col_width);
2586 cwidth = MAX(cwidth, _minwidth(child));
2587 cleft = left + (col_width - cwidth) / 2;
2589 /* center child if row height is bigger than child maxheight */
2590 cheight = MIN(_maxheight(child), row_height);
2591 cheight = MAX(cheight, _minheight(child));
2592 ctop = top + (row_height - cheight) / 2;
2594 /* g_print("layout %d %d %d %d\n", cleft, ctop, cwidth, cheight); */
2595 /* D(bug("2DL/child %p -> layout %d x %d\n", */
2596 /* child, cwidth, cheight)); */
2597 if (!MUI_Layout(child, cleft, ctop, cwidth, cheight, 0))
2598 return;
2600 left += data->horiz_spacing + col_width;
2602 ++j;
2603 if ((j % data->columns) == 0)
2604 break;
2607 top += data->vert_spacing + row_height;
2614 * all children in the same row have the same maximum height
2615 * all children in the same column have the same maximum height
2616 * if a child maximum size is smaller than the biggest minimum size,
2617 * the chid will be centered in the remaining space.
2619 * for each row, determine its height allocation
2620 * weight ? the vertical weight of a row, if no fixed-height child
2621 * in the row, is the sum of all vertical weights of children
2622 * all row members will have the same height
2624 * for each column, determine its width allocation
2625 * all column members will have the same width
2627 /* Write a proper hook function */
2628 static void
2629 group_layout_2d(struct IClass *cl, Object *obj, struct MinList *children)
2631 struct MUI_GroupData *data = INST_DATA(cl, obj);
2632 WORD left_start = 0;
2633 WORD top_start = 0;
2634 WORD total_size_height =
2635 _mheight(obj) - (data->rows - 1) * data->vert_spacing;
2636 WORD total_size_width =
2637 _mwidth(obj) - (data->columns - 1) * data->horiz_spacing;
2638 WORD total_init_height;
2639 WORD total_init_width;
2640 WORD remainder_height;
2641 WORD remainder_width;
2642 WORD layout_width;
2643 WORD layout_height;
2645 if (data->rows == 0 || data->columns == 0)
2646 return;
2647 if (data->num_children % data->rows
2648 || data->num_children % data->columns)
2649 return;
2650 if (data->row_infos == NULL || data->col_infos == NULL)
2651 return;
2653 //kprintf("group_layout_horiz: virtoff = %d,%d\n",
2654 // data->virt_offx, data->virt_offy);
2656 if (data->flags & GROUP_VIRTUAL)
2658 data->virt_mwidth =
2659 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2660 _maxwidth(obj) - _subwidth(obj));
2661 data->virt_mheight =
2662 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2663 _maxheight(obj) - _subheight(obj));
2665 layout_width = data->virt_mwidth;
2666 layout_height = data->virt_mheight;
2668 else
2670 layout_width = _mwidth(obj);
2671 layout_height = _mheight(obj);
2674 total_size_height =
2675 layout_height - (data->rows - 1) * data->vert_spacing;
2676 total_size_width =
2677 layout_width - (data->columns - 1) * data->horiz_spacing;
2679 // fix left/top ?
2681 // weight constraints
2682 Layout2D_weight_constraint(data, data->row_infos, data->col_infos,
2683 total_size_height, total_size_width,
2684 &total_init_height, &total_init_width);
2686 remainder_height = total_size_height - total_init_height;
2687 remainder_width = total_size_width - total_init_width;
2689 Layout2D_minmax_constraints_and_redistrib(data->row_infos,
2690 data->rows, total_size_height,
2691 /* (data->flags & GROUP_SAME_HEIGHT) ? data->samesize_maxmin_vert : 0, */
2692 0, remainder_height);
2694 Layout2D_minmax_constraints_and_redistrib(data->col_infos,
2695 data->columns, total_size_width,
2696 /* (data->flags & GROUP_SAME_WIDTH) ? data->samesize_maxmin_horiz : 0, */
2697 0, remainder_width);
2699 layout_2d_distribute_space(data, data->row_infos, data->col_infos,
2700 children, left_start, top_start);
2704 /* Write a proper hook function */
2705 static void group_layout_pagemode(struct IClass *cl, Object *obj,
2706 struct MinList *children)
2708 struct MUI_GroupData *data = INST_DATA(cl, obj);
2709 Object *cstate;
2710 Object *child;
2711 WORD layout_width;
2712 WORD layout_height;
2713 int w, h, yoffset = 0;
2715 if (data->flags & GROUP_VIRTUAL)
2717 data->virt_mwidth =
2718 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2719 _maxwidth(obj) - _subwidth(obj));
2720 data->virt_mheight =
2721 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2722 _maxheight(obj) - _subheight(obj));
2724 layout_width = data->virt_mwidth;
2725 layout_height = data->virt_mheight;
2727 else
2729 layout_width = _mwidth(obj);
2730 layout_height = _mheight(obj);
2733 if (data->titlegroup)
2735 yoffset = _minheight(data->titlegroup);
2736 layout_height -= yoffset;
2739 cstate = (Object *) children->mlh_Head;
2740 while ((child = NextObject(&cstate)))
2742 w = MIN(layout_width, _maxwidth(child));
2743 h = MIN(layout_height, _maxheight(child));
2745 if (child == data->titlegroup)
2747 MUI_Layout(child, (layout_width - w) / 2, 0, w, yoffset, 0);
2749 else
2751 D(bug("PM/child %p -> layout %d x %d\n", child, w, h));
2752 MUI_Layout(child, (layout_width - w) / 2,
2753 yoffset + (layout_height - h) / 2, w, h, 0);
2759 /**************************************************************************
2760 MUIM_Layout
2761 Either use a given layout hook, or the builtin method.
2762 **************************************************************************/
2763 IPTR Group__MUIM_Layout(struct IClass *cl, Object *obj,
2764 struct MUIP_Layout *msg)
2766 struct MUI_GroupData *data = INST_DATA(cl, obj);
2767 struct MUI_LayoutMsg lm = { 0 };
2769 get(data->family, MUIA_Family_List, &(lm.lm_Children));
2770 if (data->flags & GROUP_PAGEMODE)
2772 group_layout_pagemode(cl, obj, lm.lm_Children);
2774 else if (data->layout_hook)
2776 lm.lm_Type = MUILM_LAYOUT;
2777 lm.lm_Layout.Width = _mwidth(obj);
2778 lm.lm_Layout.Height = _mheight(obj);
2780 CallHookPkt(data->layout_hook, obj, &lm);
2782 if (data->flags & GROUP_VIRTUAL)
2784 data->virt_mwidth = lm.lm_Layout.Width;
2785 data->virt_mheight = lm.lm_Layout.Height;
2788 else
2790 if ((data->rows == 1) && (data->columns == 1))
2792 if (data->flags & GROUP_HORIZ)
2793 group_layout_horiz(cl, obj, lm.lm_Children);
2794 else
2795 group_layout_vert(cl, obj, lm.lm_Children);
2797 else
2798 group_layout_2d(cl, obj, lm.lm_Children);
2801 if (data->flags & GROUP_VIRTUAL)
2803 WORD new_virt_offx, new_virt_offy;
2805 new_virt_offx = data->virt_offx;
2806 new_virt_offy = data->virt_offy;
2808 if (new_virt_offx + _mwidth(obj) > data->virt_mwidth)
2810 new_virt_offx = data->virt_mwidth - _mwidth(obj);
2812 if (new_virt_offx < 0)
2813 new_virt_offx = 0;
2815 if (new_virt_offy + _mheight(obj) > data->virt_mheight)
2817 new_virt_offy = data->virt_mheight - _mheight(obj);
2819 if (new_virt_offy < 0)
2820 new_virt_offy = 0;
2822 if (new_virt_offx != data->virt_offx)
2824 nfset(obj, MUIA_Virtgroup_Left, new_virt_offx);
2827 if (new_virt_offy != data->virt_offy)
2829 nfset(obj, MUIA_Virtgroup_Top, new_virt_offy);
2834 return 0;
2837 /**************************************************************************
2838 MUIM_Show
2839 **************************************************************************/
2840 IPTR Group__MUIM_Show(struct IClass *cl, Object *obj,
2841 struct MUIP_Show *msg)
2843 struct MUI_GroupData *data = INST_DATA(cl, obj);
2844 Object *cstate;
2845 Object *child;
2846 struct MinList *ChildList = NULL;
2848 /* If msg is NULL, we won't want that the super method actually gets
2849 * this call */
2850 if (msg)
2851 DoSuperMethodA(cl, obj, (Msg) msg);
2853 get(data->family, MUIA_Family_List, &(ChildList));
2854 cstate = (Object *) ChildList->mlh_Head;
2856 if (data->flags & GROUP_PAGEMODE)
2858 int page = 0;
2859 while ((child = NextObject(&cstate)))
2861 if (child == data->titlegroup)
2863 DoShowMethod(child);
2864 continue; /* Title group is not counted as page */
2867 if (page == data->active_page)
2869 DoShowMethod(child);
2870 break;
2872 page++;
2875 else
2877 while ((child = NextObject(&cstate)))
2879 if (!(data->flags & GROUP_VIRTUAL) ||
2880 IsObjectVisible(child, MUIMasterBase))
2882 if (_flags(child) & MADF_SHOWME)
2883 DoShowMethod(child);
2887 return TRUE;
2890 /**************************************************************************
2891 MUIM_Hide
2892 **************************************************************************/
2893 IPTR Group__MUIM_Hide(struct IClass *cl, Object *obj,
2894 struct MUIP_Hide *msg)
2896 struct MUI_GroupData *data = INST_DATA(cl, obj);
2897 Object *cstate;
2898 Object *child;
2899 struct MinList *ChildList = NULL;
2901 get(data->family, MUIA_Family_List, &(ChildList));
2902 cstate = (Object *) ChildList->mlh_Head;
2904 if (data->flags & GROUP_PAGEMODE)
2906 int page = 0;
2907 while ((child = NextObject(&cstate)))
2909 if (child == data->titlegroup)
2911 DoHideMethod(child);
2912 continue; /* Title group is not counted as page */
2915 if (page == data->active_page)
2917 DoHideMethod(child);
2918 break;
2920 page++;
2923 else
2925 while ((child = NextObject(&cstate)))
2927 if (_flags(child) & MADF_CANDRAW)
2928 DoHideMethod(child);
2932 /* If msg is NULL, we won't want that the super method actually gets
2933 * this call */
2934 if (msg)
2935 return DoSuperMethodA(cl, obj, (Msg) msg);
2936 return 1;
2940 * MUIM_FindUData : tests if the MUIA_UserData of the object
2941 * contains the given <udata> and returns the object pointer in this case.
2943 IPTR Group__MUIM_FindUData(struct IClass *cl, Object *obj,
2944 struct MUIP_FindUData *msg)
2946 struct MUI_GroupData *data = INST_DATA(cl, obj);
2948 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2949 return (IPTR) obj;
2951 return DoMethodA(data->family, (Msg) msg);
2956 * MUIM_GetUData : This method tests if the MUIA_UserData of the object
2957 * contains the given <udata> and gets <attr> to <storage> for itself
2958 * in this case.
2960 IPTR Group__MUIM_GetUData(struct IClass *cl, Object *obj,
2961 struct MUIP_GetUData *msg)
2963 struct MUI_GroupData *data = INST_DATA(cl, obj);
2965 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2967 get(obj, msg->attr, msg->storage);
2968 return TRUE;
2971 return DoMethodA(data->family, (Msg) msg);
2976 * MUIM_SetUData : This method tests if the MUIA_UserData of the object
2977 * contains the given <udata> and sets <attr> to <val> for itself in this case.
2979 IPTR Group__MUIM_SetUData(struct IClass *cl, Object *obj,
2980 struct MUIP_SetUData *msg)
2982 struct MUI_GroupData *data = INST_DATA(cl, obj);
2984 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2985 set(obj, msg->attr, msg->val);
2987 DoMethodA(data->family, (Msg) msg);
2988 return TRUE;
2993 * MUIM_SetUDataOnce : This method tests if the MUIA_UserData of the object
2994 * contains the given <udata> and sets <attr> to <val> for itself in this case.
2995 * Stop after the first udata found.
2997 IPTR Group__MUIM_SetUDataOnce(struct IClass *cl, Object *obj,
2998 struct MUIP_SetUData *msg)
3000 struct MUI_GroupData *data = INST_DATA(cl, obj);
3002 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
3004 set(obj, msg->attr, msg->val);
3005 return TRUE;
3007 return DoMethodA(data->family, (Msg) msg);
3010 /**************************************************************************
3011 MUIM_DragQueryExtented
3012 **************************************************************************/
3013 IPTR Group__MUIM_DragQueryExtended(struct IClass *cl, Object *obj,
3014 struct MUIP_DragQueryExtended *msg)
3016 struct MUI_GroupData *data = INST_DATA(cl, obj);
3017 Object *cstate;
3018 Object *child;
3019 Object *found_obj;
3020 struct MinList *ChildList = NULL;
3022 get(data->family, MUIA_Family_List, &(ChildList));
3023 cstate = (Object *) ChildList->mlh_Head;
3024 while ((child = NextObject(&cstate)))
3026 if (!(_flags(child) & MADF_CANDRAW))
3027 continue;
3029 if ((found_obj = (Object *) DoMethodA(child, (Msg) msg)))
3030 return (IPTR) found_obj;
3032 return DoSuperMethodA(cl, obj, (Msg) msg);
3035 /**************************************************************************
3036 MUIM_HandleEvent
3037 **************************************************************************/
3038 IPTR Group__MUIM_HandleEvent(struct IClass *cl, Object *obj,
3039 struct MUIP_HandleEvent *msg)
3041 struct MUI_GroupData *data = INST_DATA(cl, obj);
3043 /* check this, otherwise a superclass who has IDCMP_MOUSEBUTTONS
3044 eventhandler might call DoSuperMethod, and this function gets
3045 called even when he have not added any eventhandler */
3047 if ((data->flags & GROUP_VIRTUAL) && msg->imsg)
3049 switch (msg->imsg->Class)
3051 case IDCMP_MOUSEBUTTONS:
3052 /* For virtual groups */
3053 if (msg->imsg->Code == SELECTDOWN)
3055 if (_between(_mleft(obj), msg->imsg->MouseX, _mright(obj))
3056 && _between(_mtop(obj), msg->imsg->MouseY,
3057 _mbottom(obj)))
3059 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
3060 (IPTR) & data->ehn);
3061 data->ehn.ehn_Events |= IDCMP_INTUITICKS;
3062 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
3063 (IPTR) & data->ehn);
3066 else
3068 if (data->ehn.ehn_Events & IDCMP_INTUITICKS)
3070 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
3071 (IPTR) & data->ehn);
3072 data->ehn.ehn_Events &= ~IDCMP_INTUITICKS;
3073 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
3074 (IPTR) & data->ehn);
3077 break;
3079 case IDCMP_INTUITICKS:
3080 if (!(data->ehn.ehn_Events & IDCMP_INTUITICKS))
3081 break;
3083 if (!(_between(_mleft(obj), msg->imsg->MouseX, _mright(obj))
3084 && _between(_mtop(obj), msg->imsg->MouseY,
3085 _mbottom(obj))))
3087 LONG new_virt_offx = data->virt_offx;
3088 LONG new_virt_offy = data->virt_offy;
3090 if (msg->imsg->MouseX < _mleft(obj))
3092 /* scroll left */
3093 if (new_virt_offx >= 4)
3094 new_virt_offx -= 4;
3095 else
3096 new_virt_offx = 0;
3098 else if (msg->imsg->MouseX > _mright(obj))
3100 /* scroll right */
3101 new_virt_offx += 4;
3102 if (new_virt_offx > data->virt_mwidth - _mwidth(obj))
3103 new_virt_offx = data->virt_mwidth - _mwidth(obj);
3104 if (new_virt_offx < 0)
3105 new_virt_offx = 0;
3108 if (msg->imsg->MouseY < _mtop(obj))
3110 /* scroll top */
3111 if (new_virt_offy >= 4)
3112 new_virt_offy -= 4;
3113 else
3114 new_virt_offy = 0;
3116 else if (msg->imsg->MouseY > _mbottom(obj))
3118 /* scroll bottom */
3119 new_virt_offy += 4;
3120 if (new_virt_offy > data->virt_mheight - _mheight(obj))
3121 new_virt_offy = data->virt_mheight - _mheight(obj);
3122 if (new_virt_offy < 0)
3123 new_virt_offy = 0;
3126 if (new_virt_offx != data->virt_offx
3127 || new_virt_offy != data->virt_offy)
3129 SetAttrs(obj,
3130 MUIA_Virtgroup_Left, new_virt_offx,
3131 MUIA_Virtgroup_Top, new_virt_offy,
3132 MUIA_Group_Forward, FALSE, TAG_DONE);
3135 break;
3139 return 0;
3142 /**************************************************************************
3143 MUIM_DrawBackground
3144 **************************************************************************/
3145 IPTR Group__MUIM_DrawBackground(struct IClass *cl, Object *obj,
3146 struct MUIP_DrawBackground *msg)
3148 struct MUI_GroupData *data = INST_DATA(cl, obj);
3150 if (data->flags & GROUP_VIRTUAL)
3152 struct MUIP_DrawBackground msg2 = *msg;
3154 msg2.xoffset += data->virt_offx;
3155 msg2.yoffset += data->virt_offy;
3157 return DoSuperMethodA(cl, obj, (Msg) & msg2);
3160 return DoSuperMethodA(cl, obj, (Msg) msg);
3163 /**************************************************************************
3164 MUIM_FindAreaObject
3165 Find the given object or return NULL
3166 **************************************************************************/
3167 IPTR Group__MUIM_FindAreaObject(struct IClass *cl, Object *obj,
3168 struct MUIP_FindAreaObject *msg)
3170 struct MUI_GroupData *data = INST_DATA(cl, obj);
3171 Object *cstate;
3172 Object *child;
3173 struct MinList *ChildList = NULL;
3175 // it's me ?
3176 if (msg->obj == obj)
3177 return (IPTR) obj;
3179 // it's one of my children ?
3180 get(data->family, MUIA_Family_List, &(ChildList));
3181 cstate = (Object *) ChildList->mlh_Head;
3182 while ((child = NextObject(&cstate)))
3184 if (msg->obj == child)
3185 return (IPTR) child;
3188 // let the children find it
3189 get(data->family, MUIA_Family_List, &(ChildList));
3190 cstate = (Object *) ChildList->mlh_Head;
3191 while ((child = NextObject(&cstate)))
3193 Object *res = (Object *) DoMethodA(child, (Msg) msg);
3194 if (res != NULL)
3195 return (IPTR) res;
3198 return (IPTR) NULL;
3201 /**************************************************************************
3202 MUIM_Export : to export an object's "contents" to a dataspace object.
3203 **************************************************************************/
3204 static IPTR Group__MUIM_Export(struct IClass *cl, Object *obj,
3205 struct MUIP_Export *msg)
3207 struct MUI_GroupData *data = INST_DATA(cl, obj);
3208 Object *cstate;
3209 Object *child;
3210 struct MinList *ChildList = NULL;
3212 get(data->family, MUIA_Family_List, &(ChildList));
3213 if (!ChildList)
3214 return 0;
3216 cstate = (Object *) ChildList->mlh_Head;
3217 while ((child = NextObject(&cstate)))
3219 DoMethodA(child, (Msg) msg);
3222 return 0;
3226 /**************************************************************************
3227 MUIM_Import : to import an object's "contents" from a dataspace object.
3228 **************************************************************************/
3229 static IPTR Group__MUIM_Import(struct IClass *cl, Object *obj,
3230 struct MUIP_Import *msg)
3232 struct MUI_GroupData *data = INST_DATA(cl, obj);
3233 Object *cstate;
3234 Object *child;
3235 struct MinList *ChildList = NULL;
3237 get(data->family, MUIA_Family_List, &(ChildList));
3238 if (!ChildList)
3239 return 0;
3241 cstate = (Object *) ChildList->mlh_Head;
3242 while ((child = NextObject(&cstate)))
3244 DoMethodA(child, (Msg) msg);
3247 return 0;
3250 /**************************************************************************
3251 MUIM_Notify - disabled now because previous Zune versions had a OM_GET
3252 check in MUIM_Notify which is no longer the case
3253 **************************************************************************/
3254 #if 0
3255 STATIC IPTR Group_Notify(struct IClass *cl, Object *obj,
3256 struct MUIP_Notify *msg)
3258 struct MUI_GroupData *data = INST_DATA(cl, obj);
3259 Object *cstate;
3260 Object *child;
3261 struct MinList *ChildList;
3263 /* Try at first if understand the message our self
3264 ** We disable the forwarding of the OM_GET message
3265 ** as the MUIM_Notify otherwise would "think" that
3266 ** the group class actually understands the attribute
3267 ** although a child does this only
3269 data->dont_forward_get = 1;
3270 if (DoSuperMethodA(cl, obj, (Msg) msg))
3272 data->dont_forward_get = 0;
3273 return 1;
3276 /* We ourselves didn't understand the notify tag so we try the
3277 * children now */
3278 data->dont_forward_get = 0;
3280 get(data->family, MUIA_Family_List, (IPTR *) & (ChildList));
3281 cstate = (Object *) ChildList->mlh_Head;
3282 while ((child = NextObject(&cstate)))
3284 if (DoMethodA(child, (Msg) msg))
3285 return 1;
3287 return 0;
3289 #endif
3291 #if 1
3292 /* Notes about Group_Notify() and echo notification problem:
3293 It was discovered that MUI seems to have some special handling for group class
3294 which will drop notifications on the children which are found to not
3295 understand the attribute.
3297 This is done by checking if an OM_GET on the child returns TRUE.
3298 There's a little problem here because it is not known how big the storage
3299 needed for the attribute in question will be. Almost no class uses anything
3300 bigger than one IPTR. For "big" attributes those return a pointer to the data,
3301 not the data itself. Unfortuntely there are some exceptions like colorwheel
3302 class which does not return a pointer, but the data itself. So it's not
3303 enough to use one single IPTR variable (4 Bytes on 32bit machines, 8 bytes
3304 on 64 bit machines) to store the result of the test-OM_Get.
3306 There is no general way to query the size needed so if one wants to change
3307 Zune to work like MUI one needs to choose a size which one hopes will be
3308 big enough to hold all possible attributes of all classes, old, present
3309 and future ones.
3311 STATIC IPTR Group_Notify(struct IClass *cl, Object *obj,
3312 struct MUIP_Notify *msg)
3314 struct MUI_GroupData *data = INST_DATA(cl, obj);
3315 Object *cstate;
3316 Object *child;
3317 struct MinList *ChildList = NULL;
3318 IPTR attr[30];
3320 data->dont_forward_get = 1;
3322 if (GetAttr(msg->TrigAttr, obj, attr))
3324 data->dont_forward_get = 0;
3325 return DoSuperMethodA(cl, obj, (Msg) msg);
3327 data->dont_forward_get = 0;
3329 get(data->family, MUIA_Family_List, &(ChildList));
3330 if (!ChildList)
3331 return TRUE;
3333 cstate = (Object *) ChildList->mlh_Head;
3334 while ((child = NextObject(&cstate)))
3337 if (GetAttr(msg->TrigAttr, child, attr))
3339 DoMethodA(child, (Msg) msg);
3340 /* No return here! */
3343 return TRUE;
3345 #endif
3347 BOOPSI_DISPATCHER(IPTR, Group_Dispatcher, cl, obj, msg)
3349 switch (msg->MethodID)
3351 case OM_NEW:
3352 return Group__OM_NEW(cl, obj, (struct opSet *)msg);
3353 case OM_DISPOSE:
3354 return Group__OM_DISPOSE(cl, obj, msg);
3355 case OM_SET:
3356 return Group__OM_SET(cl, obj, (struct opSet *)msg);
3357 case OM_GET:
3358 return Group__OM_GET(cl, obj, (struct opGet *)msg);
3359 case OM_ADDMEMBER: /* Fall through */
3360 case MUIM_Group_AddTail:
3361 return Group__MUIM_AddTail(cl, obj, (APTR) msg);
3362 case MUIM_Group_AddHead:
3363 return Group__MUIM_AddHead(cl, obj, (APTR) msg);
3364 case MUIM_Group_Insert:
3365 return Group__MUIM_Insert(cl, obj, (APTR) msg);
3366 case OM_REMMEMBER: /* Fall through */
3367 case MUIM_Group_Remove:
3368 return Group__MUIM_Remove(cl, obj, (APTR) msg);
3369 case MUIM_AskMinMax:
3370 return Group__MUIM_AskMinMax(cl, obj, (APTR) msg);
3371 case MUIM_Group_ExitChange:
3372 return Group__MUIM_ExitChange(cl, obj, (APTR) msg);
3373 case MUIM_Group_ExitChange2:
3374 return Group__MUIM_ExitChange2(cl, obj, (APTR) msg);
3375 case MUIM_Group_InitChange:
3376 return Group__MUIM_InitChange(cl, obj, (APTR) msg);
3377 case MUIM_Group_Sort:
3378 return Group__MUIM_Sort(cl, obj, (APTR) msg);
3379 case MUIM_Group_DoMethodNoForward:
3380 return Group__MUIM_DoMethodNoForward(cl, obj, (APTR) msg);
3381 case MUIM_ConnectParent:
3382 return Group__MUIM_ConnectParent(cl, obj, (APTR) msg);
3383 case MUIM_DisconnectParent:
3384 return Group__MUIM_DisconnectParent(cl, obj, (APTR) msg);
3385 case MUIM_Layout:
3386 return Group__MUIM_Layout(cl, obj, (APTR) msg);
3387 case MUIM_Setup:
3388 return Group__MUIM_Setup(cl, obj, (APTR) msg);
3389 case MUIM_Cleanup:
3390 return Group__MUIM_Cleanup(cl, obj, (APTR) msg);
3391 case MUIM_Draw:
3392 return Group__MUIM_Draw(cl, obj, (APTR) msg);
3394 case MUIM_FindUData:
3395 return Group__MUIM_FindUData(cl, obj, (APTR) msg);
3396 case MUIM_GetUData:
3397 return Group__MUIM_GetUData(cl, obj, (APTR) msg);
3398 case MUIM_SetUData:
3399 return Group__MUIM_SetUData(cl, obj, (APTR) msg);
3400 case MUIM_SetUDataOnce:
3401 return Group__MUIM_SetUDataOnce(cl, obj, (APTR) msg);
3402 case MUIM_Show:
3403 return Group__MUIM_Show(cl, obj, (APTR) msg);
3404 case MUIM_Hide:
3405 return Group__MUIM_Hide(cl, obj, (APTR) msg);
3406 case MUIM_HandleEvent:
3407 return Group__MUIM_HandleEvent(cl, obj, (APTR) msg);
3408 case MUIM_DrawBackground:
3409 return Group__MUIM_DrawBackground(cl, obj, (APTR) msg);
3410 case MUIM_DragQueryExtended:
3411 return Group__MUIM_DragQueryExtended(cl, obj, (APTR) msg);
3412 case MUIM_FindAreaObject:
3413 return Group__MUIM_FindAreaObject(cl, obj, (APTR) msg);
3414 case MUIM_Export:
3415 return Group__MUIM_Export(cl, obj, (APTR) msg);
3416 case MUIM_Import:
3417 return Group__MUIM_Import(cl, obj, (APTR) msg);
3419 //#if 0
3420 #if 1
3421 /* Disabled. See above */
3422 case MUIM_Notify:
3423 return Group_Notify(cl, obj, (APTR) msg);
3424 #endif
3425 case MUIM_Set:
3426 case MUIM_MultiSet:
3427 case MUIM_CallHook:
3428 case MUIM_DrawParentBackground:
3429 case MUIM_DragBegin:
3430 case MUIM_DragDrop:
3431 case MUIM_DragQuery:
3432 case MUIM_DragFinish:
3433 case MUIM_DoDrag:
3434 case MUIM_CreateDragImage:
3435 case MUIM_DeleteDragImage:
3436 case MUIM_GoActive:
3437 case MUIM_GoInactive:
3438 case MUIM_CreateBubble:
3439 case MUIM_DeleteBubble:
3440 case MUIM_CreateShortHelp:
3441 case MUIM_DeleteShortHelp:
3442 case OM_ADDTAIL:
3443 case OM_REMOVE:
3444 return DoSuperMethodA(cl, obj, (APTR) msg);
3445 /* Needs not to be forwarded? */
3448 /* sometimes you want to call a superclass method,
3449 * but not dispatching to child.
3450 * But what to do with list methods in a listview ?
3452 Group_DispatchMsg(cl, obj, (APTR) msg);
3454 return DoSuperMethodA(cl, obj, msg);
3456 BOOPSI_DISPATCHER_END
3459 * Class descriptor.
3461 const struct __MUIBuiltinClass _MUI_Group_desc =
3463 MUIC_Group,
3464 MUIC_Area,
3465 sizeof(struct MUI_GroupData),
3466 (void *) Group_Dispatcher