- MUIM_Group_ExitChange now only does a relayout if objects have been added
[AROS.git] / workbench / libs / muimaster / classes / group.c
blobc3e49f8f341f901d8e7b242515dedbcea9284b71
1 /*
2 Copyright 1999, David Le Corfec.
3 Copyright 2002-2012, 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 subtract those from the total
230 * 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 muiNotifyData(msgint->obj)->mnd_ParentObject = obj;
273 DoMethod(msgint->obj, MUIM_ConnectParent, (IPTR) obj);
276 if (_flags(obj) & MADF_SETUP)
278 DoSetupMethod(msgint->obj, muiRenderInfo(obj));
280 /* if (_flags(obj) & MADF_CANDRAW) */
281 /* DoShowMethod(msg->opam_Object); */
283 return TRUE;
286 /**************************************************************************
287 OM_NEW - Constructor
288 **************************************************************************/
289 IPTR Group__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
291 struct MUI_GroupData *data;
292 struct TagItem *tags, *tag;
293 BOOL bad_children = FALSE;
294 IPTR disabled = FALSE;
295 IPTR frame = MUIV_Frame_None;
297 D(bug("[group.mui] OM_NEW, object 0x%p\n", obj));
299 obj = (Object *) DoSuperMethodA(cl, obj, (Msg) msg);
300 if (!obj)
301 return 0;
303 /* Initial local instance data */
304 data = INST_DATA(cl, obj);
306 data->family = MUI_NewObjectA(MUIC_Family, NULL);
307 if (!data->family)
309 CoerceMethod(cl, obj, OM_DISPOSE);
310 return 0;
313 data->horiz_spacing = -1;
314 data->vert_spacing = -1;
315 data->columns = 1;
316 data->rows = 1;
317 data->active_page = 0;
318 get(obj, MUIA_Frame, &frame);
320 /* parse initial taglist */
321 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
323 switch (tag->ti_Tag)
325 case MUIA_Group_Child:
326 D(bug("[group.mui] Adding child 0x%p\n", tag->ti_Data));
327 if (tag->ti_Data)
329 DoMethod(obj, OM_ADDMEMBER, tag->ti_Data);
330 /* Set first child as group title */
331 if ((frame == MUIV_Frame_Register)
332 && (data->titlegroup == NULL))
333 data->titlegroup = (Object *) tag->ti_Data;
335 else
336 bad_children = TRUE;
337 break;
339 case MUIA_Group_ActivePage:
340 change_active_page(cl, obj, (LONG) tag->ti_Data);
341 break;
343 case MUIA_Group_Columns:
344 data->columns = (tag->ti_Data) > 1 ? tag->ti_Data : 1;
345 data->rows = 0;
346 break;
348 case MUIA_Group_Horiz:
349 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_HORIZ);
350 break;
352 case MUIA_Group_HorizSpacing:
353 data->flags |= GROUP_HSPACING;
354 data->horiz_spacing = tag->ti_Data;
355 break;
357 case MUIA_Group_LayoutHook:
358 data->layout_hook = (struct Hook *)tag->ti_Data;
359 break;
361 case MUIA_Group_PageMode:
362 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_PAGEMODE);
363 break;
365 case MUIA_Group_Rows:
366 data->rows = MAX((ULONG) tag->ti_Data, 1);
367 data->columns = 0;
368 break;
370 case MUIA_Group_SameHeight:
371 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_HEIGHT);
372 break;
374 case MUIA_Group_SameSize:
375 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_HEIGHT);
376 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_WIDTH);
377 break;
379 case MUIA_Group_SameWidth:
380 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_WIDTH);
381 break;
383 case MUIA_Group_Spacing:
384 data->flags |= (GROUP_HSPACING | GROUP_VSPACING);
385 data->horiz_spacing = tag->ti_Data;
386 data->vert_spacing = tag->ti_Data;
387 break;
389 case MUIA_Group_VertSpacing:
390 data->flags |= GROUP_VSPACING;
391 data->vert_spacing = tag->ti_Data;
392 break;
394 case MUIA_Group_Virtual:
395 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_VIRTUAL);
396 break;
400 if (bad_children)
402 CoerceMethod(cl, obj, OM_DISPOSE);
403 return 0;
406 /* D(bug("Group_New(0x%lx)\n",obj)); */
408 if (data->flags & GROUP_VIRTUAL)
410 /* This is used by MUI_Render() to determine if group is virtual.
411 * It then installs a clip region.
412 * Also MUI_Layout() uses this. Probably for speed up reason */
413 _flags(obj) |= MADF_ISVIRTUALGROUP;
416 /* will forward MUIA_Disabled to children */
417 get(obj, MUIA_Disabled, &disabled);
418 if (disabled)
420 set(obj, MUIA_Disabled, TRUE);
423 /* This is only used for virtual groups */
424 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS; /* Will be filled on demand */
425 data->ehn.ehn_Priority = 10; /* Will hear the click before all
426 * other normal objects */
427 data->ehn.ehn_Flags = 0;
428 data->ehn.ehn_Object = obj;
429 data->ehn.ehn_Class = cl;
430 return (IPTR) obj;
433 /**************************************************************************
434 OM_DISPOSE
435 **************************************************************************/
436 IPTR Group__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
438 struct MUI_GroupData *data = INST_DATA(cl, obj);
440 if (data->row_infos != NULL)
441 mui_free(data->row_infos);
442 if (data->col_infos != NULL)
443 mui_free(data->col_infos);
444 if (data->family != NULL)
445 MUI_DisposeObject(data->family);
446 return DoSuperMethodA(cl, obj, msg);
449 /**************************************************************************
450 OM_SET
451 **************************************************************************/
452 IPTR Group__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
454 struct MUI_GroupData *data = INST_DATA(cl, obj);
455 struct TagItem *tags = msg->ops_AttrList;
456 struct TagItem *tag;
457 BOOL forward = TRUE;
458 BOOL need_recalc = FALSE;
459 IPTR retval;
461 int virt_offx = data->virt_offx, virt_offy = data->virt_offy;
463 /* There are many ways to find out what tag items provided by set()
464 ** we do know. The best way should be using NextTagItem() and simply
465 ** browsing through the list.
468 /* Parse group attributes before calling DoSuperMethodA(),
469 ** otherwise if an app for example sets up a notification
470 ** on MUIA_Group_ActivePage which calls a hook, and then
471 ** the hook function calls get(obj, MUIA_Group_ActivePage),
472 ** it would get returned the old active page instead of the new
473 ** active page
476 while ((tag = NextTagItem(&tags)) != NULL)
478 switch (tag->ti_Tag)
480 case MUIA_Group_Columns:
481 data->columns = MAX((ULONG) tag->ti_Data, 1);
482 data->rows = 0;
483 need_recalc = TRUE;
484 break;
485 case MUIA_Group_ActivePage:
486 change_active_page(cl, obj, (LONG) tag->ti_Data);
487 break;
488 case MUIA_Group_Forward:
489 forward = tag->ti_Data;
490 break;
491 case MUIA_Group_HorizSpacing:
492 data->flags |= GROUP_HSPACING;
493 data->horiz_spacing = tag->ti_Data;
494 break;
495 case MUIA_Group_Rows:
496 data->rows = MAX((ULONG) tag->ti_Data, 1);
497 data->columns = 0;
498 need_recalc = TRUE;
499 break;
500 case MUIA_Group_Spacing:
501 data->flags |= (GROUP_HSPACING | GROUP_VSPACING);
502 data->horiz_spacing = tag->ti_Data;
503 data->vert_spacing = tag->ti_Data;
504 break;
505 case MUIA_Group_VertSpacing:
506 data->flags |= GROUP_VSPACING;
507 data->vert_spacing = tag->ti_Data;
508 break;
509 case MUIA_Virtgroup_Left:
510 //kprintf("set virtgroup_left: %d\n", tag->ti_Data);
511 virt_offx = tag->ti_Data;
512 break;
514 case MUIA_Group_LayoutHook:
516 [ach] Seems like MUI supports setting this attribute after
517 initialization, even though the documentation states
518 otherwise. At least some programs use it...
520 data->layout_hook = (struct Hook *)tag->ti_Data;
521 break;
523 case MUIA_Virtgroup_Top:
524 //kprintf("set virtgroup_top: %d\n", tag->ti_Data);
525 virt_offy = tag->ti_Data;
526 break;
531 if (muiRenderInfo(obj) && need_recalc)
532 DoMethod(_win(obj), MUIM_Window_RecalcDisplay, (IPTR) obj);
534 retval = DoSuperMethodA(cl, obj, (Msg) msg);
536 /* seems to be the documented behaviour, however it should be slow! */
538 if (forward)
540 /* Attributes which are to be filtered out, so that they are ignored
541 when OM_SET is passed to group's children */
543 tags = msg->ops_AttrList;
544 while ((tag = NextTagItem(&tags)) != NULL)
546 switch (tag->ti_Tag)
548 case MUIA_HelpLine:
549 case MUIA_HelpNode:
550 case MUIA_ObjectID:
551 case MUIA_UserData:
553 case MUIA_ContextMenu:
554 case MUIA_ContextMenuTrigger:
555 case MUIA_ControlChar:
556 case MUIA_CycleChain:
557 case MUIA_Draggable:
558 case MUIA_FillArea:
559 case MUIA_Group_ActivePage:
560 case MUIA_Frame:
561 case MUIA_FrameTitle:
562 case MUIA_HorizWeight:
563 case MUIA_Pressed:
564 case MUIA_ShortHelp:
565 case MUIA_ShowMe:
566 case MUIA_VertWeight:
567 case MUIA_Weight:
568 case MUIA_Virtgroup_Left:
569 case MUIA_Virtgroup_Top:
570 case MUIA_AppMessage:
571 tag->ti_Tag = TAG_IGNORE;
572 break;
573 case MUIA_Selected:
574 /* D(bug("Group_Set(%p) MUIA_Selected forwarded\n", obj)); */
575 /* tag->ti_Tag = TAG_IGNORE; */
576 break;
580 Group_DispatchMsg(cl, obj, (Msg) msg);
584 if (virt_offx != data->virt_offx || virt_offy != data->virt_offy)
586 if (_flags(obj) & MADF_CANDRAW)
587 Group__MUIM_Hide(cl, obj, NULL);
588 data->virt_offx = virt_offx;
589 data->virt_offy = virt_offy;
590 /* Relayout ourself, this will also relayout all the children */
591 DoMethod(obj, MUIM_Layout);
592 if (_flags(obj) & MADF_CANDRAW)
593 Group__MUIM_Show(cl, obj, NULL);
594 data->update = 2;
595 MUI_Redraw(obj, MADF_DRAWUPDATE);
598 return retval;
602 /**************************************************************************
603 OM_GET
604 **************************************************************************/
605 IPTR Group__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
607 /* small macro to simplify return value storage */
608 #define STORE *(msg->opg_Storage)
610 struct MUI_GroupData *data = INST_DATA(cl, obj);
612 switch (msg->opg_AttrID)
614 case MUIA_Version:
615 STORE = __version;
616 return 1;
617 case MUIA_Revision:
618 STORE = __revision;
619 return 1;
620 case MUIA_Group_ActivePage:
621 STORE = data->active_page;
622 return 1;
623 case MUIA_Group_ChildList:
624 return GetAttr(MUIA_Family_List, data->family, msg->opg_Storage);
625 case MUIA_Group_Horiz:
626 STORE = (data->flags & GROUP_HORIZ);
627 return 1;
628 case MUIA_Group_HorizSpacing:
629 STORE = data->horiz_spacing;
630 return 1;
631 case MUIA_Group_VertSpacing:
632 STORE = data->vert_spacing;
633 return 1;
634 case MUIA_Group_ChildCount:
635 STORE = data->num_children;
636 return 1;
637 case MUIA_Virtgroup_Left:
638 STORE = data->virt_offx;
639 return 1;
640 case MUIA_Virtgroup_Top:
641 STORE = data->virt_offy;
642 return 1;
643 case MUIA_Virtgroup_Width:
644 STORE = data->virt_mwidth;
645 return 1;
646 case MUIA_Virtgroup_Height:
647 STORE = data->virt_mheight;
648 return 1;
649 case MUIA_Virtgroup_MinWidth:
650 STORE = data->saved_minwidth;
651 return 1;
652 case MUIA_Virtgroup_MinHeight:
653 STORE = data->saved_minheight;
654 return 1;
657 /* our handler didn't understand the attribute, we simply pass
658 ** it to our superclass now
660 if (DoSuperMethodA(cl, obj, (Msg) msg))
661 return 1;
663 /* seems to be the documented behaviour, however it should be slow! */
664 if (!data->dont_forward_get && !data->dont_forward_methods)
666 Object *cstate;
667 Object *child;
668 struct MinList *ChildList = NULL;
670 get(data->family, MUIA_Family_List, &(ChildList));
671 cstate = (Object *) ChildList->mlh_Head;
672 while ((child = NextObject(&cstate)))
673 if (DoMethodA(child, (Msg) msg))
674 return 1;
676 return 0;
677 #undef STORE
681 /**************************************************************************
682 MUIM_AddTail
683 **************************************************************************/
684 IPTR Group__MUIM_AddTail(struct IClass *cl, Object *obj,
685 struct MUIP_Group_AddTail *msg)
687 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
690 /**************************************************************************
691 MUIM_AddHead
692 **************************************************************************/
693 IPTR Group__MUIM_AddHead(struct IClass *cl, Object *obj,
694 struct MUIP_Group_AddHead *msg)
696 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
699 /**************************************************************************
700 MUIM_Insert
701 **************************************************************************/
702 IPTR Group__MUIM_Insert(struct IClass *cl, Object *obj,
703 struct MUIP_Group_Insert *msg)
705 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
708 /**************************************************************************
709 MUIM_Remove
710 **************************************************************************/
711 IPTR Group__MUIM_Remove(struct IClass *cl, Object *obj,
712 struct MUIP_Group_Remove *msg)
714 struct MUI_GroupData *data = INST_DATA(cl, obj);
716 if (_flags(obj) & MADF_CANDRAW)
717 DoHideMethod(msg->obj);
718 if (_flags(obj) & MADF_SETUP)
719 DoMethod(msg->obj, MUIM_Cleanup);
720 if (muiNotifyData(obj)->mnd_GlobalInfo)
722 DoMethod(msg->obj, MUIM_DisconnectParent);
723 muiNotifyData(msg->obj)->mnd_ParentObject = NULL;
725 _flags(msg->obj) &= ~MADF_INVIRTUALGROUP;
728 if ((data->flags & GROUP_CHANGING) != 0)
729 data->flags |= GROUP_CHANGED;
730 data->num_children--;
731 DoMethodA(data->family, (Msg) msg);
733 return TRUE;
737 /**************************************************************************
738 MUIM_ConnectParent
739 **************************************************************************/
740 IPTR Group__MUIM_ConnectParent(struct IClass *cl, Object *obj,
741 struct MUIP_ConnectParent *msg)
743 struct MUI_GroupData *data = INST_DATA(cl, obj);
744 Object *cstate;
745 Object *child;
746 struct MinList *ChildList = NULL;
748 DoSuperMethodA(cl, obj, (Msg) msg);
750 get(data->family, MUIA_Family_List, &(ChildList));
751 cstate = (Object *) ChildList->mlh_Head;
752 while ((child = NextObject(&cstate)))
754 if ((_flags(obj) & MADF_INVIRTUALGROUP)
755 || (data->flags & GROUP_VIRTUAL))
757 _flags(child) |= MADF_INVIRTUALGROUP;
760 /* Only children of groups can have parents */
761 muiNotifyData(child)->mnd_ParentObject = obj;
763 DoMethod(child, MUIM_ConnectParent, (IPTR) obj);
765 return TRUE;
768 /**************************************************************************
769 MUIM_DisconnectParent
770 **************************************************************************/
771 IPTR Group__MUIM_DisconnectParent(struct IClass *cl, Object *obj,
772 struct MUIP_ConnectParent *msg)
774 struct MUI_GroupData *data = INST_DATA(cl, obj);
775 Object *cstate;
776 Object *child;
777 struct MinList *ChildList = NULL;
779 get(data->family, MUIA_Family_List, &(ChildList));
780 cstate = (Object *) ChildList->mlh_Head;
781 while ((child = NextObject(&cstate)))
783 DoMethodA(child, (Msg) msg);
784 muiNotifyData(child)->mnd_ParentObject = NULL;
785 _flags(child) &= ~MADF_INVIRTUALGROUP;
787 DoSuperMethodA(cl, obj, (Msg) msg);
788 return TRUE;
792 * Put group in exchange state
794 IPTR Group__MUIM_InitChange(struct IClass *cl, Object *obj,
795 struct MUIP_Group_InitChange *msg)
797 struct MUI_GroupData *data = INST_DATA(cl, obj);
799 data->flags &= ~GROUP_CHANGED;
800 data->flags |= GROUP_CHANGING;
801 return TRUE;
806 * Will recalculate display after dynamic adding/removing
808 IPTR Group__MUIM_ExitChange(struct IClass *cl, Object *obj,
809 struct MUIP_Group_ExitChange *msg)
811 struct MUI_GroupData *data = INST_DATA(cl, obj);
813 data->flags &= ~GROUP_CHANGING;
815 if (data->flags & GROUP_CHANGED)
817 data->flags &= ~GROUP_CHANGED;
819 if ((_flags(obj) & MADF_SETUP) && _win(obj))
821 Object *win = _win(obj);
822 Object *parent = obj;
824 /* CHECKME: Don't call RecalcDisplay if one of our parents is
825 in GROUP_CHANGING state to prevent crash with Zune prefs
826 program NListtree page because NList/NListtree when
827 killing tree images in MUIM_Cleanup uses InitChange/
828 ExitChange. Zune prefs program uses InitChange/ExitChange
829 when switching page -> nesting -> mess. */
831 while ((parent = _parent(parent)))
833 struct MUI_GroupData *pdata = INST_DATA(cl, parent);
835 if (parent == win)
836 break;
838 if (pdata->flags & GROUP_CHANGING)
840 return TRUE;
845 DoMethod(win, MUIM_Window_RecalcDisplay, (IPTR) obj);
849 return TRUE;
854 * Will recalculate display after dynamic adding/removing
856 IPTR Group__MUIM_ExitChange2(struct IClass *cl, Object *obj,
857 struct MUIP_Group_ExitChange2 *msg)
859 struct MUI_GroupData *data = INST_DATA(cl, obj);
861 if (data->flags & GROUP_CHANGING)
863 data->flags &= ~(GROUP_CHANGING | GROUP_CHANGED);
865 if ((_flags(obj) & MADF_SETUP) && _win(obj))
867 Object *win = _win(obj);
868 Object *parent = obj;
870 /* CHECKME: Don't call RecalcDisplay if one of our parents is
871 in GROUP_CHANGING state to prevent crash with Zune prefs
872 program NListtree page because NList/NListtree when
873 killing tree images in MUIM_Cleanup uses InitChange/
874 ExitChange. Zune prefs program uses InitChange/ExitChange
875 when switching page -> nesting -> mess. */
877 while ((parent = _parent(parent)))
879 struct MUI_GroupData *pdata = INST_DATA(cl, parent);
881 if (parent == win)
882 break;
884 if (pdata->flags & GROUP_CHANGING)
886 return TRUE;
891 DoMethod(win, MUIM_Window_RecalcDisplay, (IPTR) obj);
895 return TRUE;
900 * Sort the family
902 IPTR Group__MUIM_Sort(struct IClass *cl, Object *obj,
903 struct MUIP_Group_Sort *msg)
905 struct MUI_GroupData *data = INST_DATA(cl, obj);
907 /* modify message */
908 msg->MethodID = MUIM_Family_Sort;
910 DoMethodA(data->family, (APTR) msg);
912 /* restore original message */
913 msg->MethodID = MUIM_Group_Sort;
914 return TRUE;
917 /**************************************************************************
918 MUIM_Group_DoMethodNoForward
920 Executes the given method but does not forward it to the children
921 **************************************************************************/
922 IPTR Group__MUIM_DoMethodNoForward(struct IClass *cl, Object *obj,
923 struct MUIP_Group_DoMethodNoForward *msg)
925 struct MUI_GroupData *data = INST_DATA(cl, obj);
926 IPTR rc;
927 data->dont_forward_methods = 1; /* disable forwarding */
928 rc = DoMethodA(obj, (Msg) & msg->DoMethodID);
929 /* Probably doesn't work correctly on AROS? */
931 data->dont_forward_methods = 0;
932 return rc;
936 * Propagate a method to group children.
938 static ULONG Group_DispatchMsg(struct IClass *cl, Object *obj, Msg msg)
940 struct MUI_GroupData *data = INST_DATA(cl, obj);
941 Object *cstate;
942 Object *child;
943 struct MinList *ChildList = NULL;
945 if (data->dont_forward_methods)
946 return TRUE;
948 get(data->family, MUIA_Family_List, &(ChildList));
949 cstate = (Object *) ChildList->mlh_Head;
950 while ((child = NextObject(&cstate)))
952 DoMethodA(child, (Msg) msg);
954 return TRUE;
958 /**************************************************************************
959 MUIM_Setup
960 **************************************************************************/
961 IPTR Group__MUIM_Setup(struct IClass *cl, Object *obj,
962 struct MUIP_Setup *msg)
964 struct MUI_GroupData *data = INST_DATA(cl, obj);
965 Object *cstate;
966 Object *cstate_copy;
967 Object *child;
968 Object *childFailed;
969 struct MinList *ChildList = NULL;
971 if (!DoSuperMethodA(cl, obj, (Msg) msg))
972 return FALSE;
974 ASSERT_VALID_PTR(muiGlobalInfo(obj));
976 if (!(data->flags & GROUP_HSPACING))
977 data->horiz_spacing = muiGlobalInfo(obj)->mgi_Prefs->group_hspacing;
978 if (!(data->flags & GROUP_VSPACING))
979 data->vert_spacing = muiGlobalInfo(obj)->mgi_Prefs->group_vspacing;
980 get(data->family, MUIA_Family_List, &(ChildList));
981 cstate = cstate_copy = (Object *) ChildList->mlh_Head;
982 while ((child = NextObject(&cstate)))
984 #if 0 /* SHOWME affects only show/hide */
985 if (!(_flags(child) & MADF_SHOWME))
986 continue;
987 #endif
989 if (!DoSetupMethod(child, msg->RenderInfo))
991 /* Send MUIM_Cleanup to all objects that received MUIM_Setup.
993 childFailed = child;
994 cstate = cstate_copy;
995 while ((child = NextObject(&cstate)) && (child != childFailed))
997 #if 0 /* SHOWME affects only show/hide */
998 if (!(_flags(child) & MADF_SHOWME))
999 continue;
1000 #endif
1001 DoMethod(child, MUIM_Cleanup);
1003 return FALSE;
1007 if (data->flags & GROUP_VIRTUAL)
1009 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
1010 (IPTR) & data->ehn);
1013 return TRUE;
1017 /**************************************************************************
1018 MUIM_Cleanup
1019 **************************************************************************/
1020 IPTR Group__MUIM_Cleanup(struct IClass *cl, Object *obj, Msg msg)
1022 struct MUI_GroupData *data = INST_DATA(cl, obj);
1023 Object *cstate;
1024 Object *child;
1025 struct MinList *ChildList = NULL;
1027 if (data->flags & GROUP_VIRTUAL)
1029 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
1030 (IPTR) & data->ehn);
1033 get(data->family, MUIA_Family_List, &(ChildList));
1034 cstate = (Object *) ChildList->mlh_Head;
1035 while ((child = NextObject(&cstate)))
1037 #if 0 /* SHOWME affects only show/hide */
1038 if (!(_flags(child) & MADF_SHOWME))
1039 continue;
1040 #endif
1041 DoMethodA(child, (Msg) msg);
1043 return DoSuperMethodA(cl, obj, (Msg) msg);
1048 /**************************************************************************
1049 MUIM_Draw - draw the group
1050 **************************************************************************/
1051 IPTR Group__MUIM_Draw(struct IClass *cl, Object *obj,
1052 struct MUIP_Draw *msg)
1054 struct MUI_GroupData *data = INST_DATA(cl, obj);
1055 Object *cstate;
1056 Object *child;
1057 struct MinList *ChildList = NULL;
1058 struct Rectangle group_rect; /* child_rect; */
1059 int page;
1060 struct Region *region = NULL;
1061 APTR clip = (APTR) - 1;
1063 /* D(bug("Group_Draw(%lx) %ldx%ldx%ldx%ld upd=%d page=%d\n", */
1064 /* obj,_left(obj),_top(obj),_right(obj),_bottom(obj), */
1065 /* data->update, data->active_page)); */
1066 /* D(bug("Group_Draw(%p) msg=0x%08lx flags=0x%08lx\n", */
1067 /* obj, msg->flags, _flags(obj))); */
1069 if (data->flags & GROUP_CHANGING)
1070 return FALSE;
1072 if (muiGlobalInfo(obj)->mgi_Prefs->window_redraw ==
1073 WINDOW_REDRAW_WITHOUT_CLEAR)
1075 region = NewRegion();
1076 if (region)
1078 struct Rectangle rect;
1080 rect.MinX = _left(obj);
1081 rect.MinY = _top(obj);
1082 rect.MaxX = _right(obj);
1083 rect.MaxY = _bottom(obj);
1085 OrRectRegion(region, &rect);
1086 page = -1;
1087 get(data->family, MUIA_Family_List, &(ChildList));
1088 cstate = (Object *) ChildList->mlh_Head;
1089 while ((child = NextObject(&cstate)))
1091 /*page++; */
1092 /* redraw problem with colorwheel in coloradjust register */
1093 if ((data->flags & GROUP_PAGEMODE)
1094 && ((page != data->active_page)
1095 && (child != data->titlegroup)))
1096 continue;
1098 if ((muiAreaData(child)->mad_Flags & MADF_CANDRAW)
1099 && (_width(child) > 0) && (_height(child) > 0))
1101 rect.MinX = MAX(_left(child), _mleft(obj));
1102 rect.MinY = MAX(_top(child), _mtop(obj));
1103 rect.MaxX = MIN(_right(child), _mright(obj));
1104 rect.MaxY = MIN(_bottom(child), _mbottom(obj));
1106 if ((rect.MaxX >= rect.MinX)
1107 && (rect.MaxY >= rect.MinY))
1109 ClearRectRegion(region, &rect);
1114 clip = MUI_AddClipRegion(muiRenderInfo(obj), region);
1118 DoSuperMethodA(cl, obj, (Msg) msg);
1120 if (region)
1122 MUI_RemoveClipRegion(muiRenderInfo(obj), clip);
1123 region = NULL;
1126 else
1128 DoSuperMethodA(cl, obj, (Msg) msg);
1130 /* D(bug("Group_Draw(%p) (after dsma) msg=0x%08lx flags=0x%08lx\n", */
1131 /* obj, msg->flags, _flags(obj))); */
1133 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 1)
1136 * update is set when changing active page of a page group
1137 * need to redraw background ourself
1139 DoMethod(obj, MUIM_DrawBackground,
1140 _mleft(obj), _mtop(obj), _mwidth(obj), _mheight(obj),
1141 _mleft(obj), _mtop(obj), 0);
1143 data->update = 0;
1145 else
1147 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 2)
1149 LONG left, top, right, bottom;
1150 LONG diff_virt_offx = data->virt_offx - data->old_virt_offx;
1151 LONG diff_virt_offy = data->virt_offy - data->old_virt_offy;
1152 struct Rectangle rect;
1153 struct Rectangle *clip_rect = &muiRenderInfo(obj)->mri_ClipRect;
1155 data->update = 0;
1157 if (!diff_virt_offx && !diff_virt_offy)
1159 return 1;
1162 /* sba: I don't know how MUI handle this but ScrollRasterBF() made problems when scrolling
1163 ** a (partly visible) virtual groups in a virtual group, because e.g. _mtop() is then
1164 ** smaller than the region. ScrollRasterBF() on AmigaOS then marks the complete region
1165 ** as damaged. Using ScrollWindowRaster() solved that problem but it flickers then.
1166 ** To avoid this we prevent that the scroll area is out of the region bounds.
1167 ** The region bounds are setted in MUI_Redraw() but should probably should go in the
1168 ** MUI's clip functions
1171 left = MAX(_mleft(obj), clip_rect->MinX);
1172 top = MAX(_mtop(obj), clip_rect->MinY);
1173 right = MIN(_mright(obj), clip_rect->MaxX);
1174 bottom = MIN(_mbottom(obj), clip_rect->MaxY);
1176 /* old code was
1177 ** ScrollRasterBF(_rp(obj), diff_virt_offx, diff_virt_offy, _mleft(obj), _mtop(obj), _mright(obj),_mbottom(obj));
1180 ScrollWindowRaster(_window(obj), diff_virt_offx, diff_virt_offy,
1181 left, top, right, bottom);
1183 if ((region = NewRegion()))
1185 if (diff_virt_offx)
1187 rect.MinY = top;
1188 rect.MaxY = bottom;
1190 if (diff_virt_offx > 0)
1192 rect.MinX = right - diff_virt_offx + 1;
1193 if (rect.MinX < left)
1194 rect.MinX = left;
1195 rect.MaxX = right;
1197 else
1199 rect.MinX = left;
1200 rect.MaxX = left - diff_virt_offx - 1;
1201 if (rect.MaxX > right)
1202 rect.MaxX = right;
1205 if (rect.MinX <= rect.MaxX)
1207 DoMethod(obj, MUIM_DrawBackground,
1208 rect.MinX, rect.MinY,
1209 rect.MaxX - rect.MinX + 1,
1210 rect.MaxY - rect.MinY + 1,
1211 rect.MinX, rect.MinY, 0);
1213 OrRectRegion(region, &rect);
1217 if (diff_virt_offy)
1219 rect.MinX = left;
1220 rect.MaxX = right;
1222 if (diff_virt_offy > 0)
1224 rect.MinY = bottom - diff_virt_offy + 1;
1225 if (rect.MinY < top)
1226 rect.MinY = top;
1227 rect.MaxY = bottom;
1229 else
1231 rect.MinY = top;
1232 rect.MaxY = top - diff_virt_offy - 1;
1233 if (rect.MaxY > bottom)
1234 rect.MaxY = bottom;
1236 if (rect.MinY <= rect.MaxY)
1238 DoMethod(obj, MUIM_DrawBackground,
1239 rect.MinX, rect.MinY,
1240 rect.MaxX - rect.MinX + 1,
1241 rect.MaxY - rect.MinY + 1,
1242 rect.MinX, rect.MinY, 0);
1244 OrRectRegion(region, &rect);
1250 else
1252 if (!(msg->flags & MADF_DRAWOBJECT)
1253 && !(msg->flags & MADF_DRAWALL))
1254 return TRUE;
1258 if (data->flags & GROUP_VIRTUAL && !region)
1260 /* Not really needed if MUI Draws all the objects, maybe that's
1261 * what DRAWALL is for??? */
1262 if ((region = NewRegion()))
1264 struct Rectangle rect;
1265 rect.MinX = _mleft(obj);
1266 rect.MinY = _mtop(obj);
1267 rect.MaxX = _mright(obj);
1268 rect.MaxY = _mbottom(obj);
1269 OrRectRegion(region, &rect);
1273 /* Add clipping region if we have one */
1274 if (region)
1275 clip = MUI_AddClipRegion(muiRenderInfo(obj), region);
1277 group_rect = muiRenderInfo(obj)->mri_ClipRect;
1278 page = -1;
1279 get(data->family, MUIA_Family_List, &(ChildList));
1280 cstate = (Object *) ChildList->mlh_Head;
1281 while ((child = NextObject(&cstate)))
1283 if (!(_flags(child) & MADF_SHOWME))
1284 continue;
1286 if (child != data->titlegroup)
1287 ++page;
1289 if ((data->flags & GROUP_PAGEMODE) && ((page != data->active_page)
1290 && (child != data->titlegroup)))
1292 continue;
1295 // msg->flags |= MADF_DRAWOBJECT; /* yup, do not forget */
1297 // child_rect.MinX = _left(child);
1298 // child_rect.MinY = _top(child);
1299 // child_rect.MaxX = _right(child);
1300 // child_rect.MaxY = _bottom(child);
1301 /* g_print("intersect: a=(%d, %d, %d, %d) b=(%d, %d, %d, %d)\n", */
1302 /* group_rect.x, group_rect.y, */
1303 /* group_rect.width, group_rect.height, */
1304 /* child_rect.x, child_rect.y, */
1305 /* child_rect.width, child_rect.height); */
1307 // if (gdk_rectangle_intersect(&group_rect, &child_rect,
1308 // &muiRenderInfo(obj)->mri_ClipRect))
1309 // DoMethodA(child, (Msg)msg);
1310 /* if (((msg->flags & MADF_DRAWUPDATE) && data->update) */
1311 /* || (data->flags & GROUP_PAGEMODE)) */
1312 MUI_Redraw(child, MADF_DRAWOBJECT);
1313 /* else */
1314 /* MUI_Redraw(child, msg->flags); */
1315 muiRenderInfo(obj)->mri_ClipRect = group_rect;
1316 /* g_print("set back clip to (%d, %d, %d, %d)\n", */
1317 /* group_rect.x, group_rect.y, group_rect.width, group_rect.height); */
1319 /* D(bug("Group_Draw(%p) end\n", obj)); */
1321 if (data->flags & GROUP_VIRTUAL && region && clip != (APTR) - 1)
1323 MUI_RemoveClipRegion(muiRenderInfo(obj), clip);
1326 data->old_virt_offx = data->virt_offx;
1327 data->old_virt_offy = data->virt_offy;
1328 data->update = 0;
1330 return TRUE;
1334 #define END_MINMAX() \
1335 tmp.MaxHeight = MAX(tmp.MaxHeight, tmp.MinHeight); \
1336 tmp.MaxWidth = MAX(tmp.MaxWidth, tmp.MinWidth); \
1337 tmp.DefHeight = CLAMP(tmp.DefHeight, tmp.MinHeight, tmp.MaxHeight); \
1338 tmp.DefWidth = CLAMP(tmp.DefWidth, tmp.MinWidth, tmp.MaxWidth); \
1339 msg->MinMaxInfo->MinWidth += tmp.MinWidth; \
1340 msg->MinMaxInfo->MinHeight += tmp.MinHeight; \
1341 msg->MinMaxInfo->MaxWidth += tmp.MaxWidth; \
1342 msg->MinMaxInfo->MaxHeight += tmp.MaxHeight; \
1343 msg->MinMaxInfo->DefWidth += tmp.DefWidth; \
1344 msg->MinMaxInfo->DefHeight += tmp.DefHeight;
1347 * MinMax calculation function. When this is called,
1348 * the children of your group have already been asked
1349 * about their min/max dimension so you can use their
1350 * dimensions to calculate yours.
1352 * Notes:
1353 * - Init minwidth and maxwidth with size needed for total child spacing.
1354 * - 1st pass to find maximum minimum width, to set minwidth of each child
1355 * if they should have the same width (for a row of buttons ...)
1356 * - Adjust minwidth w/o making object bigger than their max size.
1358 static void group_minmax_horiz(struct IClass *cl, Object *obj,
1359 struct MinList *children, struct MUIP_AskMinMax *msg)
1361 struct MUI_GroupData *data = INST_DATA(cl, obj);
1362 Object *cstate;
1363 Object *child;
1364 struct MUI_MinMax tmp;
1365 WORD maxminwidth = 0;
1366 BOOL found_nonzero_vweight = FALSE;
1368 tmp.MinHeight = 0;
1369 tmp.DefHeight = 0;
1370 tmp.MaxHeight = MUI_MAXMAX;
1371 tmp.MinWidth = tmp.DefWidth = tmp.MaxWidth =
1372 (data->num_visible_children - 1) * data->horiz_spacing;
1374 if (data->flags & GROUP_SAME_WIDTH)
1376 cstate = (Object *) children->mlh_Head;
1377 while ((child = NextObject(&cstate)))
1379 if (IS_HIDDEN(child))
1380 continue;
1381 maxminwidth = MAX(maxminwidth, _minwidth(child));
1385 data->samesize_maxmin_horiz = maxminwidth;
1386 /* D(bug("group_minmax_horiz(%p) : maxminwidth=%d\n", obj, maxminwidth)); */
1388 data->horiz_weight_sum = 0;
1389 cstate = (Object *) children->mlh_Head;
1390 while ((child = NextObject(&cstate)))
1392 WORD minwidth;
1394 if (IS_HIDDEN(child))
1395 continue;
1396 if (data->flags & GROUP_SAME_WIDTH)
1398 minwidth = MAX(maxminwidth, _minwidth(child));
1399 minwidth = MIN(minwidth, _maxwidth(child));
1401 else
1402 minwidth = _minwidth(child);
1403 tmp.MinWidth += minwidth;
1404 tmp.DefWidth += w0_defwidth(child);
1405 tmp.MaxWidth += w0_maxwidth(child);
1406 tmp.MaxWidth = MIN(tmp.MaxWidth, MUI_MAXMAX);
1407 tmp.MinHeight = MAX(tmp.MinHeight, _minheight(child));
1408 tmp.DefHeight = MAX(tmp.DefHeight, _defheight(child));
1410 if all children have null weight then maxheight=minheight
1411 if all but some children have null weights, the maxheight
1412 is the min of all maxheights
1414 tmp.MaxHeight = MIN(tmp.MaxHeight, _maxheight(child));
1415 data->horiz_weight_sum += _hweight(child);
1416 if (_vweight(child) > 0)
1418 found_nonzero_vweight = TRUE;
1421 if (!found_nonzero_vweight)
1423 tmp.MaxHeight = tmp.MinHeight;
1426 //if (data->flags & GROUP_VIRTUAL)
1428 //kprintf("# min %d x %d def %d x %d max %d x %d\n",
1429 // tmp.MinWidth, tmp.MinHeight,
1430 // tmp.DefWidth, tmp.DefHeight,
1431 // tmp.MaxWidth, tmp.MaxHeight);
1434 END_MINMAX();
1437 /* minmax calculation for vertical groups (see group_minmax_horiz)
1439 static void group_minmax_vert(struct IClass *cl, Object *obj,
1440 struct MinList *children, struct MUIP_AskMinMax *msg)
1442 struct MUI_GroupData *data = INST_DATA(cl, obj);
1443 Object *cstate;
1444 Object *child;
1445 struct MUI_MinMax tmp;
1446 WORD maxminheight = 0;
1447 BOOL found_nonzero_hweight = FALSE;
1449 tmp.MinWidth = 0;
1450 tmp.DefWidth = 0;
1451 tmp.MaxWidth = MUI_MAXMAX;
1452 tmp.MinHeight = tmp.DefHeight = tmp.MaxHeight =
1453 (data->num_visible_children - 1) * data->vert_spacing;
1455 if (data->flags & GROUP_SAME_HEIGHT)
1457 cstate = (Object *) children->mlh_Head;
1458 while ((child = NextObject(&cstate)))
1460 if (IS_HIDDEN(child))
1461 continue;
1462 maxminheight = MAX(maxminheight, _minheight(child));
1466 data->samesize_maxmin_vert = maxminheight;
1467 data->vert_weight_sum = 0;
1468 cstate = (Object *) children->mlh_Head;
1469 while ((child = NextObject(&cstate)))
1471 if (IS_HIDDEN(child))
1472 continue;
1474 if (data->flags & GROUP_SAME_HEIGHT)
1475 _minheight(child) = MIN(maxminheight, w0_maxheight(child));
1476 tmp.MinHeight += _minheight(child);
1477 tmp.DefHeight += w0_defheight(child);
1478 tmp.MaxHeight += w0_maxheight(child);
1479 tmp.MaxHeight = MIN(tmp.MaxHeight, MUI_MAXMAX);
1480 tmp.MinWidth = MAX(tmp.MinWidth, _minwidth(child));
1481 tmp.DefWidth = MAX(tmp.DefWidth, _defwidth(child));
1482 tmp.MaxWidth = MIN(tmp.MaxWidth, _maxwidth(child));
1483 data->vert_weight_sum += _vweight(child);
1484 if (_hweight(child) > 0)
1486 found_nonzero_hweight = TRUE;
1489 if (!found_nonzero_hweight)
1491 tmp.MaxWidth = tmp.MinWidth;
1494 END_MINMAX();
1498 static void
1499 minmax_2d_rows_pass(struct MUI_GroupData *data, struct MinList *children,
1500 struct MUI_MinMax *req, WORD maxmin_height, WORD maxdef_height)
1502 int i, j;
1503 Object *cstate;
1504 Object *child;
1506 /* do not rewind after the while, to process line by line */
1507 cstate = (Object *) children->mlh_Head;
1508 /* for each row */
1509 for (i = 0; i < data->rows; i++)
1511 /* calculate min and max height of this row */
1512 int min_h = 0, def_h = 0, max_h = MUI_MAXMAX;
1513 BOOL found_nonzero_vweight = FALSE;
1515 data->row_infos[i].weight = 0;
1517 j = 0;
1518 while ((child = NextObject(&cstate)))
1520 if (IS_HIDDEN(child))
1521 continue;
1522 if (data->flags & GROUP_SAME_HEIGHT)
1524 _minheight(child) = MIN(maxmin_height, w0_maxheight(child));
1525 _defheight(child) = MIN(maxdef_height, w0_maxheight(child));
1527 min_h = MAX(min_h, _minheight(child));
1528 def_h = MAX(def_h, w0_defheight(child));
1529 max_h = MIN(max_h, _maxheight(child));
1530 if (_vweight(child) > 0)
1532 found_nonzero_vweight = TRUE;
1533 data->row_infos[i].weight += _vweight(child);
1535 ++j;
1536 if ((j % data->columns) == 0)
1537 break;
1539 if (!found_nonzero_vweight)
1540 max_h = min_h;
1541 else
1542 max_h = MAX(max_h, min_h);
1543 /* D(bug("row %d : min_h=%d max_h=%d\n", i, min_h, max_h)); */
1545 data->row_infos[i].min = min_h;
1546 data->row_infos[i].max = max_h;
1547 data->vert_weight_sum += data->row_infos[i].weight;
1549 req->MinHeight += min_h;
1550 req->DefHeight += def_h;
1551 req->MaxHeight += max_h;
1552 if (req->MaxHeight > MUI_MAXMAX)
1553 req->MaxHeight = MUI_MAXMAX;
1558 static void
1559 minmax_2d_columns_pass(struct MUI_GroupData *data, struct MinList *children,
1560 struct MUI_MinMax *req, WORD maxmin_width, WORD maxdef_width)
1562 int i, j;
1563 Object *cstate;
1564 Object *child;
1566 for (i = 0; i < data->columns; i++)
1568 /* calculate min and max width of this column */
1569 int min_w = 0, def_w = 0, max_w = MUI_MAXMAX;
1570 BOOL found_nonzero_hweight = FALSE;
1572 data->col_infos[i].weight = 0;
1574 j = 0;
1575 /* process all children to get children on a column */
1576 cstate = (Object *) children->mlh_Head;
1577 while ((child = NextObject(&cstate)))
1579 if (IS_HIDDEN(child))
1580 continue;
1581 ++j;
1582 if (((j - 1) % data->columns) != i)
1583 continue;
1584 if (data->flags & GROUP_SAME_WIDTH)
1586 _minwidth(child) = MIN(maxmin_width, w0_maxwidth(child));
1587 _defwidth(child) = MIN(maxdef_width, w0_maxwidth(child));
1589 min_w = MAX(min_w, _minwidth(child));
1590 def_w = MAX(def_w, w0_defwidth(child));
1592 /* this handles the case of null weight children, which limit
1593 * the max size if they're alone, but not if they are with
1594 * non-null weight obj
1596 max_w = MIN(max_w, _maxwidth(child));
1597 if (_hweight(child) > 0)
1599 found_nonzero_hweight = TRUE;
1600 data->col_infos[i].weight += _hweight(child);
1603 if (!found_nonzero_hweight)
1604 max_w = min_w;
1605 else
1606 max_w = MAX(max_w, min_w);
1607 /* D(bug("col %d : min_w=%d max_w=%d\n", i, min_w, max_w)); */
1609 data->col_infos[i].min = min_w;
1610 data->col_infos[i].max = max_w;
1611 data->horiz_weight_sum += data->col_infos[i].weight;
1613 req->MinWidth += min_w;
1614 req->DefWidth += def_w;
1615 req->MaxWidth += max_w;
1616 if (req->MaxWidth > MUI_MAXMAX)
1617 req->MaxWidth = MUI_MAXMAX;
1621 static void
1622 group_minmax_2d(struct IClass *cl, Object *obj,
1623 struct MinList *children, struct MUIP_AskMinMax *msg)
1625 struct MUI_GroupData *data = INST_DATA(cl, obj);
1626 Object *cstate;
1627 Object *child;
1628 struct MUI_MinMax tmp;
1629 WORD maxmin_width;
1630 WORD maxmin_height;
1631 WORD maxdef_width;
1632 WORD maxdef_height;
1634 if (!data->columns)
1636 if (data->num_children % data->rows)
1638 data->columns = 1;
1639 data->rows = data->num_children;
1641 else
1642 data->columns = data->num_children / data->rows;
1644 else
1646 if (data->num_children % data->columns)
1648 data->rows = 1;
1649 data->columns = data->num_children;
1651 else
1652 data->rows = data->num_children / data->columns;
1655 if (data->columns < 1)
1656 data->columns = 1;
1657 if (data->rows < 1)
1658 data->rows = 1;
1660 if (data->row_infos != NULL)
1661 mui_free(data->row_infos);
1663 data->row_infos = mui_alloc(data->rows * sizeof(struct layout2d_elem));
1664 if (NULL == data->row_infos)
1665 return;
1667 if (data->col_infos != NULL)
1668 mui_free(data->col_infos);
1670 data->col_infos =
1671 mui_alloc(data->columns * sizeof(struct layout2d_elem));
1672 if (NULL == data->col_infos)
1673 return;
1675 data->horiz_weight_sum = 0;
1676 data->vert_weight_sum = 0;
1678 tmp.MinHeight = tmp.DefHeight = tmp.MaxHeight =
1679 (data->rows - 1) * data->vert_spacing;
1680 tmp.MinWidth = tmp.DefWidth = tmp.MaxWidth =
1681 (data->columns - 1) * data->horiz_spacing;
1682 /* get minimum dims if same dims for all children are needed */
1683 maxmin_width = 0;
1684 maxmin_height = 0;
1685 maxdef_width = 0;
1686 maxdef_height = 0;
1688 if ((data->flags & GROUP_SAME_WIDTH)
1689 || (data->flags & GROUP_SAME_HEIGHT))
1691 cstate = (Object *) children->mlh_Head;
1692 while ((child = NextObject(&cstate)))
1694 if (!(_flags(child) & MADF_SHOWME))
1695 continue;
1696 maxmin_width = MAX(maxmin_width, _minwidth(child));
1697 maxmin_height = MAX(maxmin_height, _minheight(child));
1698 maxdef_width = MAX(maxdef_width, w0_defwidth(child));
1699 maxdef_height = MAX(maxdef_height, w0_defheight(child));
1701 /* g_print("2d group: mminw=%d mminh=%d\n", */
1702 /* maxmin_width, maxmin_height); */
1704 if (data->flags & GROUP_SAME_HEIGHT)
1705 data->samesize_maxmin_vert = maxmin_height;
1706 else
1707 data->samesize_maxmin_vert = 0;
1709 if (data->flags & GROUP_SAME_WIDTH)
1710 data->samesize_maxmin_horiz = maxmin_width;
1711 else
1712 data->samesize_maxmin_horiz = 0;
1714 minmax_2d_rows_pass(data, children, &tmp, maxmin_height, maxdef_height);
1715 minmax_2d_columns_pass(data, children, &tmp, maxmin_width,
1716 maxdef_width);
1718 END_MINMAX();
1722 static void
1723 group_minmax_pagemode(struct IClass *cl, Object *obj,
1724 struct MinList *children, struct MUIP_AskMinMax *msg)
1726 Object *cstate;
1727 Object *child;
1728 struct MUI_GroupData *data = INST_DATA(cl, obj);
1729 struct MUI_MinMax tmp = { 0, 0, MUI_MAXMAX, MUI_MAXMAX, 0, 0 };
1731 cstate = (Object *) children->mlh_Head;
1733 D(bug("minmax_pagemode(%lx)\n", obj, tmp.DefWidth));
1735 while ((child = NextObject(&cstate)))
1737 if (!(_flags(child) & MADF_SHOWME))
1738 continue;
1740 if (child == data->titlegroup)
1741 continue;
1743 tmp.MinHeight = MAX(tmp.MinHeight, _minheight(child));
1744 D(bug("minmax_pagemode(%p) minh child = %d tmpmin=%d\n", obj,
1745 _minheight(child), tmp.MinHeight));
1746 tmp.MinWidth = MAX(tmp.MinWidth, _minwidth(child));
1747 tmp.MaxHeight = MIN(tmp.MaxHeight, w0_maxheight(child));
1748 tmp.MaxWidth = MIN(tmp.MaxWidth, w0_maxwidth(child));
1749 tmp.DefHeight = MAX(tmp.DefHeight,
1750 ((w0_defheight(child) <
1751 MUI_MAXMAX) ? w0_defheight(child) : tmp.DefHeight));
1752 tmp.DefWidth =
1753 MAX(tmp.DefWidth,
1754 ((w0_defwidth(child) <
1755 MUI_MAXMAX) ? w0_defwidth(child) : tmp.DefWidth));
1756 D(bug("minmax_pagemode(%lx) defw = %ld\n", obj, tmp.DefWidth));
1759 if (data->titlegroup)
1761 tmp.MinHeight += _minheight(data->titlegroup);
1762 tmp.MaxHeight += w0_maxheight(data->titlegroup);
1763 tmp.DefHeight += w0_defheight(data->titlegroup);
1766 END_MINMAX();
1769 /**************************************************************************
1770 MUIM_AskMinMax : ask children about min/max sizes, then
1771 either call a hook, or the builtin method, to calculate our minmax
1772 **************************************************************************/
1773 IPTR Group__MUIM_AskMinMax(struct IClass *cl, Object *obj,
1774 struct MUIP_AskMinMax *msg)
1776 struct MUI_GroupData *data = INST_DATA(cl, obj);
1777 struct MUI_LayoutMsg lm;
1778 struct MUIP_AskMinMax childMsg;
1779 struct MUI_MinMax childMinMax;
1780 Object *cstate;
1781 Object *child;
1782 LONG super_minwidth, super_minheight;
1785 * let our superclass first fill in its size with frame, inner spc etc ...
1787 DoSuperMethodA(cl, obj, (Msg) msg);
1788 super_minwidth = msg->MinMaxInfo->MinWidth;
1789 super_minheight = msg->MinMaxInfo->MinHeight;
1792 * Ask children
1794 childMsg.MethodID = msg->MethodID;
1795 childMsg.MinMaxInfo = &childMinMax;
1796 get(data->family, MUIA_Family_List, &(lm.lm_Children));
1798 cstate = (Object *) lm.lm_Children->mlh_Head;
1800 while ((child = NextObject(&cstate)))
1802 if (!(_flags(child) & MADF_SHOWME))
1803 /* BORDERGADGETs should handle this itself */
1804 continue;
1805 /* Ask child */
1806 DoMethodA(child, (Msg) & childMsg);
1807 /* D(bug("*** group %lx, child %lx min=%ld,%ld\n", */
1808 /* obj, child, childMinMax.MinWidth, childMinMax.MinHeight)); */
1809 __area_finish_minmax(child, childMsg.MinMaxInfo);
1813 * Use children infos to calculate group size
1815 if (data->flags & GROUP_PAGEMODE)
1817 D(bug("minmax_pagemode(%p) minh initial = %d\n", obj,
1818 msg->MinMaxInfo->MinHeight));
1819 group_minmax_pagemode(cl, obj, lm.lm_Children, msg);
1820 D(bug("minmax_pagemode(%p) minh = %d\n", obj,
1821 msg->MinMaxInfo->MinHeight));
1823 else if (data->layout_hook)
1825 lm.lm_Type = MUILM_MINMAX;
1826 CallHookPkt(data->layout_hook, obj, &lm);
1828 if (lm.lm_MinMax.MaxHeight < lm.lm_MinMax.MinHeight)
1829 lm.lm_MinMax.MaxHeight = lm.lm_MinMax.MinHeight;
1830 if (lm.lm_MinMax.DefHeight < lm.lm_MinMax.MinHeight)
1831 lm.lm_MinMax.DefHeight = lm.lm_MinMax.MinHeight;
1832 if (lm.lm_MinMax.MaxWidth < lm.lm_MinMax.MinWidth)
1833 lm.lm_MinMax.MaxWidth = lm.lm_MinMax.MinWidth;
1834 if (lm.lm_MinMax.DefWidth < lm.lm_MinMax.MinWidth)
1835 lm.lm_MinMax.DefWidth = lm.lm_MinMax.MinWidth;
1837 //kprintf("### min %d x %d def %d x %d max %d x %d\n",
1838 // msg->MinMaxInfo->MinWidth,
1839 // msg->MinMaxInfo->MinHeight,
1840 // msg->MinMaxInfo->DefWidth,
1841 // msg->MinMaxInfo->DefHeight,
1842 // msg->MinMaxInfo->MaxWidth,
1843 // msg->MinMaxInfo->MaxHeight);
1845 msg->MinMaxInfo->MinWidth += lm.lm_MinMax.MinWidth;
1846 msg->MinMaxInfo->MinHeight += lm.lm_MinMax.MinHeight;
1847 msg->MinMaxInfo->MaxWidth += lm.lm_MinMax.MaxWidth;
1848 if (msg->MinMaxInfo->MaxWidth > MUI_MAXMAX)
1849 msg->MinMaxInfo->MaxWidth = MUI_MAXMAX;
1850 msg->MinMaxInfo->MaxHeight += lm.lm_MinMax.MaxHeight;
1851 if (msg->MinMaxInfo->MaxHeight > MUI_MAXMAX)
1852 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1853 msg->MinMaxInfo->DefWidth += lm.lm_MinMax.DefWidth;
1854 msg->MinMaxInfo->DefHeight += lm.lm_MinMax.DefHeight;
1856 //kprintf("#### min %d x %d def %d x %d max %d x %d\n",
1857 // msg->MinMaxInfo->MinWidth,
1858 // msg->MinMaxInfo->MinHeight,
1859 // msg->MinMaxInfo->DefWidth,
1860 // msg->MinMaxInfo->DefHeight,
1861 // msg->MinMaxInfo->MaxWidth,
1862 // msg->MinMaxInfo->MaxHeight);
1865 else
1867 if ((data->rows == 1) && (data->columns == 1))
1869 data->num_visible_children =
1870 Group_GetNumVisibleChildren(data, lm.lm_Children);
1871 if (data->flags & GROUP_HORIZ)
1872 group_minmax_horiz(cl, obj, lm.lm_Children, msg);
1873 else
1874 group_minmax_vert(cl, obj, lm.lm_Children, msg);
1876 else
1878 group_minmax_2d(cl, obj, lm.lm_Children, msg);
1882 if (data->flags & GROUP_VIRTUAL)
1884 data->saved_minwidth = msg->MinMaxInfo->MinWidth;
1885 data->saved_minheight = msg->MinMaxInfo->MinHeight;
1886 msg->MinMaxInfo->MinWidth = super_minwidth + 2;
1887 msg->MinMaxInfo->MinHeight = super_minheight + 2;
1889 //kprintf("## min %d x %d def %d x %d max %d x %d\n",
1890 // msg->MinMaxInfo->MinWidth,
1891 // msg->MinMaxInfo->MinHeight,
1892 // msg->MinMaxInfo->DefWidth,
1893 // msg->MinMaxInfo->DefHeight,
1894 // msg->MinMaxInfo->MaxWidth,
1895 // msg->MinMaxInfo->MaxHeight);
1898 return 0;
1903 // enforce minmax constraint, but also update total growable/shrinkable weights
1904 // while we're at it
1905 static void Layout1D_minmax_constraint(WORD *sizep, WORD minsize,
1906 WORD maxsize, WORD *remainp, WORD *sizegrowp, WORD *sizeshrinkp,
1907 ULONG *weightgrowp, ULONG *weightshrinkp, UWORD weight, WORD samesize)
1909 WORD size = *sizep, remain = *remainp,
1910 sizegrow = *sizegrowp, sizeshrink = *sizeshrinkp;
1911 ULONG weightgrow = *weightgrowp, weightshrink = *weightshrinkp;
1913 /* D(bug("L1D_minmax_c size=%d min=%d max=%d w=%d ss=%d\n", */
1914 /* size, minsize, maxsize, */
1915 /* weight, samesize)); */
1917 if ((samesize > 0) && (weight == 0))
1919 remain += size - samesize;
1920 size = samesize;
1921 sizeshrink -= size;
1922 sizegrow -= size;
1925 if (size <= minsize) // too little
1927 remain += size - minsize;
1928 size = minsize;
1929 sizeshrink -= size;
1930 if (size == maxsize)
1931 sizegrow -= size;
1933 else if (size >= maxsize) // too big
1935 remain += size - maxsize;
1936 size = maxsize;
1937 sizegrow -= size;
1940 if (!((samesize > 0) && (weight == 0)))
1942 if (size < maxsize)
1943 weightgrow += weight;
1944 if (size > minsize)
1945 weightshrink += weight;
1948 *sizep = size;
1949 *remainp = remain;
1950 *sizegrowp = sizegrow;
1951 *sizeshrinkp = sizeshrink;
1952 *weightgrowp = weightgrow;
1953 *weightshrinkp = weightshrink;
1957 // redistribute excess size to growable child, or reduce size of a shrinkable
1958 // child
1959 static void Layout1D_redistribution(WORD *sizep, WORD minsize,
1960 WORD maxsize, WORD *remainp, WORD *sizegrowp, WORD *sizeshrinkp,
1961 ULONG *weightgrowp, ULONG *weightshrinkp, UWORD weight)
1963 WORD size = *sizep, remain = *remainp,
1964 sizegrow = *sizegrowp, sizeshrink = *sizeshrinkp;
1965 ULONG weightgrow = *weightgrowp, weightshrink = *weightshrinkp;
1967 if (weight == 0)
1968 return;
1970 if ((remain > 0) && (size < maxsize))
1972 LONG newsize;
1974 newsize = (sizegrow * weight + weightgrow / 2) / weightgrow;
1976 /* D(bug("newsize=%ld == size_growa=%ld * w=%ld / weight_grow=%d\n", */
1977 /* newsize, sizegrow, weight, weightgrow)); */
1979 /* take care of off-by-1 errors that may toggle remainder sign
1980 * by ensuring convergence to 0
1982 if (remain - newsize + size < 0)
1984 /* D(bug("adding remainder=%d => size = %d\n", */
1985 /* remain, size + remain)); */
1986 size += remain;
1987 remain = 0;
1989 else
1991 remain -= newsize - size;
1992 size = newsize;
1993 sizegrow -= size;
1994 weightgrow -= weight;
1997 else if ((remain < 0) && (size > minsize))
1999 LONG newsize;
2001 newsize = (sizeshrink * weight + weightshrink / 2) / weightshrink;
2003 /* D(bug("newsize=%ld == size_shrinkables=%ld * w=%ld " */
2004 /* "/ weight_shrinkables=%d\n", */
2005 /* newsize, sizeshrink, weight, weightshrink)); */
2007 if (remain - newsize + size > 0)
2009 /* D(bug("adding remainder=%d => size = %d\n", */
2010 /* remain, size + remain)); */
2011 size += remain;
2012 remain = 0;
2014 else
2016 remain -= newsize - size;
2017 size = newsize;
2018 sizeshrink -= size;
2019 weightshrink -= weight;
2023 *sizep = size;
2024 *remainp = remain;
2025 *sizegrowp = sizegrow;
2026 *sizeshrinkp = sizeshrink;
2027 *weightgrowp = weightgrow;
2028 *weightshrinkp = weightshrink;
2032 // 2 passes at most, less on average (0.5 or 1.5), each does
2033 // - a minmax clamping, evenutally adding to a remainder
2034 // (remainder = missing (underflow) or remaining (overflow) space compared
2035 // to ideal sizes where children fill the whole group)
2036 // - a redistribution of the remainder, by growing (pos. remainder) or
2037 // shrinking (neg. remainder) children able to support it.
2039 // Occasionnaly the first time the redistribution is done, the minmax
2040 // constraint can be broken, thus the extra pass to check and eventually
2041 // redistribute. The second redistribution never breaks minmax constraint
2042 // (there should be a mathematical proof, but feel free to prove me wrong
2043 // with an example)
2044 static void Layout1D_minmax_constraints_and_redistrib(struct MinList
2045 *children, WORD total_size, WORD remainder, WORD samesize,
2046 BOOL group_horiz)
2048 Object *cstate;
2049 Object *child;
2050 int i;
2052 for (i = 0; i < 2; i++)
2054 WORD size_growables = total_size;
2055 WORD size_shrinkables = total_size;
2056 ULONG weight_growables = 0;
2057 ULONG weight_shrinkables = 0;
2059 /* D(bug("start : rem=%ld, A=%ld, size_growables=%ld, " */
2060 /* "size_shrinkables=%ld\n", */
2061 /* remainder, total_size, size_growables, size_shrinkables)); */
2063 // minmax constraints
2064 cstate = (Object *) children->mlh_Head;
2065 while ((child = NextObject(&cstate)))
2067 /* WORD old_size; */
2069 if (IS_HIDDEN(child))
2070 continue;
2072 if (group_horiz)
2074 /* old_size = _width(child); */
2076 Layout1D_minmax_constraint(&_width(child), _minwidth(child),
2077 _maxwidth(child), &remainder, &size_growables,
2078 &size_shrinkables, &weight_growables,
2079 &weight_shrinkables, _hweight(child), samesize);
2081 /* D(bug("loop1 on %p : width=%d was %d, rem=%d, A=%d, " */
2082 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2083 /* child, _width(child), old_size, remainder, total_size, */
2084 /* size_growables, size_shrinkables, _hweight(child), */
2085 /* _minwidth(child), _maxwidth(child))); */
2087 else // ! group_horiz
2089 /* old_size = _height(child); */
2091 Layout1D_minmax_constraint(&_height(child),
2092 _minheight(child), _maxheight(child), &remainder,
2093 &size_growables, &size_shrinkables, &weight_growables,
2094 &weight_shrinkables, _vweight(child), samesize);
2096 /* D(bug("loop1 on %p : h=%ld was %d, rem=%d, A=%d, " */
2097 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2098 /* child, _height(child), old_size, remainder, total_size,*/
2099 /* size_growables, size_shrinkables, _vweight(child), */
2100 /* _minheight(child), _maxheight(child))); */
2101 } // if (group_horiz)
2102 } // while child, minmax constraints
2104 // mid-pass break
2105 if (remainder == 0)
2106 break;
2108 /* D(bug("mid : rem=%d, A=%d, size_grow=%d, size_shrink=%d, " */
2109 /* "wg=%ld, ws=%ld\n", remainder, total_size, size_growables, */
2110 /* size_shrinkables, weight_growables, weight_shrinkables)); */
2112 // distribute remaining space to possible candidates
2113 cstate = (Object *) children->mlh_Head;
2114 while (((child = NextObject(&cstate)) != NULL) && (remainder != 0))
2116 /* WORD old_size; */
2118 if (IS_HIDDEN(child))
2119 continue;
2121 if (group_horiz)
2123 /* old_size = _width(child); */
2125 Layout1D_redistribution(&_width(child), _minwidth(child),
2126 _maxwidth(child), &remainder, &size_growables,
2127 &size_shrinkables, &weight_growables,
2128 &weight_shrinkables, _hweight(child));
2130 /* D(bug("loop2 on %p : h=%d was %d, rem=%d, A=%d, " */
2131 /* "size_grow=%d, size_shrink=%d\n", child, */
2132 /* _width(child), old_size, remainder, total_size, */
2133 /* size_growables, size_shrinkables)); */
2135 else // ! group_horiz
2137 /* old_size = _height(child); */
2139 Layout1D_redistribution(&_height(child), _minheight(child),
2140 _maxheight(child), &remainder, &size_growables,
2141 &size_shrinkables, &weight_growables,
2142 &weight_shrinkables, _vweight(child));
2144 /* D(bug("loop2 on %p : h=%d was %d, rem=%d, A=%d, " */
2145 /* "size_grow=%d, size_shrink=%d\n", child, */
2146 /* _height(child), old_size, remainder, total_size, */
2147 /* size_growables, size_shrinkables)); */
2148 } // if (group_horiz)
2150 } // while child, redistribution
2152 /* if (remainder != 0) */
2153 /* { */
2154 /* D(bug("end : rem=%ld, A=%ld, size_grow=%ld, size_shrink=%ld\n", */
2155 /* remainder, total_size, size_growables, size_shrinkables)); */
2156 /* } */
2157 // dont break here if remainder == 0, some minmax constraints
2158 // may not be respected
2159 } // end for
2161 // to easily spot layout bugs, nothing like a (division by zero) exception
2162 /* if (remainder != 0) */
2163 /* { */
2164 /* ASSERT(remainder != 0); */
2165 /* D(bug("gonna crash, remainder = %d\n", remainder)); */
2166 /* remainder /= 0; */
2167 /* } */
2171 static void Layout1D_weight_constraint(WORD *total_sizep,
2172 WORD *total_init_sizep,
2173 ULONG *total_weightp, WORD *sizep, UWORD weight, WORD minsize)
2175 if (*total_weightp > 0)
2176 *sizep = (*total_sizep * weight + *total_weightp / 2)
2177 / *total_weightp;
2178 else
2179 *sizep = minsize;
2181 *total_weightp -= weight;
2182 *total_sizep -= *sizep;
2183 *total_init_sizep += *sizep;
2187 static void group_layout_vert(struct IClass *cl, Object *obj,
2188 struct MinList *children)
2190 struct MUI_GroupData *data = INST_DATA(cl, obj);
2191 Object *cstate;
2192 Object *child;
2193 ULONG total_weight;
2194 WORD remainder; /* must converge to 0 to successfully end layout */
2195 WORD total_size;
2196 WORD total_size_backup;
2197 WORD total_init_size; /* total size of the ideally sized children */
2198 WORD width;
2199 WORD left = 0;
2200 WORD top = 0;
2201 WORD layout_width;
2202 WORD layout_height;
2204 //kprintf("group_layout_vert: virtoff = %d,%d\n",
2205 // data->virt_offx, data->virt_offy);
2207 if (data->flags & GROUP_VIRTUAL)
2209 data->virt_mwidth =
2210 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2211 _maxwidth(obj) - _subwidth(obj));
2212 data->virt_mheight =
2213 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2214 _maxheight(obj) - _subheight(obj));
2216 layout_width = data->virt_mwidth;
2217 layout_height = data->virt_mheight;
2219 else
2221 layout_width = _mwidth(obj);
2222 layout_height = _mheight(obj);
2225 total_weight = data->vert_weight_sum;
2226 total_init_size = 0;
2227 total_size =
2228 layout_height - (data->num_visible_children -
2229 1) * data->vert_spacing;
2230 total_size_backup = total_size;
2232 /* D(bug("\nvert layout for %p, A=%d W=%ld\n", */
2233 /* obj, total_size, total_weight)); */
2235 // weight constraints
2236 // calculate ideal size for each object, and total ideal size
2237 cstate = (Object *) children->mlh_Head;
2238 while ((child = NextObject(&cstate)))
2240 if (IS_HIDDEN(child))
2241 continue;
2243 Layout1D_weight_constraint(&total_size, &total_init_size,
2244 &total_weight, &_height(child), _vweight(child),
2245 _minheight(child));
2246 /* D(bug("child %p : ideal=%d w=%ld\n", */
2247 /* child, _height(child), _vweight(child))); */
2248 } // while child, weight constraints
2250 total_size = total_size_backup;
2251 remainder = total_size - total_init_size;
2253 if (data->flags & GROUP_VIRTUAL)
2255 /* This is also true for non virtual groups, but if this would be the
2256 ** case then there is a bug in the layout function
2258 if (total_size < 0)
2259 total_size = 0;
2262 Layout1D_minmax_constraints_and_redistrib(children,
2263 total_size,
2264 remainder,
2265 (data->flags & GROUP_SAME_HEIGHT) ? data->samesize_maxmin_vert : 0,
2266 FALSE);
2268 // do the layout
2269 cstate = (Object *) children->mlh_Head;
2270 while ((child = NextObject(&cstate)))
2272 if (IS_HIDDEN(child))
2273 continue;
2275 width = MIN(_maxwidth(child), layout_width);
2276 width = MAX(width, _minwidth(child));
2277 left = (layout_width - width) / 2;
2279 /* D(bug("child %p -> layout %d x %d\n", */
2280 /* child, width, _height(child))); */
2281 if (!MUI_Layout(child, left, top, width, _height(child), 0))
2282 return;
2283 top += data->vert_spacing + _height(child);
2289 static void group_layout_horiz(struct IClass *cl, Object *obj,
2290 struct MinList *children)
2292 struct MUI_GroupData *data = INST_DATA(cl, obj);
2293 Object *cstate;
2294 Object *child;
2295 ULONG total_weight;
2296 WORD remainder; /* must converge to 0 to succesfully end layout */
2297 WORD total_size;
2298 WORD total_size_backup;
2299 WORD total_init_size; /* total size of the ideally sized children */
2300 WORD height;
2301 WORD top = 0;
2302 WORD left = 0;
2303 WORD layout_width;
2304 WORD layout_height;
2306 //kprintf("group_layout_horiz: virtoff = %d,%d\n",
2307 // data->virt_offx, data->virt_offy);
2309 if (data->flags & GROUP_VIRTUAL)
2311 data->virt_mwidth =
2312 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2313 _maxwidth(obj) - _subwidth(obj));
2314 data->virt_mheight =
2315 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2316 _maxheight(obj) - _subheight(obj));
2318 layout_width = data->virt_mwidth;
2319 layout_height = data->virt_mheight;
2321 //kprintf("group_layout_horiz: layoutsize %d x %d "
2322 // " virtsize %d x %d msize %d x %d\n",
2323 // layout_width, layout_height, data->virt_mwidth,
2324 // data->virt_mheight,
2325 // _mwidth(obj), _mheight(obj));
2327 else
2329 layout_width = _mwidth(obj);
2330 layout_height = _mheight(obj);
2333 total_weight = data->horiz_weight_sum;
2334 total_init_size = 0;
2335 total_size =
2336 layout_width - (data->num_visible_children -
2337 1) * data->horiz_spacing;
2338 total_size_backup = total_size;
2340 /* D(bug("\nhoriz layout for %p, A=%d W=%ld\n", */
2341 /* obj, total_size, total_weight)); */
2343 // weight constraints
2344 // calculate ideal size for each object, and total ideal size
2345 cstate = (Object *) children->mlh_Head;
2346 while ((child = NextObject(&cstate)))
2348 if (IS_HIDDEN(child))
2349 continue;
2351 Layout1D_weight_constraint(&total_size, &total_init_size,
2352 &total_weight, &_width(child), _hweight(child),
2353 _minwidth(child));
2354 /* D(bug("child %p : ideal=%d w=%ld\n", */
2355 /* child, _width(child), _hweight(child))); */
2356 } // while child, weight constraints
2358 total_size = total_size_backup;
2359 if (data->horiz_weight_sum > 0)
2360 remainder = total_size - total_init_size;
2361 else
2362 remainder = 0;
2364 if (data->flags & GROUP_VIRTUAL)
2366 /* This is also true for non virtual groups, but if this would be the
2367 ** case then there is a bug in the layout function
2369 if (total_size < 0)
2370 total_size = 0;
2373 Layout1D_minmax_constraints_and_redistrib(children,
2374 total_size,
2375 remainder,
2376 (data->flags & GROUP_SAME_WIDTH) ? data->samesize_maxmin_horiz : 0,
2377 TRUE);
2379 // do the layout
2380 cstate = (Object *) children->mlh_Head;
2381 while ((child = NextObject(&cstate)))
2383 if (IS_HIDDEN(child))
2384 continue;
2386 height = MIN(_maxheight(child), layout_height);
2387 height = MAX(height, _minheight(child));
2388 top = (layout_height - height) / 2;
2390 /* D(bug("child %p -> layout %d x %d\n", */
2391 /* child, _width(child), height)); */
2392 if (!MUI_Layout(child, left, top, _width(child), height, 0))
2393 return;
2394 left += data->horiz_spacing + _width(child);
2400 static void Layout2D_weight_constraint(struct MUI_GroupData *data,
2401 struct layout2d_elem *row_infos,
2402 struct layout2d_elem *col_infos,
2403 WORD total_size_height, WORD total_size_width,
2404 WORD *total_init_height, WORD *total_init_width)
2406 int i;
2407 ULONG total_weight_vert = data->vert_weight_sum;
2408 ULONG total_weight_horiz = data->horiz_weight_sum;
2410 *total_init_height = 0;
2411 *total_init_width = 0;
2413 /* calc row heights */
2414 for (i = 0; i < data->rows; i++)
2416 if (total_weight_vert > 0)
2417 row_infos[i].dim =
2418 (total_size_height * row_infos[i].weight +
2419 total_weight_vert / 2) / total_weight_vert;
2420 else
2421 row_infos[i].dim = row_infos[i].min;
2423 /* D(bug("l2 row %d : ideal = %d with w=%d, A=%d, W=%d\n", */
2424 /* i, row_infos[i].dim, */
2425 /* row_infos[i].weight, total_size_height, total_weight_vert)); */
2427 total_weight_vert -= row_infos[i].weight;
2428 total_size_height -= row_infos[i].dim;
2429 *total_init_height += row_infos[i].dim;
2432 /* calc columns widths */
2433 for (i = 0; i < data->columns; i++)
2435 if (total_weight_horiz)
2436 col_infos[i].dim =
2437 (total_size_width * col_infos[i].weight +
2438 total_weight_horiz / 2) / total_weight_horiz;
2439 else
2440 col_infos[i].dim = col_infos[i].min;
2442 /* D(bug("l2 col %d : ideal = %d with w=%d, A=%d, W=%d\n", */
2443 /* i, col_infos[i].dim, */
2444 /* col_infos[i].weight, total_size_width, total_weight_horiz)); */
2446 total_weight_horiz -= col_infos[i].weight;
2447 total_size_width -= col_infos[i].dim;
2448 *total_init_width += col_infos[i].dim;
2454 static void Layout2D_minmax_constraints_and_redistrib(struct layout2d_elem
2455 *infos, WORD nitems, WORD total_size, WORD samesize, WORD remainder)
2457 int j;
2459 /* D(bug("L2D mc&r n=%d A=%d ss=%d rem=%d\n", */
2460 /* nitems, total_size, samesize, remainder)); */
2462 for (j = 0; j < 2; j++)
2464 WORD size_growables = total_size;
2465 WORD size_shrinkables = total_size;
2466 ULONG weight_growables = 0;
2467 ULONG weight_shrinkables = 0;
2468 /* WORD old_size; */
2469 int i;
2471 // minmax constraints
2472 for (i = 0; i < nitems; i++)
2474 /* old_size = infos[i].dim; */
2476 /* D(bug("bef loop1 on %d : size=%d, rem=%d, A=%d, " */
2477 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2478 /* i, infos[i].dim, remainder, total_size, */
2479 /* size_growables, size_shrinkables, infos[i].weight, */
2480 /* infos[i].min, infos[i].max)); */
2482 Layout1D_minmax_constraint(&infos[i].dim, infos[i].min,
2483 infos[i].max, &remainder, &size_growables,
2484 &size_shrinkables, &weight_growables, &weight_shrinkables,
2485 infos[i].weight, samesize);
2487 /* D(bug("loop1 on %d : size=%d was %d, rem=%d, A=%d, " */
2488 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2489 /* i, infos[i].dim, old_size, remainder, total_size, */
2490 /* size_growables, size_shrinkables, infos[i].weight, */
2491 /* infos[i].min, infos[i].max)); */
2494 if (remainder == 0)
2495 break;
2497 for (i = 0; i < nitems; i++)
2499 /* old_size = infos[i].dim; */
2501 /* D(bug("bef loop2 on %d : size=%d, rem=%d, A=%d, " */
2502 /* "size_grow=%d, size_shrink=%d\n", i, */
2503 /* infos[i].dim, remainder, total_size, */
2504 /* size_growables, size_shrinkables)); */
2506 Layout1D_redistribution(&infos[i].dim, infos[i].min,
2507 infos[i].max, &remainder, &size_growables,
2508 &size_shrinkables, &weight_growables, &weight_shrinkables,
2509 infos[i].weight);
2511 /* D(bug("loop2 on %d : size=%d was %d, rem=%d, A=%d, " */
2512 /* "size_grow=%d, size_shrink=%d\n", i, */
2513 /* infos[i].dim, old_size, remainder, total_size, */
2514 /* size_growables, size_shrinkables)); */
2519 static void
2520 layout_2d_distribute_space(struct MUI_GroupData *data,
2521 struct layout2d_elem *row_infos,
2522 struct layout2d_elem *col_infos,
2523 struct MinList *children, LONG left_start, LONG top_start)
2525 Object *cstate;
2526 Object *child;
2527 LONG left;
2528 LONG top;
2529 LONG col_width;
2530 LONG row_height;
2531 int i, j;
2534 * pass 2 : distribute space
2536 cstate = (Object *) children->mlh_Head;
2537 top = top_start;
2538 /* for each row */
2539 for (i = 0; i < data->rows; i++)
2541 /* left start for child layout in this row */
2542 left = left_start;
2544 /* max height for children in this row */
2545 row_height = row_infos[i].dim;
2546 j = 0;
2547 /* for each column */
2548 while ((child = NextObject(&cstate)))
2550 LONG cleft;
2551 LONG ctop;
2552 LONG cwidth;
2553 LONG cheight;
2555 if (IS_HIDDEN(child))
2556 continue;
2557 /* max width for children in this column */
2558 col_width = col_infos[j].dim;
2560 /* center child if col width is bigger than child maxwidth */
2561 cwidth = MIN(_maxwidth(child), col_width);
2562 cwidth = MAX(cwidth, _minwidth(child));
2563 cleft = left + (col_width - cwidth) / 2;
2565 /* center child if row height is bigger than child maxheight */
2566 cheight = MIN(_maxheight(child), row_height);
2567 cheight = MAX(cheight, _minheight(child));
2568 ctop = top + (row_height - cheight) / 2;
2570 /* g_print("layout %d %d %d %d\n", cleft, ctop, cwidth, cheight); */
2571 /* D(bug("2DL/child %p -> layout %d x %d\n", */
2572 /* child, cwidth, cheight)); */
2573 if (!MUI_Layout(child, cleft, ctop, cwidth, cheight, 0))
2574 return;
2576 left += data->horiz_spacing + col_width;
2578 ++j;
2579 if ((j % data->columns) == 0)
2580 break;
2583 top += data->vert_spacing + row_height;
2590 * all children in the same row have the same maximum height
2591 * all children in the same column have the same maximum height
2592 * if a child maximum size is smaller than the biggest minimum size,
2593 * the chid will be centered in the remaining space.
2595 * for each row, determine its height allocation
2596 * weight ? the vertical weight of a row, if no fixed-height child
2597 * in the row, is the sum of all vertical weights of children
2598 * all row members will have the same height
2600 * for each column, determine its width allocation
2601 * all column members will have the same width
2603 /* Write a proper hook function */
2604 static void
2605 group_layout_2d(struct IClass *cl, Object *obj, struct MinList *children)
2607 struct MUI_GroupData *data = INST_DATA(cl, obj);
2608 WORD left_start = 0;
2609 WORD top_start = 0;
2610 WORD total_size_height =
2611 _mheight(obj) - (data->rows - 1) * data->vert_spacing;
2612 WORD total_size_width =
2613 _mwidth(obj) - (data->columns - 1) * data->horiz_spacing;
2614 WORD total_init_height;
2615 WORD total_init_width;
2616 WORD remainder_height;
2617 WORD remainder_width;
2618 WORD layout_width;
2619 WORD layout_height;
2621 if (data->rows == 0 || data->columns == 0)
2622 return;
2623 if (data->num_children % data->rows
2624 || data->num_children % data->columns)
2625 return;
2626 if (data->row_infos == NULL || data->col_infos == NULL)
2627 return;
2629 //kprintf("group_layout_horiz: virtoff = %d,%d\n",
2630 // data->virt_offx, data->virt_offy);
2632 if (data->flags & GROUP_VIRTUAL)
2634 data->virt_mwidth =
2635 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2636 _maxwidth(obj) - _subwidth(obj));
2637 data->virt_mheight =
2638 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2639 _maxheight(obj) - _subheight(obj));
2641 layout_width = data->virt_mwidth;
2642 layout_height = data->virt_mheight;
2644 else
2646 layout_width = _mwidth(obj);
2647 layout_height = _mheight(obj);
2650 total_size_height =
2651 layout_height - (data->rows - 1) * data->vert_spacing;
2652 total_size_width =
2653 layout_width - (data->columns - 1) * data->horiz_spacing;
2655 // fix left/top ?
2657 // weight constraints
2658 Layout2D_weight_constraint(data, data->row_infos, data->col_infos,
2659 total_size_height, total_size_width,
2660 &total_init_height, &total_init_width);
2662 remainder_height = total_size_height - total_init_height;
2663 remainder_width = total_size_width - total_init_width;
2665 Layout2D_minmax_constraints_and_redistrib(data->row_infos,
2666 data->rows, total_size_height,
2667 /* (data->flags & GROUP_SAME_HEIGHT) ? data->samesize_maxmin_vert : 0, */
2668 0, remainder_height);
2670 Layout2D_minmax_constraints_and_redistrib(data->col_infos,
2671 data->columns, total_size_width,
2672 /* (data->flags & GROUP_SAME_WIDTH) ? data->samesize_maxmin_horiz : 0, */
2673 0, remainder_width);
2675 layout_2d_distribute_space(data, data->row_infos, data->col_infos,
2676 children, left_start, top_start);
2680 /* Write a proper hook function */
2681 static void group_layout_pagemode(struct IClass *cl, Object *obj,
2682 struct MinList *children)
2684 struct MUI_GroupData *data = INST_DATA(cl, obj);
2685 Object *cstate;
2686 Object *child;
2687 WORD layout_width;
2688 WORD layout_height;
2689 int w, h, yoffset = 0;
2691 if (data->flags & GROUP_VIRTUAL)
2693 data->virt_mwidth =
2694 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2695 _maxwidth(obj) - _subwidth(obj));
2696 data->virt_mheight =
2697 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2698 _maxheight(obj) - _subheight(obj));
2700 layout_width = data->virt_mwidth;
2701 layout_height = data->virt_mheight;
2703 else
2705 layout_width = _mwidth(obj);
2706 layout_height = _mheight(obj);
2709 if (data->titlegroup)
2711 yoffset = _minheight(data->titlegroup);
2712 layout_height -= yoffset;
2715 cstate = (Object *) children->mlh_Head;
2716 while ((child = NextObject(&cstate)))
2718 w = MIN(layout_width, _maxwidth(child));
2719 h = MIN(layout_height, _maxheight(child));
2721 if (child == data->titlegroup)
2723 MUI_Layout(child, (layout_width - w) / 2, 0, w, yoffset, 0);
2725 else
2727 D(bug("PM/child %p -> layout %d x %d\n", child, w, h));
2728 MUI_Layout(child, (layout_width - w) / 2,
2729 yoffset + (layout_height - h) / 2, w, h, 0);
2735 /**************************************************************************
2736 MUIM_Layout
2737 Either use a given layout hook, or the builtin method.
2738 **************************************************************************/
2739 IPTR Group__MUIM_Layout(struct IClass *cl, Object *obj,
2740 struct MUIP_Layout *msg)
2742 struct MUI_GroupData *data = INST_DATA(cl, obj);
2743 struct MUI_LayoutMsg lm = { 0 };
2745 get(data->family, MUIA_Family_List, &(lm.lm_Children));
2746 if (data->flags & GROUP_PAGEMODE)
2748 group_layout_pagemode(cl, obj, lm.lm_Children);
2750 else if (data->layout_hook)
2752 lm.lm_Type = MUILM_LAYOUT;
2753 lm.lm_Layout.Width = _mwidth(obj);
2754 lm.lm_Layout.Height = _mheight(obj);
2756 CallHookPkt(data->layout_hook, obj, &lm);
2758 if (data->flags & GROUP_VIRTUAL)
2760 data->virt_mwidth = lm.lm_Layout.Width;
2761 data->virt_mheight = lm.lm_Layout.Height;
2764 else
2766 if ((data->rows == 1) && (data->columns == 1))
2768 if (data->flags & GROUP_HORIZ)
2769 group_layout_horiz(cl, obj, lm.lm_Children);
2770 else
2771 group_layout_vert(cl, obj, lm.lm_Children);
2773 else
2774 group_layout_2d(cl, obj, lm.lm_Children);
2777 if (data->flags & GROUP_VIRTUAL)
2779 WORD new_virt_offx, new_virt_offy;
2781 new_virt_offx = data->virt_offx;
2782 new_virt_offy = data->virt_offy;
2784 if (new_virt_offx + _mwidth(obj) > data->virt_mwidth)
2786 new_virt_offx = data->virt_mwidth - _mwidth(obj);
2788 if (new_virt_offx < 0)
2789 new_virt_offx = 0;
2791 if (new_virt_offy + _mheight(obj) > data->virt_mheight)
2793 new_virt_offy = data->virt_mheight - _mheight(obj);
2795 if (new_virt_offy < 0)
2796 new_virt_offy = 0;
2798 if (new_virt_offx != data->virt_offx)
2800 nfset(obj, MUIA_Virtgroup_Left, new_virt_offx);
2803 if (new_virt_offy != data->virt_offy)
2805 nfset(obj, MUIA_Virtgroup_Top, new_virt_offy);
2810 return 0;
2813 /**************************************************************************
2814 MUIM_Show
2815 **************************************************************************/
2816 IPTR Group__MUIM_Show(struct IClass *cl, Object *obj,
2817 struct MUIP_Show *msg)
2819 struct MUI_GroupData *data = INST_DATA(cl, obj);
2820 Object *cstate;
2821 Object *child;
2822 struct MinList *ChildList = NULL;
2824 /* If msg is NULL, we won't want that the super method actually gets
2825 * this call */
2826 if (msg)
2827 DoSuperMethodA(cl, obj, (Msg) msg);
2829 get(data->family, MUIA_Family_List, &(ChildList));
2830 cstate = (Object *) ChildList->mlh_Head;
2832 if (data->flags & GROUP_PAGEMODE)
2834 int page = 0;
2835 while ((child = NextObject(&cstate)))
2837 if (child == data->titlegroup)
2839 DoShowMethod(child);
2840 continue; /* Title group is not counted as page */
2843 if (page == data->active_page)
2845 DoShowMethod(child);
2846 break;
2848 page++;
2851 else
2853 while ((child = NextObject(&cstate)))
2855 if (!(data->flags & GROUP_VIRTUAL) ||
2856 IsObjectVisible(child, MUIMasterBase))
2858 if (_flags(child) & MADF_SHOWME)
2859 DoShowMethod(child);
2863 return TRUE;
2866 /**************************************************************************
2867 MUIM_Hide
2868 **************************************************************************/
2869 IPTR Group__MUIM_Hide(struct IClass *cl, Object *obj,
2870 struct MUIP_Hide *msg)
2872 struct MUI_GroupData *data = INST_DATA(cl, obj);
2873 Object *cstate;
2874 Object *child;
2875 struct MinList *ChildList = NULL;
2877 get(data->family, MUIA_Family_List, &(ChildList));
2878 cstate = (Object *) ChildList->mlh_Head;
2880 if (data->flags & GROUP_PAGEMODE)
2882 int page = 0;
2883 while ((child = NextObject(&cstate)))
2885 if (child == data->titlegroup)
2887 DoHideMethod(child);
2888 continue; /* Title group is not counted as page */
2891 if (page == data->active_page)
2893 DoHideMethod(child);
2894 break;
2896 page++;
2899 else
2901 while ((child = NextObject(&cstate)))
2903 if (_flags(child) & MADF_CANDRAW)
2904 DoHideMethod(child);
2908 /* If msg is NULL, we won't want that the super method actually gets
2909 * this call */
2910 if (msg)
2911 return DoSuperMethodA(cl, obj, (Msg) msg);
2912 return 1;
2916 * MUIM_FindUData : tests if the MUIA_UserData of the object
2917 * contains the given <udata> and returns the object pointer in this case.
2919 IPTR Group__MUIM_FindUData(struct IClass *cl, Object *obj,
2920 struct MUIP_FindUData *msg)
2922 struct MUI_GroupData *data = INST_DATA(cl, obj);
2924 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2925 return (IPTR) obj;
2927 return DoMethodA(data->family, (Msg) msg);
2932 * MUIM_GetUData : This method tests if the MUIA_UserData of the object
2933 * contains the given <udata> and gets <attr> to <storage> for itself
2934 * in this case.
2936 IPTR Group__MUIM_GetUData(struct IClass *cl, Object *obj,
2937 struct MUIP_GetUData *msg)
2939 struct MUI_GroupData *data = INST_DATA(cl, obj);
2941 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2943 get(obj, msg->attr, msg->storage);
2944 return TRUE;
2947 return DoMethodA(data->family, (Msg) msg);
2952 * MUIM_SetUData : This method tests if the MUIA_UserData of the object
2953 * contains the given <udata> and sets <attr> to <val> for itself in this case.
2955 IPTR Group__MUIM_SetUData(struct IClass *cl, Object *obj,
2956 struct MUIP_SetUData *msg)
2958 struct MUI_GroupData *data = INST_DATA(cl, obj);
2960 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2961 set(obj, msg->attr, msg->val);
2963 DoMethodA(data->family, (Msg) msg);
2964 return TRUE;
2969 * MUIM_SetUDataOnce : This method tests if the MUIA_UserData of the object
2970 * contains the given <udata> and sets <attr> to <val> for itself in this case.
2971 * Stop after the first udata found.
2973 IPTR Group__MUIM_SetUDataOnce(struct IClass *cl, Object *obj,
2974 struct MUIP_SetUData *msg)
2976 struct MUI_GroupData *data = INST_DATA(cl, obj);
2978 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2980 set(obj, msg->attr, msg->val);
2981 return TRUE;
2983 return DoMethodA(data->family, (Msg) msg);
2986 /**************************************************************************
2987 MUIM_DragQueryExtented
2988 **************************************************************************/
2989 IPTR Group__MUIM_DragQueryExtended(struct IClass *cl, Object *obj,
2990 struct MUIP_DragQueryExtended *msg)
2992 struct MUI_GroupData *data = INST_DATA(cl, obj);
2993 Object *cstate;
2994 Object *child;
2995 Object *found_obj;
2996 struct MinList *ChildList = NULL;
2998 get(data->family, MUIA_Family_List, &(ChildList));
2999 cstate = (Object *) ChildList->mlh_Head;
3000 while ((child = NextObject(&cstate)))
3002 if (!(_flags(child) & MADF_CANDRAW))
3003 continue;
3005 if ((found_obj = (Object *) DoMethodA(child, (Msg) msg)))
3006 return (IPTR) found_obj;
3008 return DoSuperMethodA(cl, obj, (Msg) msg);
3011 /**************************************************************************
3012 MUIM_HandleEvent
3013 **************************************************************************/
3014 IPTR Group__MUIM_HandleEvent(struct IClass *cl, Object *obj,
3015 struct MUIP_HandleEvent *msg)
3017 struct MUI_GroupData *data = INST_DATA(cl, obj);
3019 /* check this, otherwise a superclass who has IDCMP_MOUSEBUTTONS
3020 eventhandler might call DoSuperMethod, and this function gets
3021 called even when he have not added any eventhandler */
3023 if ((data->flags & GROUP_VIRTUAL) && msg->imsg)
3025 switch (msg->imsg->Class)
3027 case IDCMP_MOUSEBUTTONS:
3028 /* For virtual groups */
3029 if (msg->imsg->Code == SELECTDOWN)
3031 if (_between(_mleft(obj), msg->imsg->MouseX, _mright(obj))
3032 && _between(_mtop(obj), msg->imsg->MouseY,
3033 _mbottom(obj)))
3035 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
3036 (IPTR) & data->ehn);
3037 data->ehn.ehn_Events |= IDCMP_INTUITICKS;
3038 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
3039 (IPTR) & data->ehn);
3042 else
3044 if (data->ehn.ehn_Events & IDCMP_INTUITICKS)
3046 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
3047 (IPTR) & data->ehn);
3048 data->ehn.ehn_Events &= ~IDCMP_INTUITICKS;
3049 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
3050 (IPTR) & data->ehn);
3053 break;
3055 case IDCMP_INTUITICKS:
3056 if (!(data->ehn.ehn_Events & IDCMP_INTUITICKS))
3057 break;
3059 if (!(_between(_mleft(obj), msg->imsg->MouseX, _mright(obj))
3060 && _between(_mtop(obj), msg->imsg->MouseY,
3061 _mbottom(obj))))
3063 LONG new_virt_offx = data->virt_offx;
3064 LONG new_virt_offy = data->virt_offy;
3066 if (msg->imsg->MouseX < _mleft(obj))
3068 /* scroll left */
3069 if (new_virt_offx >= 4)
3070 new_virt_offx -= 4;
3071 else
3072 new_virt_offx = 0;
3074 else if (msg->imsg->MouseX > _mright(obj))
3076 /* scroll right */
3077 new_virt_offx += 4;
3078 if (new_virt_offx > data->virt_mwidth - _mwidth(obj))
3079 new_virt_offx = data->virt_mwidth - _mwidth(obj);
3080 if (new_virt_offx < 0)
3081 new_virt_offx = 0;
3084 if (msg->imsg->MouseY < _mtop(obj))
3086 /* scroll top */
3087 if (new_virt_offy >= 4)
3088 new_virt_offy -= 4;
3089 else
3090 new_virt_offy = 0;
3092 else if (msg->imsg->MouseY > _mbottom(obj))
3094 /* scroll bottom */
3095 new_virt_offy += 4;
3096 if (new_virt_offy > data->virt_mheight - _mheight(obj))
3097 new_virt_offy = data->virt_mheight - _mheight(obj);
3098 if (new_virt_offy < 0)
3099 new_virt_offy = 0;
3102 if (new_virt_offx != data->virt_offx
3103 || new_virt_offy != data->virt_offy)
3105 SetAttrs(obj,
3106 MUIA_Virtgroup_Left, new_virt_offx,
3107 MUIA_Virtgroup_Top, new_virt_offy,
3108 MUIA_Group_Forward, FALSE, TAG_DONE);
3111 break;
3115 return 0;
3118 /**************************************************************************
3119 MUIM_DrawBackground
3120 **************************************************************************/
3121 IPTR Group__MUIM_DrawBackground(struct IClass *cl, Object *obj,
3122 struct MUIP_DrawBackground *msg)
3124 struct MUI_GroupData *data = INST_DATA(cl, obj);
3126 if (data->flags & GROUP_VIRTUAL)
3128 struct MUIP_DrawBackground msg2 = *msg;
3130 msg2.xoffset += data->virt_offx;
3131 msg2.yoffset += data->virt_offy;
3133 return DoSuperMethodA(cl, obj, (Msg) & msg2);
3136 return DoSuperMethodA(cl, obj, (Msg) msg);
3139 /**************************************************************************
3140 MUIM_FindAreaObject
3141 Find the given object or return NULL
3142 **************************************************************************/
3143 IPTR Group__MUIM_FindAreaObject(struct IClass *cl, Object *obj,
3144 struct MUIP_FindAreaObject *msg)
3146 struct MUI_GroupData *data = INST_DATA(cl, obj);
3147 Object *cstate;
3148 Object *child;
3149 struct MinList *ChildList = NULL;
3151 // it's me ?
3152 if (msg->obj == obj)
3153 return (IPTR) obj;
3155 // it's one of my children ?
3156 get(data->family, MUIA_Family_List, &(ChildList));
3157 cstate = (Object *) ChildList->mlh_Head;
3158 while ((child = NextObject(&cstate)))
3160 if (msg->obj == child)
3161 return (IPTR) child;
3164 // let the children find it
3165 get(data->family, MUIA_Family_List, &(ChildList));
3166 cstate = (Object *) ChildList->mlh_Head;
3167 while ((child = NextObject(&cstate)))
3169 Object *res = (Object *) DoMethodA(child, (Msg) msg);
3170 if (res != NULL)
3171 return (IPTR) res;
3174 return (IPTR) NULL;
3177 /**************************************************************************
3178 MUIM_Export : to export an objects "contents" to a dataspace object.
3179 **************************************************************************/
3180 static IPTR Group__MUIM_Export(struct IClass *cl, Object *obj,
3181 struct MUIP_Export *msg)
3183 struct MUI_GroupData *data = INST_DATA(cl, obj);
3184 Object *cstate;
3185 Object *child;
3186 struct MinList *ChildList = NULL;
3188 get(data->family, MUIA_Family_List, &(ChildList));
3189 if (!ChildList)
3190 return 0;
3192 cstate = (Object *) ChildList->mlh_Head;
3193 while ((child = NextObject(&cstate)))
3195 DoMethodA(child, (Msg) msg);
3198 return 0;
3202 /**************************************************************************
3203 MUIM_Import : to import an objects "contents" from a dataspace object.
3204 **************************************************************************/
3205 static IPTR Group__MUIM_Import(struct IClass *cl, Object *obj,
3206 struct MUIP_Import *msg)
3208 struct MUI_GroupData *data = INST_DATA(cl, obj);
3209 Object *cstate;
3210 Object *child;
3211 struct MinList *ChildList = NULL;
3213 get(data->family, MUIA_Family_List, &(ChildList));
3214 if (!ChildList)
3215 return 0;
3217 cstate = (Object *) ChildList->mlh_Head;
3218 while ((child = NextObject(&cstate)))
3220 DoMethodA(child, (Msg) msg);
3223 return 0;
3226 /**************************************************************************
3227 MUIM_Notify - disabled now because previous Zune versions had a OM_GET
3228 check in MUIM_Notify which is no longer the case
3229 **************************************************************************/
3230 #if 0
3231 STATIC IPTR Group_Notify(struct IClass *cl, Object *obj,
3232 struct MUIP_Notify *msg)
3234 struct MUI_GroupData *data = INST_DATA(cl, obj);
3235 Object *cstate;
3236 Object *child;
3237 struct MinList *ChildList;
3239 /* Try at first if understand the message our self
3240 ** We disable the forwarding of the OM_GET message
3241 ** as the MUIM_Notify otherwise would "think" that
3242 ** the group class actually understands the attribute
3243 ** although a child does this only
3245 data->dont_forward_get = 1;
3246 if (DoSuperMethodA(cl, obj, (Msg) msg))
3248 data->dont_forward_get = 0;
3249 return 1;
3252 /* We ourselves didn't understand the notify tag so we try the
3253 * children now */
3254 data->dont_forward_get = 0;
3256 get(data->family, MUIA_Family_List, (IPTR *) & (ChildList));
3257 cstate = (Object *) ChildList->mlh_Head;
3258 while ((child = NextObject(&cstate)))
3260 if (DoMethodA(child, (Msg) msg))
3261 return 1;
3263 return 0;
3265 #endif
3267 #if 1
3268 /* Notes about Group_Notify() and echo notification problem:
3269 It was discovered that MUI seems to have some special handling for group class
3270 which will drop notifications on the children which are found to not
3271 understand the attribute.
3273 This is done by checking if an OM_GET on the child returns TRUE.
3274 There's a little problem here because it is not known how big the storage
3275 needed for the attribute in question will be. Almost no class uses anything
3276 bigger than one IPTR. For "big" attributes those return a pointer to the data,
3277 not the data itself. Unfortuntely there are some exceptions like colorwheel
3278 class which does not return a pointer, but the data itself. So it's not
3279 enough to use one single IPTR variable (4 Bytes on 32bit machines, 8 bytes
3280 on 64 bit machines) to store the result of the test-OM_Get.
3282 There is no general way to query the size needed so if one wants to change
3283 Zune to work like MUI one needs to choose a size which one hopes will be
3284 big enough to hold all possible attributes of all classes, old, present
3285 and future ones.
3287 STATIC IPTR Group_Notify(struct IClass *cl, Object *obj,
3288 struct MUIP_Notify *msg)
3290 struct MUI_GroupData *data = INST_DATA(cl, obj);
3291 Object *cstate;
3292 Object *child;
3293 struct MinList *ChildList = NULL;
3294 IPTR attr[30];
3296 data->dont_forward_get = 1;
3298 if (GetAttr(msg->TrigAttr, obj, attr))
3300 data->dont_forward_get = 0;
3301 return DoSuperMethodA(cl, obj, (Msg) msg);
3303 data->dont_forward_get = 0;
3305 get(data->family, MUIA_Family_List, &(ChildList));
3306 if (!ChildList)
3307 return TRUE;
3309 cstate = (Object *) ChildList->mlh_Head;
3310 while ((child = NextObject(&cstate)))
3313 if (GetAttr(msg->TrigAttr, child, attr))
3315 DoMethodA(child, (Msg) msg);
3316 /* No return here! */
3319 return TRUE;
3321 #endif
3323 BOOPSI_DISPATCHER(IPTR, Group_Dispatcher, cl, obj, msg)
3325 switch (msg->MethodID)
3327 case OM_NEW:
3328 return Group__OM_NEW(cl, obj, (struct opSet *)msg);
3329 case OM_DISPOSE:
3330 return Group__OM_DISPOSE(cl, obj, msg);
3331 case OM_SET:
3332 return Group__OM_SET(cl, obj, (struct opSet *)msg);
3333 case OM_GET:
3334 return Group__OM_GET(cl, obj, (struct opGet *)msg);
3335 case OM_ADDMEMBER: /* Fall through */
3336 case MUIM_Group_AddTail:
3337 return Group__MUIM_AddTail(cl, obj, (APTR) msg);
3338 case MUIM_Group_AddHead:
3339 return Group__MUIM_AddHead(cl, obj, (APTR) msg);
3340 case MUIM_Group_Insert:
3341 return Group__MUIM_Insert(cl, obj, (APTR) msg);
3342 case OM_REMMEMBER: /* Fall through */
3343 case MUIM_Group_Remove:
3344 return Group__MUIM_Remove(cl, obj, (APTR) msg);
3345 case MUIM_AskMinMax:
3346 return Group__MUIM_AskMinMax(cl, obj, (APTR) msg);
3347 case MUIM_Group_ExitChange:
3348 return Group__MUIM_ExitChange(cl, obj, (APTR) msg);
3349 case MUIM_Group_ExitChange2:
3350 return Group__MUIM_ExitChange2(cl, obj, (APTR) msg);
3351 case MUIM_Group_InitChange:
3352 return Group__MUIM_InitChange(cl, obj, (APTR) msg);
3353 case MUIM_Group_Sort:
3354 return Group__MUIM_Sort(cl, obj, (APTR) msg);
3355 case MUIM_Group_DoMethodNoForward:
3356 return Group__MUIM_DoMethodNoForward(cl, obj, (APTR) msg);
3357 case MUIM_ConnectParent:
3358 return Group__MUIM_ConnectParent(cl, obj, (APTR) msg);
3359 case MUIM_DisconnectParent:
3360 return Group__MUIM_DisconnectParent(cl, obj, (APTR) msg);
3361 case MUIM_Layout:
3362 return Group__MUIM_Layout(cl, obj, (APTR) msg);
3363 case MUIM_Setup:
3364 return Group__MUIM_Setup(cl, obj, (APTR) msg);
3365 case MUIM_Cleanup:
3366 return Group__MUIM_Cleanup(cl, obj, (APTR) msg);
3367 case MUIM_Draw:
3368 return Group__MUIM_Draw(cl, obj, (APTR) msg);
3370 case MUIM_FindUData:
3371 return Group__MUIM_FindUData(cl, obj, (APTR) msg);
3372 case MUIM_GetUData:
3373 return Group__MUIM_GetUData(cl, obj, (APTR) msg);
3374 case MUIM_SetUData:
3375 return Group__MUIM_SetUData(cl, obj, (APTR) msg);
3376 case MUIM_SetUDataOnce:
3377 return Group__MUIM_SetUDataOnce(cl, obj, (APTR) msg);
3378 case MUIM_Show:
3379 return Group__MUIM_Show(cl, obj, (APTR) msg);
3380 case MUIM_Hide:
3381 return Group__MUIM_Hide(cl, obj, (APTR) msg);
3382 case MUIM_HandleEvent:
3383 return Group__MUIM_HandleEvent(cl, obj, (APTR) msg);
3384 case MUIM_DrawBackground:
3385 return Group__MUIM_DrawBackground(cl, obj, (APTR) msg);
3386 case MUIM_DragQueryExtended:
3387 return Group__MUIM_DragQueryExtended(cl, obj, (APTR) msg);
3388 case MUIM_FindAreaObject:
3389 return Group__MUIM_FindAreaObject(cl, obj, (APTR) msg);
3390 case MUIM_Export:
3391 return Group__MUIM_Export(cl, obj, (APTR) msg);
3392 case MUIM_Import:
3393 return Group__MUIM_Import(cl, obj, (APTR) msg);
3395 //#if 0
3396 #if 1
3397 /* Disabled. See above */
3398 case MUIM_Notify:
3399 return Group_Notify(cl, obj, (APTR) msg);
3400 #endif
3401 case MUIM_Set:
3402 case MUIM_MultiSet:
3403 case MUIM_CallHook:
3404 case MUIM_DrawParentBackground:
3405 case MUIM_DragBegin:
3406 case MUIM_DragDrop:
3407 case MUIM_DragQuery:
3408 case MUIM_DragFinish:
3409 case MUIM_DoDrag:
3410 case MUIM_CreateDragImage:
3411 case MUIM_DeleteDragImage:
3412 case MUIM_GoActive:
3413 case MUIM_GoInactive:
3414 case MUIM_CreateBubble:
3415 case MUIM_DeleteBubble:
3416 case MUIM_CreateShortHelp:
3417 case MUIM_DeleteShortHelp:
3418 case OM_ADDTAIL:
3419 case OM_REMOVE:
3420 return DoSuperMethodA(cl, obj, (APTR) msg);
3421 /* Needs not to be forwarded? */
3424 /* sometimes you want to call a superclass method,
3425 * but not dispatching to child.
3426 * But what to do with list methods in a listview ?
3428 Group_DispatchMsg(cl, obj, (APTR) msg);
3430 return DoSuperMethodA(cl, obj, msg);
3432 BOOPSI_DISPATCHER_END
3435 * Class descriptor.
3437 const struct __MUIBuiltinClass _MUI_Group_desc =
3439 MUIC_Group,
3440 MUIC_Area,
3441 sizeof(struct MUI_GroupData),
3442 (void *) Group_Dispatcher