- Implemented AppMessage handling. This necessitated the rearrangement of
[AROS.git] / workbench / libs / muimaster / classes / group.c
blobd05e4b25e8ef9e78b6a12a17d1551485d05b5d48
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)
137 /* During minmax calculations objects with a weight of 0 shall
138 be treated like they had identical min/def/max size, ie. fixed size.
140 During layout objects with 0 weight must be treated like fixed-sized
141 too, but for hgroups only in x direction, and for vgroups only in
142 y direction. I think ... */
144 #define w0_defwidth(x) (_hweight(x) ? _defwidth(x) : _minwidth(x))
145 #define w0_maxwidth(x) (_hweight(x) ? _maxwidth(x) : _minwidth(x))
147 #define w0_defheight(x) (_vweight(x) ? _defheight(x) : _minheight(x))
148 #define w0_maxheight(x) (_vweight(x) ? _maxheight(x) : _minheight(x))
150 static const int __version = 1;
151 static const int __revision = 1;
153 IPTR Group__MUIM_Show(struct IClass *cl, Object *obj,
154 struct MUIP_Show *msg);
155 IPTR Group__MUIM_Hide(struct IClass *cl, Object *obj,
156 struct MUIP_Hide *msg);
158 /*****************************************************************************/
159 /*****************************************************************************/
162 static ULONG Group_DispatchMsg(struct IClass *cl, Object *obj, Msg msg);
164 static void change_active_page(struct IClass *cl, Object *obj, LONG page)
166 struct MUI_GroupData *data = INST_DATA(cl, obj);
167 LONG newpage, num_children = data->num_children;
169 if (!(data->flags & GROUP_PAGEMODE))
170 return;
172 if (data->titlegroup != NULL)
173 num_children--;
175 switch (page)
177 case MUIV_Group_ActivePage_First:
178 newpage = 0;
179 break;
180 case MUIV_Group_ActivePage_Last:
181 newpage = num_children - 1;
182 break;
183 case MUIV_Group_ActivePage_Prev:
184 newpage = data->active_page - 1;
185 if (newpage == -1)
186 newpage = num_children - 1;
187 break;
188 case MUIV_Group_ActivePage_Next:
189 case MUIV_Group_ActivePage_Advance:
190 newpage = (data->active_page + 1) % num_children;
191 break;
192 default:
193 newpage = page;
194 break;
197 if (newpage != data->active_page)
199 if (_flags(obj) & MADF_CANDRAW)
200 Group__MUIM_Hide(cl, obj, NULL);
202 data->active_page = newpage;
204 if (_flags(obj) & MADF_CANDRAW)
206 DoMethod(obj, MUIM_Layout);
207 Group__MUIM_Show(cl, obj, NULL);
208 data->update = 1;
209 MUI_Redraw(obj, MADF_DRAWUPDATE);
212 if (data->titlegroup)
213 set(data->titlegroup, MUIA_Group_ActivePage, newpage);
217 /**************************************************************************
218 Returns the number of visible children. Visible children are all children
219 that have MADF_SHOWME and not MADF_BORDERGADGET set.
220 **************************************************************************/
221 static int Group_GetNumVisibleChildren(struct MUI_GroupData *data,
222 struct MinList *children)
224 int num_visible_children = data->num_children;
225 Object *cstate;
226 Object *child;
228 /* As there can be invisible children we have subtract those from the total
229 * number of children */
230 cstate = (Object *) children->mlh_Head;
231 while ((child = NextObject(&cstate)))
233 if (IS_HIDDEN(child))
234 num_visible_children--;
236 return num_visible_children;
239 /**************************************************************************
240 Handles insertion of objects - works based on fact that IDs of all methods
241 that use it are the same for Group and Family and that struct share obj at
242 same offset.
243 **************************************************************************/
244 struct MUIP_StructWithObj
246 STACKED ULONG MethodID;
247 STACKED Object *obj;
250 static IPTR Group__MUIM_AddObject(struct IClass *cl, Object *obj, Msg msg)
252 struct MUI_GroupData *data = INST_DATA(cl, obj);
253 struct MUIP_StructWithObj *msgint = (struct MUIP_StructWithObj *)msg;
255 DoMethodA(data->family, (Msg) msg);
256 data->num_children++;
258 /* if we are in an application tree, propagate pointers */
259 if (muiNotifyData(obj)->mnd_GlobalInfo)
261 /* Only children of groups can have parents */
263 if ((_flags(obj) & MADF_INVIRTUALGROUP)
264 || (data->flags & GROUP_VIRTUAL))
266 _flags(msgint->obj) |= MADF_INVIRTUALGROUP;
269 muiNotifyData(msgint->obj)->mnd_ParentObject = obj;
270 DoMethod(msgint->obj, MUIM_ConnectParent, (IPTR) obj);
273 if (_flags(obj) & MADF_SETUP)
275 DoSetupMethod(msgint->obj, muiRenderInfo(obj));
277 /* if (_flags(obj) & MADF_CANDRAW) */
278 /* DoShowMethod(msg->opam_Object); */
280 return TRUE;
283 /**************************************************************************
284 OM_NEW - Constructor
285 **************************************************************************/
286 IPTR Group__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
288 struct MUI_GroupData *data;
289 struct TagItem *tags, *tag;
290 BOOL bad_children = FALSE;
291 IPTR disabled = FALSE;
292 IPTR frame = MUIV_Frame_None;
294 D(bug("[group.mui] OM_NEW, object 0x%p\n", obj));
296 obj = (Object *) DoSuperMethodA(cl, obj, (Msg) msg);
297 if (!obj)
298 return 0;
300 /* Initial local instance data */
301 data = INST_DATA(cl, obj);
303 data->family = MUI_NewObjectA(MUIC_Family, NULL);
304 if (!data->family)
306 CoerceMethod(cl, obj, OM_DISPOSE);
307 return 0;
310 data->horiz_spacing = -1;
311 data->vert_spacing = -1;
312 data->columns = 1;
313 data->rows = 1;
314 data->active_page = 0;
315 get(obj, MUIA_Frame, &frame);
317 /* parse initial taglist */
318 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
320 switch (tag->ti_Tag)
322 case MUIA_Group_Child:
323 D(bug("[group.mui] Adding child 0x%p\n", tag->ti_Data));
324 if (tag->ti_Data)
326 DoMethod(obj, OM_ADDMEMBER, tag->ti_Data);
327 /* Set first child as group title */
328 if ((frame == MUIV_Frame_Register)
329 && (data->titlegroup == NULL))
330 data->titlegroup = (Object *) tag->ti_Data;
332 else
333 bad_children = TRUE;
334 break;
336 case MUIA_Group_ActivePage:
337 change_active_page(cl, obj, (LONG) tag->ti_Data);
338 break;
340 case MUIA_Group_Columns:
341 data->columns = (tag->ti_Data) > 1 ? tag->ti_Data : 1;
342 data->rows = 0;
343 break;
345 case MUIA_Group_Horiz:
346 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_HORIZ);
347 break;
349 case MUIA_Group_HorizSpacing:
350 data->flags |= GROUP_HSPACING;
351 data->horiz_spacing = tag->ti_Data;
352 break;
354 case MUIA_Group_LayoutHook:
355 data->layout_hook = (struct Hook *)tag->ti_Data;
356 break;
358 case MUIA_Group_PageMode:
359 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_PAGEMODE);
360 break;
362 case MUIA_Group_Rows:
363 data->rows = MAX((ULONG) tag->ti_Data, 1);
364 data->columns = 0;
365 break;
367 case MUIA_Group_SameHeight:
368 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_HEIGHT);
369 break;
371 case MUIA_Group_SameSize:
372 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_HEIGHT);
373 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_WIDTH);
374 break;
376 case MUIA_Group_SameWidth:
377 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_SAME_WIDTH);
378 break;
380 case MUIA_Group_Spacing:
381 data->flags |= (GROUP_HSPACING | GROUP_VSPACING);
382 data->horiz_spacing = tag->ti_Data;
383 data->vert_spacing = tag->ti_Data;
384 break;
386 case MUIA_Group_VertSpacing:
387 data->flags |= GROUP_VSPACING;
388 data->vert_spacing = tag->ti_Data;
389 break;
391 case MUIA_Group_Virtual:
392 _handle_bool_tag(data->flags, tag->ti_Data, GROUP_VIRTUAL);
393 break;
397 if (bad_children)
399 CoerceMethod(cl, obj, OM_DISPOSE);
400 return 0;
403 /* D(bug("Group_New(0x%lx)\n",obj)); */
405 if (data->flags & GROUP_VIRTUAL)
407 /* This is used by MUI_Render() to determine if group is virtual.
408 * It then installs a clip region.
409 * Also MUI_Layout() uses this. Probably for speed up reason */
410 _flags(obj) |= MADF_ISVIRTUALGROUP;
413 /* will forward MUIA_Disabled to children */
414 get(obj, MUIA_Disabled, &disabled);
415 if (disabled)
417 set(obj, MUIA_Disabled, TRUE);
420 /* This is only used for virtual groups */
421 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS; /* Will be filled on demand */
422 data->ehn.ehn_Priority = 10; /* Will hear the click before all
423 * other normal objects */
424 data->ehn.ehn_Flags = 0;
425 data->ehn.ehn_Object = obj;
426 data->ehn.ehn_Class = cl;
427 return (IPTR) obj;
430 /**************************************************************************
431 OM_DISPOSE
432 **************************************************************************/
433 IPTR Group__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
435 struct MUI_GroupData *data = INST_DATA(cl, obj);
437 if (data->row_infos != NULL)
438 mui_free(data->row_infos);
439 if (data->col_infos != NULL)
440 mui_free(data->col_infos);
441 if (data->family != NULL)
442 MUI_DisposeObject(data->family);
443 return DoSuperMethodA(cl, obj, msg);
446 /**************************************************************************
447 OM_SET
448 **************************************************************************/
449 IPTR Group__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
451 struct MUI_GroupData *data = INST_DATA(cl, obj);
452 struct TagItem *tags = msg->ops_AttrList;
453 struct TagItem *tag;
454 BOOL forward = TRUE;
455 BOOL need_recalc = FALSE;
456 IPTR retval;
458 int virt_offx = data->virt_offx, virt_offy = data->virt_offy;
460 /* There are many ways to find out what tag items provided by set()
461 ** we do know. The best way should be using NextTagItem() and simply
462 ** browsing through the list.
465 /* Parse group attributes before calling DoSuperMethodA(),
466 ** otherwise if an app for example sets up a notification
467 ** on MUIA_Group_ActivePage which calls a hook, and then
468 ** the hook function calls get(obj, MUIA_Group_ActivePage),
469 ** it would get returned the old active page instead of the new
470 ** active page
473 while ((tag = NextTagItem(&tags)) != NULL)
475 switch (tag->ti_Tag)
477 case MUIA_Group_Columns:
478 data->columns = MAX((ULONG) tag->ti_Data, 1);
479 data->rows = 0;
480 need_recalc = TRUE;
481 break;
482 case MUIA_Group_ActivePage:
483 change_active_page(cl, obj, (LONG) tag->ti_Data);
484 break;
485 case MUIA_Group_Forward:
486 forward = tag->ti_Data;
487 break;
488 case MUIA_Group_HorizSpacing:
489 data->flags |= GROUP_HSPACING;
490 data->horiz_spacing = tag->ti_Data;
491 break;
492 case MUIA_Group_Rows:
493 data->rows = MAX((ULONG) tag->ti_Data, 1);
494 data->columns = 0;
495 need_recalc = TRUE;
496 break;
497 case MUIA_Group_Spacing:
498 data->flags |= (GROUP_HSPACING | GROUP_VSPACING);
499 data->horiz_spacing = tag->ti_Data;
500 data->vert_spacing = tag->ti_Data;
501 break;
502 case MUIA_Group_VertSpacing:
503 data->flags |= GROUP_VSPACING;
504 data->vert_spacing = tag->ti_Data;
505 break;
506 case MUIA_Virtgroup_Left:
507 //kprintf("set virtgroup_left: %d\n", tag->ti_Data);
508 virt_offx = tag->ti_Data;
509 break;
511 case MUIA_Group_LayoutHook:
513 [ach] Seems like MUI supports setting this attribute after
514 initialization, even though the documentation states
515 otherwise. At least some programs use it...
517 data->layout_hook = (struct Hook *)tag->ti_Data;
518 break;
520 case MUIA_Virtgroup_Top:
521 //kprintf("set virtgroup_top: %d\n", tag->ti_Data);
522 virt_offy = tag->ti_Data;
523 break;
528 if (muiRenderInfo(obj) && need_recalc)
529 DoMethod(_win(obj), MUIM_Window_RecalcDisplay, (IPTR) obj);
531 retval = DoSuperMethodA(cl, obj, (Msg) msg);
533 /* seems to be the documented behaviour, however it should be slow! */
535 if (forward)
537 /* Attributes which are to be filtered out, so that they are ignored
538 when OM_SET is passed to group's children */
540 tags = msg->ops_AttrList;
541 while ((tag = NextTagItem(&tags)) != NULL)
543 switch (tag->ti_Tag)
545 case MUIA_HelpLine:
546 case MUIA_HelpNode:
547 case MUIA_ObjectID:
548 case MUIA_UserData:
550 case MUIA_ContextMenu:
551 case MUIA_ContextMenuTrigger:
552 case MUIA_ControlChar:
553 case MUIA_CycleChain:
554 case MUIA_Draggable:
555 case MUIA_FillArea:
556 case MUIA_Group_ActivePage:
557 case MUIA_Frame:
558 case MUIA_FrameTitle:
559 case MUIA_HorizWeight:
560 case MUIA_Pressed:
561 case MUIA_ShortHelp:
562 case MUIA_ShowMe:
563 case MUIA_VertWeight:
564 case MUIA_Weight:
565 case MUIA_Virtgroup_Left:
566 case MUIA_Virtgroup_Top:
567 case MUIA_AppMessage:
568 tag->ti_Tag = TAG_IGNORE;
569 break;
570 case MUIA_Selected:
571 /* D(bug("Group_Set(%p) MUIA_Selected forwarded\n", obj)); */
572 /* tag->ti_Tag = TAG_IGNORE; */
573 break;
577 Group_DispatchMsg(cl, obj, (Msg) msg);
581 if (virt_offx != data->virt_offx || virt_offy != data->virt_offy)
583 if (_flags(obj) & MADF_CANDRAW)
584 Group__MUIM_Hide(cl, obj, NULL);
585 data->virt_offx = virt_offx;
586 data->virt_offy = virt_offy;
587 /* Relayout ourself, this will also relayout all the children */
588 DoMethod(obj, MUIM_Layout);
589 if (_flags(obj) & MADF_CANDRAW)
590 Group__MUIM_Show(cl, obj, NULL);
591 data->update = 2;
592 MUI_Redraw(obj, MADF_DRAWUPDATE);
595 return retval;
599 /**************************************************************************
600 OM_GET
601 **************************************************************************/
602 IPTR Group__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
604 /* small macro to simplify return value storage */
605 #define STORE *(msg->opg_Storage)
607 struct MUI_GroupData *data = INST_DATA(cl, obj);
609 switch (msg->opg_AttrID)
611 case MUIA_Version:
612 STORE = __version;
613 return 1;
614 case MUIA_Revision:
615 STORE = __revision;
616 return 1;
617 case MUIA_Group_ActivePage:
618 STORE = data->active_page;
619 return 1;
620 case MUIA_Group_ChildList:
621 return GetAttr(MUIA_Family_List, data->family, msg->opg_Storage);
622 case MUIA_Group_Horiz:
623 STORE = (data->flags & GROUP_HORIZ);
624 return 1;
625 case MUIA_Group_HorizSpacing:
626 STORE = data->horiz_spacing;
627 return 1;
628 case MUIA_Group_VertSpacing:
629 STORE = data->vert_spacing;
630 return 1;
631 case MUIA_Group_ChildCount:
632 STORE = data->num_children;
633 return 1;
634 case MUIA_Virtgroup_Left:
635 STORE = data->virt_offx;
636 return 1;
637 case MUIA_Virtgroup_Top:
638 STORE = data->virt_offy;
639 return 1;
640 case MUIA_Virtgroup_Width:
641 STORE = data->virt_mwidth;
642 return 1;
643 case MUIA_Virtgroup_Height:
644 STORE = data->virt_mheight;
645 return 1;
646 case MUIA_Virtgroup_MinWidth:
647 STORE = data->saved_minwidth;
648 return 1;
649 case MUIA_Virtgroup_MinHeight:
650 STORE = data->saved_minheight;
651 return 1;
654 /* our handler didn't understand the attribute, we simply pass
655 ** it to our superclass now
657 if (DoSuperMethodA(cl, obj, (Msg) msg))
658 return 1;
660 /* seems to be the documented behaviour, however it should be slow! */
661 if (!data->dont_forward_get && !data->dont_forward_methods)
663 Object *cstate;
664 Object *child;
665 struct MinList *ChildList = NULL;
667 get(data->family, MUIA_Family_List, &(ChildList));
668 cstate = (Object *) ChildList->mlh_Head;
669 while ((child = NextObject(&cstate)))
670 if (DoMethodA(child, (Msg) msg))
671 return 1;
673 return 0;
674 #undef STORE
678 /**************************************************************************
679 MUIM_AddTail
680 **************************************************************************/
681 IPTR Group__MUIM_AddTail(struct IClass *cl, Object *obj,
682 struct MUIP_Group_AddTail *msg)
684 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
687 /**************************************************************************
688 MUIM_AddHead
689 **************************************************************************/
690 IPTR Group__MUIM_AddHead(struct IClass *cl, Object *obj,
691 struct MUIP_Group_AddHead *msg)
693 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
696 /**************************************************************************
697 MUIM_Insert
698 **************************************************************************/
699 IPTR Group__MUIM_Insert(struct IClass *cl, Object *obj,
700 struct MUIP_Group_Insert *msg)
702 return Group__MUIM_AddObject(cl, obj, (Msg) msg);
705 /**************************************************************************
706 MUIM_Remove
707 **************************************************************************/
708 IPTR Group__MUIM_Remove(struct IClass *cl, Object *obj,
709 struct MUIP_Group_Remove *msg)
711 struct MUI_GroupData *data = INST_DATA(cl, obj);
713 if (_flags(obj) & MADF_CANDRAW)
714 DoHideMethod(msg->obj);
715 if (_flags(obj) & MADF_SETUP)
716 DoMethod(msg->obj, MUIM_Cleanup);
717 if (muiNotifyData(obj)->mnd_GlobalInfo)
719 DoMethod(msg->obj, MUIM_DisconnectParent);
720 muiNotifyData(msg->obj)->mnd_ParentObject = NULL;
722 _flags(msg->obj) &= ~MADF_INVIRTUALGROUP;
725 data->num_children--;
726 DoMethodA(data->family, (Msg) msg);
728 return TRUE;
732 /**************************************************************************
733 MUIM_ConnectParent
734 **************************************************************************/
735 IPTR Group__MUIM_ConnectParent(struct IClass *cl, Object *obj,
736 struct MUIP_ConnectParent *msg)
738 struct MUI_GroupData *data = INST_DATA(cl, obj);
739 Object *cstate;
740 Object *child;
741 struct MinList *ChildList = NULL;
743 DoSuperMethodA(cl, obj, (Msg) msg);
745 get(data->family, MUIA_Family_List, &(ChildList));
746 cstate = (Object *) ChildList->mlh_Head;
747 while ((child = NextObject(&cstate)))
749 if ((_flags(obj) & MADF_INVIRTUALGROUP)
750 || (data->flags & GROUP_VIRTUAL))
752 _flags(child) |= MADF_INVIRTUALGROUP;
755 /* Only children of groups can have parents */
756 muiNotifyData(child)->mnd_ParentObject = obj;
758 DoMethod(child, MUIM_ConnectParent, (IPTR) obj);
760 return TRUE;
763 /**************************************************************************
764 MUIM_DisconnectParent
765 **************************************************************************/
766 IPTR Group__MUIM_DisconnectParent(struct IClass *cl, Object *obj,
767 struct MUIP_ConnectParent *msg)
769 struct MUI_GroupData *data = INST_DATA(cl, obj);
770 Object *cstate;
771 Object *child;
772 struct MinList *ChildList = NULL;
774 get(data->family, MUIA_Family_List, &(ChildList));
775 cstate = (Object *) ChildList->mlh_Head;
776 while ((child = NextObject(&cstate)))
778 DoMethodA(child, (Msg) msg);
779 muiNotifyData(child)->mnd_ParentObject = NULL;
780 _flags(child) &= ~MADF_INVIRTUALGROUP;
782 DoSuperMethodA(cl, obj, (Msg) msg);
783 return TRUE;
787 * Put group in exchange state
789 IPTR Group__MUIM_InitChange(struct IClass *cl, Object *obj,
790 struct MUIP_Group_InitChange *msg)
792 struct MUI_GroupData *data = INST_DATA(cl, obj);
794 data->flags |= GROUP_CHANGING;
795 return TRUE;
800 * Will recalculate display after dynamic adding/removing
802 IPTR Group__MUIM_ExitChange(struct IClass *cl, Object *obj,
803 struct MUIP_Group_ExitChange *msg)
805 struct MUI_GroupData *data = INST_DATA(cl, obj);
807 if (data->flags & GROUP_CHANGING)
809 data->flags &= ~GROUP_CHANGING;
811 if ((_flags(obj) & MADF_SETUP) && _win(obj))
813 Object *win = _win(obj);
814 Object *parent = obj;
816 /* CHECKME: Don't call RecalcDisplay if one of our parents is
817 in GROUP_CHANGING state to prevent crash with Zune prefs
818 program NListtree page because NList/NListtree when
819 killing tree images in MUIM_Cleanup uses InitChange/
820 ExitChange. Zune prefs program uses InitChange/ExitChange
821 when switching page -> nesting -> mess. */
823 while ((parent = _parent(parent)))
825 struct MUI_GroupData *pdata = INST_DATA(cl, parent);
827 if (parent == win)
828 break;
830 if (pdata->flags & GROUP_CHANGING)
832 return TRUE;
837 DoMethod(win, MUIM_Window_RecalcDisplay, (IPTR) obj);
841 return TRUE;
846 * Sort the family
848 IPTR Group__MUIM_Sort(struct IClass *cl, Object *obj,
849 struct MUIP_Group_Sort *msg)
851 struct MUI_GroupData *data = INST_DATA(cl, obj);
853 /* modify message */
854 msg->MethodID = MUIM_Family_Sort;
856 DoMethodA(data->family, (APTR) msg);
858 /* restore original message */
859 msg->MethodID = MUIM_Group_Sort;
860 return TRUE;
863 /**************************************************************************
864 MUIM_Group_DoMethodNoForward
866 Executes the given method but does not forward it to the children
867 **************************************************************************/
868 IPTR Group__MUIM_DoMethodNoForward(struct IClass *cl, Object *obj,
869 struct MUIP_Group_DoMethodNoForward *msg)
871 struct MUI_GroupData *data = INST_DATA(cl, obj);
872 IPTR rc;
873 data->dont_forward_methods = 1; /* disable forwarding */
874 rc = DoMethodA(obj, (Msg) & msg->DoMethodID);
875 /* Probably doesn't work correctly on AROS? */
877 data->dont_forward_methods = 0;
878 return rc;
882 * Propagate a method to group children.
884 static ULONG Group_DispatchMsg(struct IClass *cl, Object *obj, Msg msg)
886 struct MUI_GroupData *data = INST_DATA(cl, obj);
887 Object *cstate;
888 Object *child;
889 struct MinList *ChildList = NULL;
891 if (data->dont_forward_methods)
892 return TRUE;
894 get(data->family, MUIA_Family_List, &(ChildList));
895 cstate = (Object *) ChildList->mlh_Head;
896 while ((child = NextObject(&cstate)))
898 DoMethodA(child, (Msg) msg);
900 return TRUE;
904 /**************************************************************************
905 MUIM_Setup
906 **************************************************************************/
907 IPTR Group__MUIM_Setup(struct IClass *cl, Object *obj,
908 struct MUIP_Setup *msg)
910 struct MUI_GroupData *data = INST_DATA(cl, obj);
911 Object *cstate;
912 Object *cstate_copy;
913 Object *child;
914 Object *childFailed;
915 struct MinList *ChildList = NULL;
917 if (!DoSuperMethodA(cl, obj, (Msg) msg))
918 return FALSE;
920 ASSERT_VALID_PTR(muiGlobalInfo(obj));
922 if (!(data->flags & GROUP_HSPACING))
923 data->horiz_spacing = muiGlobalInfo(obj)->mgi_Prefs->group_hspacing;
924 if (!(data->flags & GROUP_VSPACING))
925 data->vert_spacing = muiGlobalInfo(obj)->mgi_Prefs->group_vspacing;
926 get(data->family, MUIA_Family_List, &(ChildList));
927 cstate = cstate_copy = (Object *) ChildList->mlh_Head;
928 while ((child = NextObject(&cstate)))
930 #if 0 /* SHOWME affects only show/hide */
931 if (!(_flags(child) & MADF_SHOWME))
932 continue;
933 #endif
935 if (!DoSetupMethod(child, msg->RenderInfo))
937 /* Send MUIM_Cleanup to all objects that received MUIM_Setup.
939 childFailed = child;
940 cstate = cstate_copy;
941 while ((child = NextObject(&cstate)) && (child != childFailed))
943 #if 0 /* SHOWME affects only show/hide */
944 if (!(_flags(child) & MADF_SHOWME))
945 continue;
946 #endif
947 DoMethod(child, MUIM_Cleanup);
949 return FALSE;
953 if (data->flags & GROUP_VIRTUAL)
955 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
956 (IPTR) & data->ehn);
959 return TRUE;
963 /**************************************************************************
964 MUIM_Cleanup
965 **************************************************************************/
966 IPTR Group__MUIM_Cleanup(struct IClass *cl, Object *obj, Msg msg)
968 struct MUI_GroupData *data = INST_DATA(cl, obj);
969 Object *cstate;
970 Object *child;
971 struct MinList *ChildList = NULL;
973 if (data->flags & GROUP_VIRTUAL)
975 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
976 (IPTR) & data->ehn);
979 get(data->family, MUIA_Family_List, &(ChildList));
980 cstate = (Object *) ChildList->mlh_Head;
981 while ((child = NextObject(&cstate)))
983 #if 0 /* SHOWME affects only show/hide */
984 if (!(_flags(child) & MADF_SHOWME))
985 continue;
986 #endif
987 DoMethodA(child, (Msg) msg);
989 return DoSuperMethodA(cl, obj, (Msg) msg);
994 /**************************************************************************
995 MUIM_Draw - draw the group
996 **************************************************************************/
997 IPTR Group__MUIM_Draw(struct IClass *cl, Object *obj,
998 struct MUIP_Draw *msg)
1000 struct MUI_GroupData *data = INST_DATA(cl, obj);
1001 Object *cstate;
1002 Object *child;
1003 struct MinList *ChildList = NULL;
1004 struct Rectangle group_rect; /* child_rect; */
1005 int page;
1006 struct Region *region = NULL;
1007 APTR clip = (APTR) - 1;
1009 /* D(bug("Group_Draw(%lx) %ldx%ldx%ldx%ld upd=%d page=%d\n", */
1010 /* obj,_left(obj),_top(obj),_right(obj),_bottom(obj), */
1011 /* data->update, data->active_page)); */
1012 /* D(bug("Group_Draw(%p) msg=0x%08lx flags=0x%08lx\n", */
1013 /* obj, msg->flags, _flags(obj))); */
1015 if (data->flags & GROUP_CHANGING)
1016 return FALSE;
1018 if (muiGlobalInfo(obj)->mgi_Prefs->window_redraw ==
1019 WINDOW_REDRAW_WITHOUT_CLEAR)
1021 region = NewRegion();
1022 if (region)
1024 struct Rectangle rect;
1026 rect.MinX = _left(obj);
1027 rect.MinY = _top(obj);
1028 rect.MaxX = _right(obj);
1029 rect.MaxY = _bottom(obj);
1031 OrRectRegion(region, &rect);
1032 page = -1;
1033 get(data->family, MUIA_Family_List, &(ChildList));
1034 cstate = (Object *) ChildList->mlh_Head;
1035 while ((child = NextObject(&cstate)))
1037 /*page++; */
1038 /* redraw problem with colorwheel in coloradjust register */
1039 if ((data->flags & GROUP_PAGEMODE)
1040 && ((page != data->active_page)
1041 && (child != data->titlegroup)))
1042 continue;
1044 if ((muiAreaData(child)->mad_Flags & MADF_CANDRAW)
1045 && (_width(child) > 0) && (_height(child) > 0))
1047 rect.MinX = MAX(_left(child), _mleft(obj));
1048 rect.MinY = MAX(_top(child), _mtop(obj));
1049 rect.MaxX = MIN(_right(child), _mright(obj));
1050 rect.MaxY = MIN(_bottom(child), _mbottom(obj));
1052 if ((rect.MaxX >= rect.MinX)
1053 && (rect.MaxY >= rect.MinY))
1055 ClearRectRegion(region, &rect);
1060 clip = MUI_AddClipRegion(muiRenderInfo(obj), region);
1064 DoSuperMethodA(cl, obj, (Msg) msg);
1066 if (region)
1068 MUI_RemoveClipRegion(muiRenderInfo(obj), clip);
1069 region = NULL;
1072 else
1074 DoSuperMethodA(cl, obj, (Msg) msg);
1076 /* D(bug("Group_Draw(%p) (after dsma) msg=0x%08lx flags=0x%08lx\n", */
1077 /* obj, msg->flags, _flags(obj))); */
1079 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 1)
1082 * update is set when changing active page of a page group
1083 * need to redraw background ourself
1085 DoMethod(obj, MUIM_DrawBackground,
1086 _mleft(obj), _mtop(obj), _mwidth(obj), _mheight(obj),
1087 _mleft(obj), _mtop(obj), 0);
1089 data->update = 0;
1091 else
1093 if ((msg->flags & MADF_DRAWUPDATE) && data->update == 2)
1095 LONG left, top, right, bottom;
1096 LONG diff_virt_offx = data->virt_offx - data->old_virt_offx;
1097 LONG diff_virt_offy = data->virt_offy - data->old_virt_offy;
1098 struct Rectangle rect;
1099 struct Rectangle *clip_rect = &muiRenderInfo(obj)->mri_ClipRect;
1101 data->update = 0;
1103 if (!diff_virt_offx && !diff_virt_offy)
1105 return 1;
1108 /* sba: I don't know how MUI handle this but ScrollRasterBF() made problems when scrolling
1109 ** a (partly visible) virtual groups in a virtual group, because e.g. _mtop() is then
1110 ** smaller than the region. ScrollRasterBF() on AmigaOS then marks the complete region
1111 ** as damaged. Using ScrollWindowRaster() solved that problem but it flickers then.
1112 ** To avoid this we prevent that the scroll area is out of the region bounds.
1113 ** The region bounds are setted in MUI_Redraw() but should probably should go in the
1114 ** MUI's clip functions
1117 left = MAX(_mleft(obj), clip_rect->MinX);
1118 top = MAX(_mtop(obj), clip_rect->MinY);
1119 right = MIN(_mright(obj), clip_rect->MaxX);
1120 bottom = MIN(_mbottom(obj), clip_rect->MaxY);
1122 /* old code was
1123 ** ScrollRasterBF(_rp(obj), diff_virt_offx, diff_virt_offy, _mleft(obj), _mtop(obj), _mright(obj),_mbottom(obj));
1126 ScrollWindowRaster(_window(obj), diff_virt_offx, diff_virt_offy,
1127 left, top, right, bottom);
1129 if ((region = NewRegion()))
1131 if (diff_virt_offx)
1133 rect.MinY = top;
1134 rect.MaxY = bottom;
1136 if (diff_virt_offx > 0)
1138 rect.MinX = right - diff_virt_offx + 1;
1139 if (rect.MinX < left)
1140 rect.MinX = left;
1141 rect.MaxX = right;
1143 else
1145 rect.MinX = left;
1146 rect.MaxX = left - diff_virt_offx - 1;
1147 if (rect.MaxX > right)
1148 rect.MaxX = right;
1151 if (rect.MinX <= rect.MaxX)
1153 DoMethod(obj, MUIM_DrawBackground,
1154 rect.MinX, rect.MinY,
1155 rect.MaxX - rect.MinX + 1,
1156 rect.MaxY - rect.MinY + 1,
1157 rect.MinX, rect.MinY, 0);
1159 OrRectRegion(region, &rect);
1163 if (diff_virt_offy)
1165 rect.MinX = left;
1166 rect.MaxX = right;
1168 if (diff_virt_offy > 0)
1170 rect.MinY = bottom - diff_virt_offy + 1;
1171 if (rect.MinY < top)
1172 rect.MinY = top;
1173 rect.MaxY = bottom;
1175 else
1177 rect.MinY = top;
1178 rect.MaxY = top - diff_virt_offy - 1;
1179 if (rect.MaxY > bottom)
1180 rect.MaxY = bottom;
1182 if (rect.MinY <= rect.MaxY)
1184 DoMethod(obj, MUIM_DrawBackground,
1185 rect.MinX, rect.MinY,
1186 rect.MaxX - rect.MinX + 1,
1187 rect.MaxY - rect.MinY + 1,
1188 rect.MinX, rect.MinY, 0);
1190 OrRectRegion(region, &rect);
1196 else
1198 if (!(msg->flags & MADF_DRAWOBJECT)
1199 && !(msg->flags & MADF_DRAWALL))
1200 return TRUE;
1204 if (data->flags & GROUP_VIRTUAL && !region)
1206 /* Not really needed if MUI Draws all the objects, maybe that's
1207 * what DRAWALL is for??? */
1208 if ((region = NewRegion()))
1210 struct Rectangle rect;
1211 rect.MinX = _mleft(obj);
1212 rect.MinY = _mtop(obj);
1213 rect.MaxX = _mright(obj);
1214 rect.MaxY = _mbottom(obj);
1215 OrRectRegion(region, &rect);
1219 /* Add clipping region if we have one */
1220 if (region)
1221 clip = MUI_AddClipRegion(muiRenderInfo(obj), region);
1223 group_rect = muiRenderInfo(obj)->mri_ClipRect;
1224 page = -1;
1225 get(data->family, MUIA_Family_List, &(ChildList));
1226 cstate = (Object *) ChildList->mlh_Head;
1227 while ((child = NextObject(&cstate)))
1229 if (!(_flags(child) & MADF_SHOWME))
1230 continue;
1232 if (child != data->titlegroup)
1233 ++page;
1235 if ((data->flags & GROUP_PAGEMODE) && ((page != data->active_page)
1236 && (child != data->titlegroup)))
1238 continue;
1241 // msg->flags |= MADF_DRAWOBJECT; /* yup, do not forget */
1243 // child_rect.MinX = _left(child);
1244 // child_rect.MinY = _top(child);
1245 // child_rect.MaxX = _right(child);
1246 // child_rect.MaxY = _bottom(child);
1247 /* g_print("intersect: a=(%d, %d, %d, %d) b=(%d, %d, %d, %d)\n", */
1248 /* group_rect.x, group_rect.y, */
1249 /* group_rect.width, group_rect.height, */
1250 /* child_rect.x, child_rect.y, */
1251 /* child_rect.width, child_rect.height); */
1253 // if (gdk_rectangle_intersect(&group_rect, &child_rect,
1254 // &muiRenderInfo(obj)->mri_ClipRect))
1255 // DoMethodA(child, (Msg)msg);
1256 /* if (((msg->flags & MADF_DRAWUPDATE) && data->update) */
1257 /* || (data->flags & GROUP_PAGEMODE)) */
1258 MUI_Redraw(child, MADF_DRAWOBJECT);
1259 /* else */
1260 /* MUI_Redraw(child, msg->flags); */
1261 muiRenderInfo(obj)->mri_ClipRect = group_rect;
1262 /* g_print("set back clip to (%d, %d, %d, %d)\n", */
1263 /* group_rect.x, group_rect.y, group_rect.width, group_rect.height); */
1265 /* D(bug("Group_Draw(%p) end\n", obj)); */
1267 if (data->flags & GROUP_VIRTUAL && region && clip != (APTR) - 1)
1269 MUI_RemoveClipRegion(muiRenderInfo(obj), clip);
1272 data->old_virt_offx = data->virt_offx;
1273 data->old_virt_offy = data->virt_offy;
1274 data->update = 0;
1276 return TRUE;
1280 #define END_MINMAX() \
1281 tmp.MaxHeight = MAX(tmp.MaxHeight, tmp.MinHeight); \
1282 tmp.MaxWidth = MAX(tmp.MaxWidth, tmp.MinWidth); \
1283 tmp.DefHeight = CLAMP(tmp.DefHeight, tmp.MinHeight, tmp.MaxHeight); \
1284 tmp.DefWidth = CLAMP(tmp.DefWidth, tmp.MinWidth, tmp.MaxWidth); \
1285 msg->MinMaxInfo->MinWidth += tmp.MinWidth; \
1286 msg->MinMaxInfo->MinHeight += tmp.MinHeight; \
1287 msg->MinMaxInfo->MaxWidth += tmp.MaxWidth; \
1288 msg->MinMaxInfo->MaxHeight += tmp.MaxHeight; \
1289 msg->MinMaxInfo->DefWidth += tmp.DefWidth; \
1290 msg->MinMaxInfo->DefHeight += tmp.DefHeight;
1293 * MinMax calculation function. When this is called,
1294 * the children of your group have already been asked
1295 * about their min/max dimension so you can use their
1296 * dimensions to calculate yours.
1298 * Notes:
1299 * - Init minwidth and maxwidth with size needed for total child spacing.
1300 * - 1st pass to find maximum minimum width, to set minwidth of each child
1301 * if they should have the same width (for a row of buttons ...)
1302 * - Adjust minwidth w/o making object bigger than their max size.
1304 static void group_minmax_horiz(struct IClass *cl, Object *obj,
1305 struct MinList *children, struct MUIP_AskMinMax *msg)
1307 struct MUI_GroupData *data = INST_DATA(cl, obj);
1308 Object *cstate;
1309 Object *child;
1310 struct MUI_MinMax tmp;
1311 WORD maxminwidth = 0;
1312 BOOL found_nonzero_vweight = FALSE;
1314 tmp.MinHeight = 0;
1315 tmp.DefHeight = 0;
1316 tmp.MaxHeight = MUI_MAXMAX;
1317 tmp.MinWidth = tmp.DefWidth = tmp.MaxWidth =
1318 (data->num_visible_children - 1) * data->horiz_spacing;
1320 if (data->flags & GROUP_SAME_WIDTH)
1322 cstate = (Object *) children->mlh_Head;
1323 while ((child = NextObject(&cstate)))
1325 if (IS_HIDDEN(child))
1326 continue;
1327 maxminwidth = MAX(maxminwidth, _minwidth(child));
1331 data->samesize_maxmin_horiz = maxminwidth;
1332 /* D(bug("group_minmax_horiz(%p) : maxminwidth=%d\n", obj, maxminwidth)); */
1334 data->horiz_weight_sum = 0;
1335 cstate = (Object *) children->mlh_Head;
1336 while ((child = NextObject(&cstate)))
1338 WORD minwidth;
1340 if (IS_HIDDEN(child))
1341 continue;
1342 if (data->flags & GROUP_SAME_WIDTH)
1344 minwidth = MAX(maxminwidth, _minwidth(child));
1345 minwidth = MIN(minwidth, _maxwidth(child));
1347 else
1348 minwidth = _minwidth(child);
1349 tmp.MinWidth += minwidth;
1350 tmp.DefWidth += w0_defwidth(child);
1351 tmp.MaxWidth += w0_maxwidth(child);
1352 tmp.MaxWidth = MIN(tmp.MaxWidth, MUI_MAXMAX);
1353 tmp.MinHeight = MAX(tmp.MinHeight, _minheight(child));
1354 tmp.DefHeight = MAX(tmp.DefHeight, _defheight(child));
1356 if all children have null weight then maxheight=minheight
1357 if all but some children have null weights, the maxheight
1358 is the min of all maxheights
1360 tmp.MaxHeight = MIN(tmp.MaxHeight, _maxheight(child));
1361 data->horiz_weight_sum += _hweight(child);
1362 if (_vweight(child) > 0)
1364 found_nonzero_vweight = TRUE;
1367 if (!found_nonzero_vweight)
1369 tmp.MaxHeight = tmp.MinHeight;
1372 //if (data->flags & GROUP_VIRTUAL)
1374 //kprintf("# min %d x %d def %d x %d max %d x %d\n",
1375 // tmp.MinWidth, tmp.MinHeight,
1376 // tmp.DefWidth, tmp.DefHeight,
1377 // tmp.MaxWidth, tmp.MaxHeight);
1380 END_MINMAX();
1383 /* minmax calculation for vertical groups (see group_minmax_horiz)
1385 static void group_minmax_vert(struct IClass *cl, Object *obj,
1386 struct MinList *children, struct MUIP_AskMinMax *msg)
1388 struct MUI_GroupData *data = INST_DATA(cl, obj);
1389 Object *cstate;
1390 Object *child;
1391 struct MUI_MinMax tmp;
1392 WORD maxminheight = 0;
1393 BOOL found_nonzero_hweight = FALSE;
1395 tmp.MinWidth = 0;
1396 tmp.DefWidth = 0;
1397 tmp.MaxWidth = MUI_MAXMAX;
1398 tmp.MinHeight = tmp.DefHeight = tmp.MaxHeight =
1399 (data->num_visible_children - 1) * data->vert_spacing;
1401 if (data->flags & GROUP_SAME_HEIGHT)
1403 cstate = (Object *) children->mlh_Head;
1404 while ((child = NextObject(&cstate)))
1406 if (IS_HIDDEN(child))
1407 continue;
1408 maxminheight = MAX(maxminheight, _minheight(child));
1412 data->samesize_maxmin_vert = maxminheight;
1413 data->vert_weight_sum = 0;
1414 cstate = (Object *) children->mlh_Head;
1415 while ((child = NextObject(&cstate)))
1417 if (IS_HIDDEN(child))
1418 continue;
1420 if (data->flags & GROUP_SAME_HEIGHT)
1421 _minheight(child) = MIN(maxminheight, w0_maxheight(child));
1422 tmp.MinHeight += _minheight(child);
1423 tmp.DefHeight += w0_defheight(child);
1424 tmp.MaxHeight += w0_maxheight(child);
1425 tmp.MaxHeight = MIN(tmp.MaxHeight, MUI_MAXMAX);
1426 tmp.MinWidth = MAX(tmp.MinWidth, _minwidth(child));
1427 tmp.DefWidth = MAX(tmp.DefWidth, _defwidth(child));
1428 tmp.MaxWidth = MIN(tmp.MaxWidth, _maxwidth(child));
1429 data->vert_weight_sum += _vweight(child);
1430 if (_hweight(child) > 0)
1432 found_nonzero_hweight = TRUE;
1435 if (!found_nonzero_hweight)
1437 tmp.MaxWidth = tmp.MinWidth;
1440 END_MINMAX();
1444 static void
1445 minmax_2d_rows_pass(struct MUI_GroupData *data, struct MinList *children,
1446 struct MUI_MinMax *req, WORD maxmin_height, WORD maxdef_height)
1448 int i, j;
1449 Object *cstate;
1450 Object *child;
1452 /* do not rewind after the while, to process line by line */
1453 cstate = (Object *) children->mlh_Head;
1454 /* for each row */
1455 for (i = 0; i < data->rows; i++)
1457 /* calculate min and max height of this row */
1458 int min_h = 0, def_h = 0, max_h = MUI_MAXMAX;
1459 BOOL found_nonzero_vweight = FALSE;
1461 data->row_infos[i].weight = 0;
1463 j = 0;
1464 while ((child = NextObject(&cstate)))
1466 if (IS_HIDDEN(child))
1467 continue;
1468 if (data->flags & GROUP_SAME_HEIGHT)
1470 _minheight(child) = MIN(maxmin_height, w0_maxheight(child));
1471 _defheight(child) = MIN(maxdef_height, w0_maxheight(child));
1473 min_h = MAX(min_h, _minheight(child));
1474 def_h = MAX(def_h, w0_defheight(child));
1475 max_h = MIN(max_h, _maxheight(child));
1476 if (_vweight(child) > 0)
1478 found_nonzero_vweight = TRUE;
1479 data->row_infos[i].weight += _vweight(child);
1481 ++j;
1482 if ((j % data->columns) == 0)
1483 break;
1485 if (!found_nonzero_vweight)
1486 max_h = min_h;
1487 else
1488 max_h = MAX(max_h, min_h);
1489 /* D(bug("row %d : min_h=%d max_h=%d\n", i, min_h, max_h)); */
1491 data->row_infos[i].min = min_h;
1492 data->row_infos[i].max = max_h;
1493 data->vert_weight_sum += data->row_infos[i].weight;
1495 req->MinHeight += min_h;
1496 req->DefHeight += def_h;
1497 req->MaxHeight += max_h;
1498 if (req->MaxHeight > MUI_MAXMAX)
1499 req->MaxHeight = MUI_MAXMAX;
1504 static void
1505 minmax_2d_columns_pass(struct MUI_GroupData *data, struct MinList *children,
1506 struct MUI_MinMax *req, WORD maxmin_width, WORD maxdef_width)
1508 int i, j;
1509 Object *cstate;
1510 Object *child;
1512 for (i = 0; i < data->columns; i++)
1514 /* calculate min and max width of this column */
1515 int min_w = 0, def_w = 0, max_w = MUI_MAXMAX;
1516 BOOL found_nonzero_hweight = FALSE;
1518 data->col_infos[i].weight = 0;
1520 j = 0;
1521 /* process all children to get children on a column */
1522 cstate = (Object *) children->mlh_Head;
1523 while ((child = NextObject(&cstate)))
1525 if (IS_HIDDEN(child))
1526 continue;
1527 ++j;
1528 if (((j - 1) % data->columns) != i)
1529 continue;
1530 if (data->flags & GROUP_SAME_WIDTH)
1532 _minwidth(child) = MIN(maxmin_width, w0_maxwidth(child));
1533 _defwidth(child) = MIN(maxdef_width, w0_maxwidth(child));
1535 min_w = MAX(min_w, _minwidth(child));
1536 def_w = MAX(def_w, w0_defwidth(child));
1538 /* this handles the case of null weight children, which limit
1539 * the max size if they're alone, but not if they are with
1540 * non-null weight obj
1542 max_w = MIN(max_w, _maxwidth(child));
1543 if (_hweight(child) > 0)
1545 found_nonzero_hweight = TRUE;
1546 data->col_infos[i].weight += _hweight(child);
1549 if (!found_nonzero_hweight)
1550 max_w = min_w;
1551 else
1552 max_w = MAX(max_w, min_w);
1553 /* D(bug("col %d : min_w=%d max_w=%d\n", i, min_w, max_w)); */
1555 data->col_infos[i].min = min_w;
1556 data->col_infos[i].max = max_w;
1557 data->horiz_weight_sum += data->col_infos[i].weight;
1559 req->MinWidth += min_w;
1560 req->DefWidth += def_w;
1561 req->MaxWidth += max_w;
1562 if (req->MaxWidth > MUI_MAXMAX)
1563 req->MaxWidth = MUI_MAXMAX;
1567 static void
1568 group_minmax_2d(struct IClass *cl, Object *obj,
1569 struct MinList *children, struct MUIP_AskMinMax *msg)
1571 struct MUI_GroupData *data = INST_DATA(cl, obj);
1572 Object *cstate;
1573 Object *child;
1574 struct MUI_MinMax tmp;
1575 WORD maxmin_width;
1576 WORD maxmin_height;
1577 WORD maxdef_width;
1578 WORD maxdef_height;
1580 if (!data->columns)
1582 if (data->num_children % data->rows)
1584 data->columns = 1;
1585 data->rows = data->num_children;
1587 else
1588 data->columns = data->num_children / data->rows;
1590 else
1592 if (data->num_children % data->columns)
1594 data->rows = 1;
1595 data->columns = data->num_children;
1597 else
1598 data->rows = data->num_children / data->columns;
1601 if (data->columns < 1)
1602 data->columns = 1;
1603 if (data->rows < 1)
1604 data->rows = 1;
1606 if (data->row_infos != NULL)
1607 mui_free(data->row_infos);
1609 data->row_infos = mui_alloc(data->rows * sizeof(struct layout2d_elem));
1610 if (NULL == data->row_infos)
1611 return;
1613 if (data->col_infos != NULL)
1614 mui_free(data->col_infos);
1616 data->col_infos =
1617 mui_alloc(data->columns * sizeof(struct layout2d_elem));
1618 if (NULL == data->col_infos)
1619 return;
1621 data->horiz_weight_sum = 0;
1622 data->vert_weight_sum = 0;
1624 tmp.MinHeight = tmp.DefHeight = tmp.MaxHeight =
1625 (data->rows - 1) * data->vert_spacing;
1626 tmp.MinWidth = tmp.DefWidth = tmp.MaxWidth =
1627 (data->columns - 1) * data->horiz_spacing;
1628 /* get minimum dims if same dims for all children are needed */
1629 maxmin_width = 0;
1630 maxmin_height = 0;
1631 maxdef_width = 0;
1632 maxdef_height = 0;
1634 if ((data->flags & GROUP_SAME_WIDTH)
1635 || (data->flags & GROUP_SAME_HEIGHT))
1637 cstate = (Object *) children->mlh_Head;
1638 while ((child = NextObject(&cstate)))
1640 if (!(_flags(child) & MADF_SHOWME))
1641 continue;
1642 maxmin_width = MAX(maxmin_width, _minwidth(child));
1643 maxmin_height = MAX(maxmin_height, _minheight(child));
1644 maxdef_width = MAX(maxdef_width, w0_defwidth(child));
1645 maxdef_height = MAX(maxdef_height, w0_defheight(child));
1647 /* g_print("2d group: mminw=%d mminh=%d\n", */
1648 /* maxmin_width, maxmin_height); */
1650 if (data->flags & GROUP_SAME_HEIGHT)
1651 data->samesize_maxmin_vert = maxmin_height;
1652 else
1653 data->samesize_maxmin_vert = 0;
1655 if (data->flags & GROUP_SAME_WIDTH)
1656 data->samesize_maxmin_horiz = maxmin_width;
1657 else
1658 data->samesize_maxmin_horiz = 0;
1660 minmax_2d_rows_pass(data, children, &tmp, maxmin_height, maxdef_height);
1661 minmax_2d_columns_pass(data, children, &tmp, maxmin_width,
1662 maxdef_width);
1664 END_MINMAX();
1668 static void
1669 group_minmax_pagemode(struct IClass *cl, Object *obj,
1670 struct MinList *children, struct MUIP_AskMinMax *msg)
1672 Object *cstate;
1673 Object *child;
1674 struct MUI_GroupData *data = INST_DATA(cl, obj);
1675 struct MUI_MinMax tmp = { 0, 0, MUI_MAXMAX, MUI_MAXMAX, 0, 0 };
1677 cstate = (Object *) children->mlh_Head;
1679 D(bug("minmax_pagemode(%lx)\n", obj, tmp.DefWidth));
1681 while ((child = NextObject(&cstate)))
1683 if (!(_flags(child) & MADF_SHOWME))
1684 continue;
1686 if (child == data->titlegroup)
1687 continue;
1689 tmp.MinHeight = MAX(tmp.MinHeight, _minheight(child));
1690 D(bug("minmax_pagemode(%p) minh child = %d tmpmin=%d\n", obj,
1691 _minheight(child), tmp.MinHeight));
1692 tmp.MinWidth = MAX(tmp.MinWidth, _minwidth(child));
1693 tmp.MaxHeight = MIN(tmp.MaxHeight, w0_maxheight(child));
1694 tmp.MaxWidth = MIN(tmp.MaxWidth, w0_maxwidth(child));
1695 tmp.DefHeight = MAX(tmp.DefHeight,
1696 ((w0_defheight(child) <
1697 MUI_MAXMAX) ? w0_defheight(child) : tmp.DefHeight));
1698 tmp.DefWidth =
1699 MAX(tmp.DefWidth,
1700 ((w0_defwidth(child) <
1701 MUI_MAXMAX) ? w0_defwidth(child) : tmp.DefWidth));
1702 D(bug("minmax_pagemode(%lx) defw = %ld\n", obj, tmp.DefWidth));
1705 if (data->titlegroup)
1707 tmp.MinHeight += _minheight(data->titlegroup);
1708 tmp.MaxHeight += w0_maxheight(data->titlegroup);
1709 tmp.DefHeight += w0_defheight(data->titlegroup);
1712 END_MINMAX();
1715 /**************************************************************************
1716 MUIM_AskMinMax : ask children about min/max sizes, then
1717 either call a hook, or the builtin method, to calculate our minmax
1718 **************************************************************************/
1719 IPTR Group__MUIM_AskMinMax(struct IClass *cl, Object *obj,
1720 struct MUIP_AskMinMax *msg)
1722 struct MUI_GroupData *data = INST_DATA(cl, obj);
1723 struct MUI_LayoutMsg lm;
1724 struct MUIP_AskMinMax childMsg;
1725 struct MUI_MinMax childMinMax;
1726 Object *cstate;
1727 Object *child;
1728 LONG super_minwidth, super_minheight;
1731 * let our superclass first fill in its size with frame, inner spc etc ...
1733 DoSuperMethodA(cl, obj, (Msg) msg);
1734 super_minwidth = msg->MinMaxInfo->MinWidth;
1735 super_minheight = msg->MinMaxInfo->MinHeight;
1738 * Ask children
1740 childMsg.MethodID = msg->MethodID;
1741 childMsg.MinMaxInfo = &childMinMax;
1742 get(data->family, MUIA_Family_List, &(lm.lm_Children));
1744 cstate = (Object *) lm.lm_Children->mlh_Head;
1746 while ((child = NextObject(&cstate)))
1748 if (!(_flags(child) & MADF_SHOWME))
1749 /* BORDERGADGETs should handle this itself */
1750 continue;
1751 /* Ask child */
1752 DoMethodA(child, (Msg) & childMsg);
1753 /* D(bug("*** group %lx, child %lx min=%ld,%ld\n", */
1754 /* obj, child, childMinMax.MinWidth, childMinMax.MinHeight)); */
1755 __area_finish_minmax(child, childMsg.MinMaxInfo);
1759 * Use children infos to calculate group size
1761 if (data->flags & GROUP_PAGEMODE)
1763 D(bug("minmax_pagemode(%p) minh initial = %d\n", obj,
1764 msg->MinMaxInfo->MinHeight));
1765 group_minmax_pagemode(cl, obj, lm.lm_Children, msg);
1766 D(bug("minmax_pagemode(%p) minh = %d\n", obj,
1767 msg->MinMaxInfo->MinHeight));
1769 else if (data->layout_hook)
1771 lm.lm_Type = MUILM_MINMAX;
1772 CallHookPkt(data->layout_hook, obj, &lm);
1774 if (lm.lm_MinMax.MaxHeight < lm.lm_MinMax.MinHeight)
1775 lm.lm_MinMax.MaxHeight = lm.lm_MinMax.MinHeight;
1776 if (lm.lm_MinMax.DefHeight < lm.lm_MinMax.MinHeight)
1777 lm.lm_MinMax.DefHeight = lm.lm_MinMax.MinHeight;
1778 if (lm.lm_MinMax.MaxWidth < lm.lm_MinMax.MinWidth)
1779 lm.lm_MinMax.MaxWidth = lm.lm_MinMax.MinWidth;
1780 if (lm.lm_MinMax.DefWidth < lm.lm_MinMax.MinWidth)
1781 lm.lm_MinMax.DefWidth = lm.lm_MinMax.MinWidth;
1783 //kprintf("### min %d x %d def %d x %d max %d x %d\n",
1784 // msg->MinMaxInfo->MinWidth,
1785 // msg->MinMaxInfo->MinHeight,
1786 // msg->MinMaxInfo->DefWidth,
1787 // msg->MinMaxInfo->DefHeight,
1788 // msg->MinMaxInfo->MaxWidth,
1789 // msg->MinMaxInfo->MaxHeight);
1791 msg->MinMaxInfo->MinWidth += lm.lm_MinMax.MinWidth;
1792 msg->MinMaxInfo->MinHeight += lm.lm_MinMax.MinHeight;
1793 msg->MinMaxInfo->MaxWidth += lm.lm_MinMax.MaxWidth;
1794 if (msg->MinMaxInfo->MaxWidth > MUI_MAXMAX)
1795 msg->MinMaxInfo->MaxWidth = MUI_MAXMAX;
1796 msg->MinMaxInfo->MaxHeight += lm.lm_MinMax.MaxHeight;
1797 if (msg->MinMaxInfo->MaxHeight > MUI_MAXMAX)
1798 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
1799 msg->MinMaxInfo->DefWidth += lm.lm_MinMax.DefWidth;
1800 msg->MinMaxInfo->DefHeight += lm.lm_MinMax.DefHeight;
1802 //kprintf("#### min %d x %d def %d x %d max %d x %d\n",
1803 // msg->MinMaxInfo->MinWidth,
1804 // msg->MinMaxInfo->MinHeight,
1805 // msg->MinMaxInfo->DefWidth,
1806 // msg->MinMaxInfo->DefHeight,
1807 // msg->MinMaxInfo->MaxWidth,
1808 // msg->MinMaxInfo->MaxHeight);
1811 else
1813 if ((data->rows == 1) && (data->columns == 1))
1815 data->num_visible_children =
1816 Group_GetNumVisibleChildren(data, lm.lm_Children);
1817 if (data->flags & GROUP_HORIZ)
1818 group_minmax_horiz(cl, obj, lm.lm_Children, msg);
1819 else
1820 group_minmax_vert(cl, obj, lm.lm_Children, msg);
1822 else
1824 group_minmax_2d(cl, obj, lm.lm_Children, msg);
1828 if (data->flags & GROUP_VIRTUAL)
1830 data->saved_minwidth = msg->MinMaxInfo->MinWidth;
1831 data->saved_minheight = msg->MinMaxInfo->MinHeight;
1832 msg->MinMaxInfo->MinWidth = super_minwidth + 2;
1833 msg->MinMaxInfo->MinHeight = super_minheight + 2;
1835 //kprintf("## min %d x %d def %d x %d max %d x %d\n",
1836 // msg->MinMaxInfo->MinWidth,
1837 // msg->MinMaxInfo->MinHeight,
1838 // msg->MinMaxInfo->DefWidth,
1839 // msg->MinMaxInfo->DefHeight,
1840 // msg->MinMaxInfo->MaxWidth,
1841 // msg->MinMaxInfo->MaxHeight);
1844 return 0;
1849 // enforce minmax constraint, but also update total growable/shrinkable weights
1850 // while we're at it
1851 static void Layout1D_minmax_constraint(WORD *sizep, WORD minsize,
1852 WORD maxsize, WORD *remainp, WORD *sizegrowp, WORD *sizeshrinkp,
1853 ULONG *weightgrowp, ULONG *weightshrinkp, UWORD weight, WORD samesize)
1855 WORD size = *sizep, remain = *remainp,
1856 sizegrow = *sizegrowp, sizeshrink = *sizeshrinkp;
1857 ULONG weightgrow = *weightgrowp, weightshrink = *weightshrinkp;
1859 /* D(bug("L1D_minmax_c size=%d min=%d max=%d w=%d ss=%d\n", */
1860 /* size, minsize, maxsize, */
1861 /* weight, samesize)); */
1863 if ((samesize > 0) && (weight == 0))
1865 remain += size - samesize;
1866 size = samesize;
1867 sizeshrink -= size;
1868 sizegrow -= size;
1871 if (size <= minsize) // too little
1873 remain += size - minsize;
1874 size = minsize;
1875 sizeshrink -= size;
1876 if (size == maxsize)
1877 sizegrow -= size;
1879 else if (size >= maxsize) // too big
1881 remain += size - maxsize;
1882 size = maxsize;
1883 sizegrow -= size;
1886 if (!((samesize > 0) && (weight == 0)))
1888 if (size < maxsize)
1889 weightgrow += weight;
1890 if (size > minsize)
1891 weightshrink += weight;
1894 *sizep = size;
1895 *remainp = remain;
1896 *sizegrowp = sizegrow;
1897 *sizeshrinkp = sizeshrink;
1898 *weightgrowp = weightgrow;
1899 *weightshrinkp = weightshrink;
1903 // redistribute excess size to growable child, or reduce size of a shrinkable
1904 // child
1905 static void Layout1D_redistribution(WORD *sizep, WORD minsize,
1906 WORD maxsize, WORD *remainp, WORD *sizegrowp, WORD *sizeshrinkp,
1907 ULONG *weightgrowp, ULONG *weightshrinkp, UWORD weight)
1909 WORD size = *sizep, remain = *remainp,
1910 sizegrow = *sizegrowp, sizeshrink = *sizeshrinkp;
1911 ULONG weightgrow = *weightgrowp, weightshrink = *weightshrinkp;
1913 if (weight == 0)
1914 return;
1916 if ((remain > 0) && (size < maxsize))
1918 LONG newsize;
1920 newsize = (sizegrow * weight + weightgrow / 2) / weightgrow;
1922 /* D(bug("newsize=%ld == size_growa=%ld * w=%ld / weight_grow=%d\n", */
1923 /* newsize, sizegrow, weight, weightgrow)); */
1925 /* take care of off-by-1 errors that may toggle remainder sign
1926 * by ensuring convergence to 0
1928 if (remain - newsize + size < 0)
1930 /* D(bug("adding remainder=%d => size = %d\n", */
1931 /* remain, size + remain)); */
1932 size += remain;
1933 remain = 0;
1935 else
1937 remain -= newsize - size;
1938 size = newsize;
1939 sizegrow -= size;
1940 weightgrow -= weight;
1943 else if ((remain < 0) && (size > minsize))
1945 LONG newsize;
1947 newsize = (sizeshrink * weight + weightshrink / 2) / weightshrink;
1949 /* D(bug("newsize=%ld == size_shrinkables=%ld * w=%ld " */
1950 /* "/ weight_shrinkables=%d\n", */
1951 /* newsize, sizeshrink, weight, weightshrink)); */
1953 if (remain - newsize + size > 0)
1955 /* D(bug("adding remainder=%d => size = %d\n", */
1956 /* remain, size + remain)); */
1957 size += remain;
1958 remain = 0;
1960 else
1962 remain -= newsize - size;
1963 size = newsize;
1964 sizeshrink -= size;
1965 weightshrink -= weight;
1969 *sizep = size;
1970 *remainp = remain;
1971 *sizegrowp = sizegrow;
1972 *sizeshrinkp = sizeshrink;
1973 *weightgrowp = weightgrow;
1974 *weightshrinkp = weightshrink;
1978 // 2 passes at most, less on average (0.5 or 1.5), each does
1979 // - a minmax clamping, evenutally adding to a remainder
1980 // (remainder = missing (underflow) or remaining (overflow) space compared
1981 // to ideal sizes where children fill the whole group)
1982 // - a redistribution of the remainder, by growing (pos. remainder) or
1983 // shrinking (neg. remainder) children able to support it.
1985 // Occasionnaly the first time the redistribution is done, the minmax
1986 // constraint can be broken, thus the extra pass to check and eventually
1987 // redistribute. The second redistribution never breaks minmax constraint
1988 // (there should be a mathematical proof, but feel free to prove me wrong
1989 // with an example)
1990 static void Layout1D_minmax_constraints_and_redistrib(struct MinList
1991 *children, WORD total_size, WORD remainder, WORD samesize,
1992 BOOL group_horiz)
1994 Object *cstate;
1995 Object *child;
1996 int i;
1998 for (i = 0; i < 2; i++)
2000 WORD size_growables = total_size;
2001 WORD size_shrinkables = total_size;
2002 ULONG weight_growables = 0;
2003 ULONG weight_shrinkables = 0;
2005 /* D(bug("start : rem=%ld, A=%ld, size_growables=%ld, " */
2006 /* "size_shrinkables=%ld\n", */
2007 /* remainder, total_size, size_growables, size_shrinkables)); */
2009 // minmax constraints
2010 cstate = (Object *) children->mlh_Head;
2011 while ((child = NextObject(&cstate)))
2013 /* WORD old_size; */
2015 if (IS_HIDDEN(child))
2016 continue;
2018 if (group_horiz)
2020 /* old_size = _width(child); */
2022 Layout1D_minmax_constraint(&_width(child), _minwidth(child),
2023 _maxwidth(child), &remainder, &size_growables,
2024 &size_shrinkables, &weight_growables,
2025 &weight_shrinkables, _hweight(child), samesize);
2027 /* D(bug("loop1 on %p : width=%d was %d, rem=%d, A=%d, " */
2028 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2029 /* child, _width(child), old_size, remainder, total_size, */
2030 /* size_growables, size_shrinkables, _hweight(child), */
2031 /* _minwidth(child), _maxwidth(child))); */
2033 else // ! group_horiz
2035 /* old_size = _height(child); */
2037 Layout1D_minmax_constraint(&_height(child),
2038 _minheight(child), _maxheight(child), &remainder,
2039 &size_growables, &size_shrinkables, &weight_growables,
2040 &weight_shrinkables, _vweight(child), samesize);
2042 /* D(bug("loop1 on %p : h=%ld was %d, rem=%d, A=%d, " */
2043 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2044 /* child, _height(child), old_size, remainder, total_size,*/
2045 /* size_growables, size_shrinkables, _vweight(child), */
2046 /* _minheight(child), _maxheight(child))); */
2047 } // if (group_horiz)
2048 } // while child, minmax constraints
2050 // mid-pass break
2051 if (remainder == 0)
2052 break;
2054 /* D(bug("mid : rem=%d, A=%d, size_grow=%d, size_shrink=%d, " */
2055 /* "wg=%ld, ws=%ld\n", remainder, total_size, size_growables, */
2056 /* size_shrinkables, weight_growables, weight_shrinkables)); */
2058 // distribute remaining space to possible candidates
2059 cstate = (Object *) children->mlh_Head;
2060 while (((child = NextObject(&cstate)) != NULL) && (remainder != 0))
2062 /* WORD old_size; */
2064 if (IS_HIDDEN(child))
2065 continue;
2067 if (group_horiz)
2069 /* old_size = _width(child); */
2071 Layout1D_redistribution(&_width(child), _minwidth(child),
2072 _maxwidth(child), &remainder, &size_growables,
2073 &size_shrinkables, &weight_growables,
2074 &weight_shrinkables, _hweight(child));
2076 /* D(bug("loop2 on %p : h=%d was %d, rem=%d, A=%d, " */
2077 /* "size_grow=%d, size_shrink=%d\n", child, */
2078 /* _width(child), old_size, remainder, total_size, */
2079 /* size_growables, size_shrinkables)); */
2081 else // ! group_horiz
2083 /* old_size = _height(child); */
2085 Layout1D_redistribution(&_height(child), _minheight(child),
2086 _maxheight(child), &remainder, &size_growables,
2087 &size_shrinkables, &weight_growables,
2088 &weight_shrinkables, _vweight(child));
2090 /* D(bug("loop2 on %p : h=%d was %d, rem=%d, A=%d, " */
2091 /* "size_grow=%d, size_shrink=%d\n", child, */
2092 /* _height(child), old_size, remainder, total_size, */
2093 /* size_growables, size_shrinkables)); */
2094 } // if (group_horiz)
2096 } // while child, redistribution
2098 /* if (remainder != 0) */
2099 /* { */
2100 /* D(bug("end : rem=%ld, A=%ld, size_grow=%ld, size_shrink=%ld\n", */
2101 /* remainder, total_size, size_growables, size_shrinkables)); */
2102 /* } */
2103 // dont break here if remainder == 0, some minmax constraints
2104 // may not be respected
2105 } // end for
2107 // to easily spot layout bugs, nothing like a (division by zero) exception
2108 /* if (remainder != 0) */
2109 /* { */
2110 /* ASSERT(remainder != 0); */
2111 /* D(bug("gonna crash, remainder = %d\n", remainder)); */
2112 /* remainder /= 0; */
2113 /* } */
2117 static void Layout1D_weight_constraint(WORD *total_sizep,
2118 WORD *total_init_sizep,
2119 ULONG *total_weightp, WORD *sizep, UWORD weight, WORD minsize)
2121 if (*total_weightp > 0)
2122 *sizep = (*total_sizep * weight + *total_weightp / 2)
2123 / *total_weightp;
2124 else
2125 *sizep = minsize;
2127 *total_weightp -= weight;
2128 *total_sizep -= *sizep;
2129 *total_init_sizep += *sizep;
2133 static void group_layout_vert(struct IClass *cl, Object *obj,
2134 struct MinList *children)
2136 struct MUI_GroupData *data = INST_DATA(cl, obj);
2137 Object *cstate;
2138 Object *child;
2139 ULONG total_weight;
2140 WORD remainder; /* must converge to 0 to successfully end layout */
2141 WORD total_size;
2142 WORD total_size_backup;
2143 WORD total_init_size; /* total size of the ideally sized children */
2144 WORD width;
2145 WORD left = 0;
2146 WORD top = 0;
2147 WORD layout_width;
2148 WORD layout_height;
2150 //kprintf("group_layout_vert: virtoff = %d,%d\n",
2151 // data->virt_offx, data->virt_offy);
2153 if (data->flags & GROUP_VIRTUAL)
2155 data->virt_mwidth =
2156 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2157 _maxwidth(obj) - _subwidth(obj));
2158 data->virt_mheight =
2159 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2160 _maxheight(obj) - _subheight(obj));
2162 layout_width = data->virt_mwidth;
2163 layout_height = data->virt_mheight;
2165 else
2167 layout_width = _mwidth(obj);
2168 layout_height = _mheight(obj);
2171 total_weight = data->vert_weight_sum;
2172 total_init_size = 0;
2173 total_size =
2174 layout_height - (data->num_visible_children -
2175 1) * data->vert_spacing;
2176 total_size_backup = total_size;
2178 /* D(bug("\nvert layout for %p, A=%d W=%ld\n", */
2179 /* obj, total_size, total_weight)); */
2181 // weight constraints
2182 // calculate ideal size for each object, and total ideal size
2183 cstate = (Object *) children->mlh_Head;
2184 while ((child = NextObject(&cstate)))
2186 if (IS_HIDDEN(child))
2187 continue;
2189 Layout1D_weight_constraint(&total_size, &total_init_size,
2190 &total_weight, &_height(child), _vweight(child),
2191 _minheight(child));
2192 /* D(bug("child %p : ideal=%d w=%ld\n", */
2193 /* child, _height(child), _vweight(child))); */
2194 } // while child, weight constraints
2196 total_size = total_size_backup;
2197 remainder = total_size - total_init_size;
2199 if (data->flags & GROUP_VIRTUAL)
2201 /* This is also true for non virtual groups, but if this would be the
2202 ** case then there is a bug in the layout function
2204 if (total_size < 0)
2205 total_size = 0;
2208 Layout1D_minmax_constraints_and_redistrib(children,
2209 total_size,
2210 remainder,
2211 (data->flags & GROUP_SAME_HEIGHT) ? data->samesize_maxmin_vert : 0,
2212 FALSE);
2214 // do the layout
2215 cstate = (Object *) children->mlh_Head;
2216 while ((child = NextObject(&cstate)))
2218 if (IS_HIDDEN(child))
2219 continue;
2221 width = MIN(_maxwidth(child), layout_width);
2222 width = MAX(width, _minwidth(child));
2223 left = (layout_width - width) / 2;
2225 /* D(bug("child %p -> layout %d x %d\n", */
2226 /* child, width, _height(child))); */
2227 if (!MUI_Layout(child, left, top, width, _height(child), 0))
2228 return;
2229 top += data->vert_spacing + _height(child);
2235 static void group_layout_horiz(struct IClass *cl, Object *obj,
2236 struct MinList *children)
2238 struct MUI_GroupData *data = INST_DATA(cl, obj);
2239 Object *cstate;
2240 Object *child;
2241 ULONG total_weight;
2242 WORD remainder; /* must converge to 0 to succesfully end layout */
2243 WORD total_size;
2244 WORD total_size_backup;
2245 WORD total_init_size; /* total size of the ideally sized children */
2246 WORD height;
2247 WORD top = 0;
2248 WORD left = 0;
2249 WORD layout_width;
2250 WORD layout_height;
2252 //kprintf("group_layout_horiz: virtoff = %d,%d\n",
2253 // data->virt_offx, data->virt_offy);
2255 if (data->flags & GROUP_VIRTUAL)
2257 data->virt_mwidth =
2258 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2259 _maxwidth(obj) - _subwidth(obj));
2260 data->virt_mheight =
2261 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2262 _maxheight(obj) - _subheight(obj));
2264 layout_width = data->virt_mwidth;
2265 layout_height = data->virt_mheight;
2267 //kprintf("group_layout_horiz: layoutsize %d x %d "
2268 // " virtsize %d x %d msize %d x %d\n",
2269 // layout_width, layout_height, data->virt_mwidth,
2270 // data->virt_mheight,
2271 // _mwidth(obj), _mheight(obj));
2273 else
2275 layout_width = _mwidth(obj);
2276 layout_height = _mheight(obj);
2279 total_weight = data->horiz_weight_sum;
2280 total_init_size = 0;
2281 total_size =
2282 layout_width - (data->num_visible_children -
2283 1) * data->horiz_spacing;
2284 total_size_backup = total_size;
2286 /* D(bug("\nhoriz layout for %p, A=%d W=%ld\n", */
2287 /* obj, total_size, total_weight)); */
2289 // weight constraints
2290 // calculate ideal size for each object, and total ideal size
2291 cstate = (Object *) children->mlh_Head;
2292 while ((child = NextObject(&cstate)))
2294 if (IS_HIDDEN(child))
2295 continue;
2297 Layout1D_weight_constraint(&total_size, &total_init_size,
2298 &total_weight, &_width(child), _hweight(child),
2299 _minwidth(child));
2300 /* D(bug("child %p : ideal=%d w=%ld\n", */
2301 /* child, _width(child), _hweight(child))); */
2302 } // while child, weight constraints
2304 total_size = total_size_backup;
2305 if (data->horiz_weight_sum > 0)
2306 remainder = total_size - total_init_size;
2307 else
2308 remainder = 0;
2310 if (data->flags & GROUP_VIRTUAL)
2312 /* This is also true for non virtual groups, but if this would be the
2313 ** case then there is a bug in the layout function
2315 if (total_size < 0)
2316 total_size = 0;
2319 Layout1D_minmax_constraints_and_redistrib(children,
2320 total_size,
2321 remainder,
2322 (data->flags & GROUP_SAME_WIDTH) ? data->samesize_maxmin_horiz : 0,
2323 TRUE);
2325 // do the layout
2326 cstate = (Object *) children->mlh_Head;
2327 while ((child = NextObject(&cstate)))
2329 if (IS_HIDDEN(child))
2330 continue;
2332 height = MIN(_maxheight(child), layout_height);
2333 height = MAX(height, _minheight(child));
2334 top = (layout_height - height) / 2;
2336 /* D(bug("child %p -> layout %d x %d\n", */
2337 /* child, _width(child), height)); */
2338 if (!MUI_Layout(child, left, top, _width(child), height, 0))
2339 return;
2340 left += data->horiz_spacing + _width(child);
2346 static void Layout2D_weight_constraint(struct MUI_GroupData *data,
2347 struct layout2d_elem *row_infos,
2348 struct layout2d_elem *col_infos,
2349 WORD total_size_height, WORD total_size_width,
2350 WORD *total_init_height, WORD *total_init_width)
2352 int i;
2353 ULONG total_weight_vert = data->vert_weight_sum;
2354 ULONG total_weight_horiz = data->horiz_weight_sum;
2356 *total_init_height = 0;
2357 *total_init_width = 0;
2359 /* calc row heights */
2360 for (i = 0; i < data->rows; i++)
2362 if (total_weight_vert > 0)
2363 row_infos[i].dim =
2364 (total_size_height * row_infos[i].weight +
2365 total_weight_vert / 2) / total_weight_vert;
2366 else
2367 row_infos[i].dim = row_infos[i].min;
2369 /* D(bug("l2 row %d : ideal = %d with w=%d, A=%d, W=%d\n", */
2370 /* i, row_infos[i].dim, */
2371 /* row_infos[i].weight, total_size_height, total_weight_vert)); */
2373 total_weight_vert -= row_infos[i].weight;
2374 total_size_height -= row_infos[i].dim;
2375 *total_init_height += row_infos[i].dim;
2378 /* calc columns widths */
2379 for (i = 0; i < data->columns; i++)
2381 if (total_weight_horiz)
2382 col_infos[i].dim =
2383 (total_size_width * col_infos[i].weight +
2384 total_weight_horiz / 2) / total_weight_horiz;
2385 else
2386 col_infos[i].dim = col_infos[i].min;
2388 /* D(bug("l2 col %d : ideal = %d with w=%d, A=%d, W=%d\n", */
2389 /* i, col_infos[i].dim, */
2390 /* col_infos[i].weight, total_size_width, total_weight_horiz)); */
2392 total_weight_horiz -= col_infos[i].weight;
2393 total_size_width -= col_infos[i].dim;
2394 *total_init_width += col_infos[i].dim;
2400 static void Layout2D_minmax_constraints_and_redistrib(struct layout2d_elem
2401 *infos, WORD nitems, WORD total_size, WORD samesize, WORD remainder)
2403 int j;
2405 /* D(bug("L2D mc&r n=%d A=%d ss=%d rem=%d\n", */
2406 /* nitems, total_size, samesize, remainder)); */
2408 for (j = 0; j < 2; j++)
2410 WORD size_growables = total_size;
2411 WORD size_shrinkables = total_size;
2412 ULONG weight_growables = 0;
2413 ULONG weight_shrinkables = 0;
2414 /* WORD old_size; */
2415 int i;
2417 // minmax constraints
2418 for (i = 0; i < nitems; i++)
2420 /* old_size = infos[i].dim; */
2422 /* D(bug("bef loop1 on %d : size=%d, rem=%d, A=%d, " */
2423 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2424 /* i, infos[i].dim, remainder, total_size, */
2425 /* size_growables, size_shrinkables, infos[i].weight, */
2426 /* infos[i].min, infos[i].max)); */
2428 Layout1D_minmax_constraint(&infos[i].dim, infos[i].min,
2429 infos[i].max, &remainder, &size_growables,
2430 &size_shrinkables, &weight_growables, &weight_shrinkables,
2431 infos[i].weight, samesize);
2433 /* D(bug("loop1 on %d : size=%d was %d, rem=%d, A=%d, " */
2434 /* "sizegrow=%d, sizeshrink=%d w=%d min=%d max=%d\n", */
2435 /* i, infos[i].dim, old_size, remainder, total_size, */
2436 /* size_growables, size_shrinkables, infos[i].weight, */
2437 /* infos[i].min, infos[i].max)); */
2440 if (remainder == 0)
2441 break;
2443 for (i = 0; i < nitems; i++)
2445 /* old_size = infos[i].dim; */
2447 /* D(bug("bef loop2 on %d : size=%d, rem=%d, A=%d, " */
2448 /* "size_grow=%d, size_shrink=%d\n", i, */
2449 /* infos[i].dim, remainder, total_size, */
2450 /* size_growables, size_shrinkables)); */
2452 Layout1D_redistribution(&infos[i].dim, infos[i].min,
2453 infos[i].max, &remainder, &size_growables,
2454 &size_shrinkables, &weight_growables, &weight_shrinkables,
2455 infos[i].weight);
2457 /* D(bug("loop2 on %d : size=%d was %d, rem=%d, A=%d, " */
2458 /* "size_grow=%d, size_shrink=%d\n", i, */
2459 /* infos[i].dim, old_size, remainder, total_size, */
2460 /* size_growables, size_shrinkables)); */
2465 static void
2466 layout_2d_distribute_space(struct MUI_GroupData *data,
2467 struct layout2d_elem *row_infos,
2468 struct layout2d_elem *col_infos,
2469 struct MinList *children, LONG left_start, LONG top_start)
2471 Object *cstate;
2472 Object *child;
2473 LONG left;
2474 LONG top;
2475 LONG col_width;
2476 LONG row_height;
2477 int i, j;
2480 * pass 2 : distribute space
2482 cstate = (Object *) children->mlh_Head;
2483 top = top_start;
2484 /* for each row */
2485 for (i = 0; i < data->rows; i++)
2487 /* left start for child layout in this row */
2488 left = left_start;
2490 /* max height for children in this row */
2491 row_height = row_infos[i].dim;
2492 j = 0;
2493 /* for each column */
2494 while ((child = NextObject(&cstate)))
2496 LONG cleft;
2497 LONG ctop;
2498 LONG cwidth;
2499 LONG cheight;
2501 if (IS_HIDDEN(child))
2502 continue;
2503 /* max width for children in this column */
2504 col_width = col_infos[j].dim;
2506 /* center child if col width is bigger than child maxwidth */
2507 cwidth = MIN(_maxwidth(child), col_width);
2508 cwidth = MAX(cwidth, _minwidth(child));
2509 cleft = left + (col_width - cwidth) / 2;
2511 /* center child if row height is bigger than child maxheight */
2512 cheight = MIN(_maxheight(child), row_height);
2513 cheight = MAX(cheight, _minheight(child));
2514 ctop = top + (row_height - cheight) / 2;
2516 /* g_print("layout %d %d %d %d\n", cleft, ctop, cwidth, cheight); */
2517 /* D(bug("2DL/child %p -> layout %d x %d\n", */
2518 /* child, cwidth, cheight)); */
2519 if (!MUI_Layout(child, cleft, ctop, cwidth, cheight, 0))
2520 return;
2522 left += data->horiz_spacing + col_width;
2524 ++j;
2525 if ((j % data->columns) == 0)
2526 break;
2529 top += data->vert_spacing + row_height;
2536 * all children in the same row have the same maximum height
2537 * all children in the same column have the same maximum height
2538 * if a child maximum size is smaller than the biggest minimum size,
2539 * the chid will be centered in the remaining space.
2541 * for each row, determine its height allocation
2542 * weight ? the vertical weight of a row, if no fixed-height child
2543 * in the row, is the sum of all vertical weights of children
2544 * all row members will have the same height
2546 * for each column, determine its width allocation
2547 * all column members will have the same width
2549 /* Write a proper hook function */
2550 static void
2551 group_layout_2d(struct IClass *cl, Object *obj, struct MinList *children)
2553 struct MUI_GroupData *data = INST_DATA(cl, obj);
2554 WORD left_start = 0;
2555 WORD top_start = 0;
2556 WORD total_size_height =
2557 _mheight(obj) - (data->rows - 1) * data->vert_spacing;
2558 WORD total_size_width =
2559 _mwidth(obj) - (data->columns - 1) * data->horiz_spacing;
2560 WORD total_init_height;
2561 WORD total_init_width;
2562 WORD remainder_height;
2563 WORD remainder_width;
2564 WORD layout_width;
2565 WORD layout_height;
2567 if (data->rows == 0 || data->columns == 0)
2568 return;
2569 if (data->num_children % data->rows
2570 || data->num_children % data->columns)
2571 return;
2572 if (data->row_infos == NULL || data->col_infos == NULL)
2573 return;
2575 //kprintf("group_layout_horiz: virtoff = %d,%d\n",
2576 // data->virt_offx, data->virt_offy);
2578 if (data->flags & GROUP_VIRTUAL)
2580 data->virt_mwidth =
2581 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2582 _maxwidth(obj) - _subwidth(obj));
2583 data->virt_mheight =
2584 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2585 _maxheight(obj) - _subheight(obj));
2587 layout_width = data->virt_mwidth;
2588 layout_height = data->virt_mheight;
2590 else
2592 layout_width = _mwidth(obj);
2593 layout_height = _mheight(obj);
2596 total_size_height =
2597 layout_height - (data->rows - 1) * data->vert_spacing;
2598 total_size_width =
2599 layout_width - (data->columns - 1) * data->horiz_spacing;
2601 // fix left/top ?
2603 // weight constraints
2604 Layout2D_weight_constraint(data, data->row_infos, data->col_infos,
2605 total_size_height, total_size_width,
2606 &total_init_height, &total_init_width);
2608 remainder_height = total_size_height - total_init_height;
2609 remainder_width = total_size_width - total_init_width;
2611 Layout2D_minmax_constraints_and_redistrib(data->row_infos,
2612 data->rows, total_size_height,
2613 /* (data->flags & GROUP_SAME_HEIGHT) ? data->samesize_maxmin_vert : 0, */
2614 0, remainder_height);
2616 Layout2D_minmax_constraints_and_redistrib(data->col_infos,
2617 data->columns, total_size_width,
2618 /* (data->flags & GROUP_SAME_WIDTH) ? data->samesize_maxmin_horiz : 0, */
2619 0, remainder_width);
2621 layout_2d_distribute_space(data, data->row_infos, data->col_infos,
2622 children, left_start, top_start);
2626 /* Write a proper hook function */
2627 static void group_layout_pagemode(struct IClass *cl, Object *obj,
2628 struct MinList *children)
2630 struct MUI_GroupData *data = INST_DATA(cl, obj);
2631 Object *cstate;
2632 Object *child;
2633 WORD layout_width;
2634 WORD layout_height;
2635 int w, h, yoffset = 0;
2637 if (data->flags & GROUP_VIRTUAL)
2639 data->virt_mwidth =
2640 CLAMP(_mwidth(obj), data->saved_minwidth - _subwidth(obj),
2641 _maxwidth(obj) - _subwidth(obj));
2642 data->virt_mheight =
2643 CLAMP(_mheight(obj), data->saved_minheight - _subheight(obj),
2644 _maxheight(obj) - _subheight(obj));
2646 layout_width = data->virt_mwidth;
2647 layout_height = data->virt_mheight;
2649 else
2651 layout_width = _mwidth(obj);
2652 layout_height = _mheight(obj);
2655 if (data->titlegroup)
2657 yoffset = _minheight(data->titlegroup);
2658 layout_height -= yoffset;
2661 cstate = (Object *) children->mlh_Head;
2662 while ((child = NextObject(&cstate)))
2664 w = MIN(layout_width, _maxwidth(child));
2665 h = MIN(layout_height, _maxheight(child));
2667 if (child == data->titlegroup)
2669 MUI_Layout(child, (layout_width - w) / 2, 0, w, yoffset, 0);
2671 else
2673 D(bug("PM/child %p -> layout %d x %d\n", child, w, h));
2674 MUI_Layout(child, (layout_width - w) / 2,
2675 yoffset + (layout_height - h) / 2, w, h, 0);
2681 /**************************************************************************
2682 MUIM_Layout
2683 Either use a given layout hook, or the builtin method.
2684 **************************************************************************/
2685 IPTR Group__MUIM_Layout(struct IClass *cl, Object *obj,
2686 struct MUIP_Layout *msg)
2688 struct MUI_GroupData *data = INST_DATA(cl, obj);
2689 struct MUI_LayoutMsg lm = { 0 };
2691 get(data->family, MUIA_Family_List, &(lm.lm_Children));
2692 if (data->flags & GROUP_PAGEMODE)
2694 group_layout_pagemode(cl, obj, lm.lm_Children);
2696 else if (data->layout_hook)
2698 lm.lm_Type = MUILM_LAYOUT;
2699 lm.lm_Layout.Width = _mwidth(obj);
2700 lm.lm_Layout.Height = _mheight(obj);
2702 CallHookPkt(data->layout_hook, obj, &lm);
2704 if (data->flags & GROUP_VIRTUAL)
2706 data->virt_mwidth = lm.lm_Layout.Width;
2707 data->virt_mheight = lm.lm_Layout.Height;
2710 else
2712 if ((data->rows == 1) && (data->columns == 1))
2714 if (data->flags & GROUP_HORIZ)
2715 group_layout_horiz(cl, obj, lm.lm_Children);
2716 else
2717 group_layout_vert(cl, obj, lm.lm_Children);
2719 else
2720 group_layout_2d(cl, obj, lm.lm_Children);
2723 if (data->flags & GROUP_VIRTUAL)
2725 WORD new_virt_offx, new_virt_offy;
2727 new_virt_offx = data->virt_offx;
2728 new_virt_offy = data->virt_offy;
2730 if (new_virt_offx + _mwidth(obj) > data->virt_mwidth)
2732 new_virt_offx = data->virt_mwidth - _mwidth(obj);
2734 if (new_virt_offx < 0)
2735 new_virt_offx = 0;
2737 if (new_virt_offy + _mheight(obj) > data->virt_mheight)
2739 new_virt_offy = data->virt_mheight - _mheight(obj);
2741 if (new_virt_offy < 0)
2742 new_virt_offy = 0;
2744 if (new_virt_offx != data->virt_offx)
2746 nfset(obj, MUIA_Virtgroup_Left, new_virt_offx);
2749 if (new_virt_offy != data->virt_offy)
2751 nfset(obj, MUIA_Virtgroup_Top, new_virt_offy);
2756 return 0;
2759 /**************************************************************************
2760 MUIM_Show
2761 **************************************************************************/
2762 IPTR Group__MUIM_Show(struct IClass *cl, Object *obj,
2763 struct MUIP_Show *msg)
2765 struct MUI_GroupData *data = INST_DATA(cl, obj);
2766 Object *cstate;
2767 Object *child;
2768 struct MinList *ChildList = NULL;
2770 /* If msg is NULL, we won't want that the super method actually gets
2771 * this call */
2772 if (msg)
2773 DoSuperMethodA(cl, obj, (Msg) msg);
2775 get(data->family, MUIA_Family_List, &(ChildList));
2776 cstate = (Object *) ChildList->mlh_Head;
2778 if (data->flags & GROUP_PAGEMODE)
2780 int page = 0;
2781 while ((child = NextObject(&cstate)))
2783 if (child == data->titlegroup)
2785 DoShowMethod(child);
2786 continue; /* Title group is not counted as page */
2789 if (page == data->active_page)
2791 DoShowMethod(child);
2792 break;
2794 page++;
2797 else
2799 while ((child = NextObject(&cstate)))
2801 if (!(data->flags & GROUP_VIRTUAL) ||
2802 IsObjectVisible(child, MUIMasterBase))
2804 if (_flags(child) & MADF_SHOWME)
2805 DoShowMethod(child);
2809 return TRUE;
2812 /**************************************************************************
2813 MUIM_Hide
2814 **************************************************************************/
2815 IPTR Group__MUIM_Hide(struct IClass *cl, Object *obj,
2816 struct MUIP_Hide *msg)
2818 struct MUI_GroupData *data = INST_DATA(cl, obj);
2819 Object *cstate;
2820 Object *child;
2821 struct MinList *ChildList = NULL;
2823 get(data->family, MUIA_Family_List, &(ChildList));
2824 cstate = (Object *) ChildList->mlh_Head;
2826 if (data->flags & GROUP_PAGEMODE)
2828 int page = 0;
2829 while ((child = NextObject(&cstate)))
2831 if (child == data->titlegroup)
2833 DoHideMethod(child);
2834 continue; /* Title group is not counted as page */
2837 if (page == data->active_page)
2839 DoHideMethod(child);
2840 break;
2842 page++;
2845 else
2847 while ((child = NextObject(&cstate)))
2849 if (_flags(child) & MADF_CANDRAW)
2850 DoHideMethod(child);
2854 /* If msg is NULL, we won't want that the super method actually gets
2855 * this call */
2856 if (msg)
2857 return DoSuperMethodA(cl, obj, (Msg) msg);
2858 return 1;
2862 * MUIM_FindUData : tests if the MUIA_UserData of the object
2863 * contains the given <udata> and returns the object pointer in this case.
2865 IPTR Group__MUIM_FindUData(struct IClass *cl, Object *obj,
2866 struct MUIP_FindUData *msg)
2868 struct MUI_GroupData *data = INST_DATA(cl, obj);
2870 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2871 return (IPTR) obj;
2873 return DoMethodA(data->family, (Msg) msg);
2878 * MUIM_GetUData : This method tests if the MUIA_UserData of the object
2879 * contains the given <udata> and gets <attr> to <storage> for itself
2880 * in this case.
2882 IPTR Group__MUIM_GetUData(struct IClass *cl, Object *obj,
2883 struct MUIP_GetUData *msg)
2885 struct MUI_GroupData *data = INST_DATA(cl, obj);
2887 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2889 get(obj, msg->attr, msg->storage);
2890 return TRUE;
2893 return DoMethodA(data->family, (Msg) msg);
2898 * MUIM_SetUData : This method tests if the MUIA_UserData of the object
2899 * contains the given <udata> and sets <attr> to <val> for itself in this case.
2901 IPTR Group__MUIM_SetUData(struct IClass *cl, Object *obj,
2902 struct MUIP_SetUData *msg)
2904 struct MUI_GroupData *data = INST_DATA(cl, obj);
2906 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2907 set(obj, msg->attr, msg->val);
2909 DoMethodA(data->family, (Msg) msg);
2910 return TRUE;
2915 * MUIM_SetUDataOnce : This method tests if the MUIA_UserData of the object
2916 * contains the given <udata> and sets <attr> to <val> for itself in this case.
2917 * Stop after the first udata found.
2919 IPTR Group__MUIM_SetUDataOnce(struct IClass *cl, Object *obj,
2920 struct MUIP_SetUData *msg)
2922 struct MUI_GroupData *data = INST_DATA(cl, obj);
2924 if (muiNotifyData(obj)->mnd_UserData == msg->udata)
2926 set(obj, msg->attr, msg->val);
2927 return TRUE;
2929 return DoMethodA(data->family, (Msg) msg);
2932 /**************************************************************************
2933 MUIM_DragQueryExtented
2934 **************************************************************************/
2935 IPTR Group__MUIM_DragQueryExtended(struct IClass *cl, Object *obj,
2936 struct MUIP_DragQueryExtended *msg)
2938 struct MUI_GroupData *data = INST_DATA(cl, obj);
2939 Object *cstate;
2940 Object *child;
2941 Object *found_obj;
2942 struct MinList *ChildList = NULL;
2944 get(data->family, MUIA_Family_List, &(ChildList));
2945 cstate = (Object *) ChildList->mlh_Head;
2946 while ((child = NextObject(&cstate)))
2948 if (!(_flags(child) & MADF_CANDRAW))
2949 continue;
2951 if ((found_obj = (Object *) DoMethodA(child, (Msg) msg)))
2952 return (IPTR) found_obj;
2954 return DoSuperMethodA(cl, obj, (Msg) msg);
2957 /**************************************************************************
2958 MUIM_HandleEvent
2959 **************************************************************************/
2960 IPTR Group__MUIM_HandleEvent(struct IClass *cl, Object *obj,
2961 struct MUIP_HandleEvent *msg)
2963 struct MUI_GroupData *data = INST_DATA(cl, obj);
2965 /* check this, otherwise a superclass who has IDCMP_MOUSEBUTTONS
2966 eventhandler might call DoSuperMethod, and this function gets
2967 called even when he have not added any eventhandler */
2969 if ((data->flags & GROUP_VIRTUAL) && msg->imsg)
2971 switch (msg->imsg->Class)
2973 case IDCMP_MOUSEBUTTONS:
2974 /* For virtual groups */
2975 if (msg->imsg->Code == SELECTDOWN)
2977 if (_between(_mleft(obj), msg->imsg->MouseX, _mright(obj))
2978 && _between(_mtop(obj), msg->imsg->MouseY,
2979 _mbottom(obj)))
2981 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
2982 (IPTR) & data->ehn);
2983 data->ehn.ehn_Events |= IDCMP_INTUITICKS;
2984 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
2985 (IPTR) & data->ehn);
2988 else
2990 if (data->ehn.ehn_Events & IDCMP_INTUITICKS)
2992 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
2993 (IPTR) & data->ehn);
2994 data->ehn.ehn_Events &= ~IDCMP_INTUITICKS;
2995 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
2996 (IPTR) & data->ehn);
2999 break;
3001 case IDCMP_INTUITICKS:
3002 if (!(data->ehn.ehn_Events & IDCMP_INTUITICKS))
3003 break;
3005 if (!(_between(_mleft(obj), msg->imsg->MouseX, _mright(obj))
3006 && _between(_mtop(obj), msg->imsg->MouseY,
3007 _mbottom(obj))))
3009 LONG new_virt_offx = data->virt_offx;
3010 LONG new_virt_offy = data->virt_offy;
3012 if (msg->imsg->MouseX < _mleft(obj))
3014 /* scroll left */
3015 if (new_virt_offx >= 4)
3016 new_virt_offx -= 4;
3017 else
3018 new_virt_offx = 0;
3020 else if (msg->imsg->MouseX > _mright(obj))
3022 /* scroll right */
3023 new_virt_offx += 4;
3024 if (new_virt_offx > data->virt_mwidth - _mwidth(obj))
3025 new_virt_offx = data->virt_mwidth - _mwidth(obj);
3026 if (new_virt_offx < 0)
3027 new_virt_offx = 0;
3030 if (msg->imsg->MouseY < _mtop(obj))
3032 /* scroll top */
3033 if (new_virt_offy >= 4)
3034 new_virt_offy -= 4;
3035 else
3036 new_virt_offy = 0;
3038 else if (msg->imsg->MouseY > _mbottom(obj))
3040 /* scroll bottom */
3041 new_virt_offy += 4;
3042 if (new_virt_offy > data->virt_mheight - _mheight(obj))
3043 new_virt_offy = data->virt_mheight - _mheight(obj);
3044 if (new_virt_offy < 0)
3045 new_virt_offy = 0;
3048 if (new_virt_offx != data->virt_offx
3049 || new_virt_offy != data->virt_offy)
3051 SetAttrs(obj,
3052 MUIA_Virtgroup_Left, new_virt_offx,
3053 MUIA_Virtgroup_Top, new_virt_offy,
3054 MUIA_Group_Forward, FALSE, TAG_DONE);
3057 break;
3061 return 0;
3064 /**************************************************************************
3065 MUIM_DrawBackground
3066 **************************************************************************/
3067 IPTR Group__MUIM_DrawBackground(struct IClass *cl, Object *obj,
3068 struct MUIP_DrawBackground *msg)
3070 struct MUI_GroupData *data = INST_DATA(cl, obj);
3072 if (data->flags & GROUP_VIRTUAL)
3074 struct MUIP_DrawBackground msg2 = *msg;
3076 msg2.xoffset += data->virt_offx;
3077 msg2.yoffset += data->virt_offy;
3079 return DoSuperMethodA(cl, obj, (Msg) & msg2);
3082 return DoSuperMethodA(cl, obj, (Msg) msg);
3085 /**************************************************************************
3086 MUIM_FindAreaObject
3087 Find the given object or return NULL
3088 **************************************************************************/
3089 IPTR Group__MUIM_FindAreaObject(struct IClass *cl, Object *obj,
3090 struct MUIP_FindAreaObject *msg)
3092 struct MUI_GroupData *data = INST_DATA(cl, obj);
3093 Object *cstate;
3094 Object *child;
3095 struct MinList *ChildList = NULL;
3097 // it's me ?
3098 if (msg->obj == obj)
3099 return (IPTR) obj;
3101 // it's one of my children ?
3102 get(data->family, MUIA_Family_List, &(ChildList));
3103 cstate = (Object *) ChildList->mlh_Head;
3104 while ((child = NextObject(&cstate)))
3106 if (msg->obj == child)
3107 return (IPTR) child;
3110 // let the children find it
3111 get(data->family, MUIA_Family_List, &(ChildList));
3112 cstate = (Object *) ChildList->mlh_Head;
3113 while ((child = NextObject(&cstate)))
3115 Object *res = (Object *) DoMethodA(child, (Msg) msg);
3116 if (res != NULL)
3117 return (IPTR) res;
3120 return (IPTR) NULL;
3123 /**************************************************************************
3124 MUIM_Export : to export an objects "contents" to a dataspace object.
3125 **************************************************************************/
3126 static IPTR Group__MUIM_Export(struct IClass *cl, Object *obj,
3127 struct MUIP_Export *msg)
3129 struct MUI_GroupData *data = INST_DATA(cl, obj);
3130 Object *cstate;
3131 Object *child;
3132 struct MinList *ChildList = NULL;
3134 get(data->family, MUIA_Family_List, &(ChildList));
3135 if (!ChildList)
3136 return 0;
3138 cstate = (Object *) ChildList->mlh_Head;
3139 while ((child = NextObject(&cstate)))
3141 DoMethodA(child, (Msg) msg);
3144 return 0;
3148 /**************************************************************************
3149 MUIM_Import : to import an objects "contents" from a dataspace object.
3150 **************************************************************************/
3151 static IPTR Group__MUIM_Import(struct IClass *cl, Object *obj,
3152 struct MUIP_Import *msg)
3154 struct MUI_GroupData *data = INST_DATA(cl, obj);
3155 Object *cstate;
3156 Object *child;
3157 struct MinList *ChildList = NULL;
3159 get(data->family, MUIA_Family_List, &(ChildList));
3160 if (!ChildList)
3161 return 0;
3163 cstate = (Object *) ChildList->mlh_Head;
3164 while ((child = NextObject(&cstate)))
3166 DoMethodA(child, (Msg) msg);
3169 return 0;
3172 /**************************************************************************
3173 MUIM_Notify - disabled now because previous Zune versions had a OM_GET
3174 check in MUIM_Notify which is no longer the case
3175 **************************************************************************/
3176 #if 0
3177 STATIC IPTR Group_Notify(struct IClass *cl, Object *obj,
3178 struct MUIP_Notify *msg)
3180 struct MUI_GroupData *data = INST_DATA(cl, obj);
3181 Object *cstate;
3182 Object *child;
3183 struct MinList *ChildList;
3185 /* Try at first if understand the message our self
3186 ** We disable the forwarding of the OM_GET message
3187 ** as the MUIM_Notify otherwise would "think" that
3188 ** the group class actually understands the attribute
3189 ** although a child does this only
3191 data->dont_forward_get = 1;
3192 if (DoSuperMethodA(cl, obj, (Msg) msg))
3194 data->dont_forward_get = 0;
3195 return 1;
3198 /* We ourselves didn't understand the notify tag so we try the
3199 * children now */
3200 data->dont_forward_get = 0;
3202 get(data->family, MUIA_Family_List, (IPTR *) & (ChildList));
3203 cstate = (Object *) ChildList->mlh_Head;
3204 while ((child = NextObject(&cstate)))
3206 if (DoMethodA(child, (Msg) msg))
3207 return 1;
3209 return 0;
3211 #endif
3213 #if 1
3214 /* Notes about Group_Notify() and echo notification problem:
3215 It was discovered that MUI seems to have some special handling for group class
3216 which will drop notifications on the children which are found to not
3217 understand the attribute.
3219 This is done by checking if an OM_GET on the child returns TRUE.
3220 There's a little problem here because it is not known how big the storage
3221 needed for the attribute in question will be. Almost no class uses anything
3222 bigger than one IPTR. For "big" attributes those return a pointer to the data,
3223 not the data itself. Unfortuntely there are some exceptions like colorwheel
3224 class which does not return a pointer, but the data itself. So it's not
3225 enough to use one single IPTR variable (4 Bytes on 32bit machines, 8 bytes
3226 on 64 bit machines) to store the result of the test-OM_Get.
3228 There is no general way to query the size needed so if one wants to change
3229 Zune to work like MUI one needs to choose a size which one hopes will be
3230 big enough to hold all possible attributes of all classes, old, present
3231 and future ones.
3233 STATIC IPTR Group_Notify(struct IClass *cl, Object *obj,
3234 struct MUIP_Notify *msg)
3236 struct MUI_GroupData *data = INST_DATA(cl, obj);
3237 Object *cstate;
3238 Object *child;
3239 struct MinList *ChildList = NULL;
3240 IPTR attr[30];
3242 data->dont_forward_get = 1;
3244 if (GetAttr(msg->TrigAttr, obj, attr))
3246 data->dont_forward_get = 0;
3247 return DoSuperMethodA(cl, obj, (Msg) msg);
3249 data->dont_forward_get = 0;
3251 get(data->family, MUIA_Family_List, &(ChildList));
3252 if (!ChildList)
3253 return TRUE;
3255 cstate = (Object *) ChildList->mlh_Head;
3256 while ((child = NextObject(&cstate)))
3259 if (GetAttr(msg->TrigAttr, child, attr))
3261 DoMethodA(child, (Msg) msg);
3262 /* No return here! */
3265 return TRUE;
3267 #endif
3269 BOOPSI_DISPATCHER(IPTR, Group_Dispatcher, cl, obj, msg)
3271 switch (msg->MethodID)
3273 case OM_NEW:
3274 return Group__OM_NEW(cl, obj, (struct opSet *)msg);
3275 case OM_DISPOSE:
3276 return Group__OM_DISPOSE(cl, obj, msg);
3277 case OM_SET:
3278 return Group__OM_SET(cl, obj, (struct opSet *)msg);
3279 case OM_GET:
3280 return Group__OM_GET(cl, obj, (struct opGet *)msg);
3281 case OM_ADDMEMBER: /* Fall through */
3282 case MUIM_Group_AddTail:
3283 return Group__MUIM_AddTail(cl, obj, (APTR) msg);
3284 case MUIM_Group_AddHead:
3285 return Group__MUIM_AddHead(cl, obj, (APTR) msg);
3286 case MUIM_Group_Insert:
3287 return Group__MUIM_Insert(cl, obj, (APTR) msg);
3288 case OM_REMMEMBER: /* Fall through */
3289 case MUIM_Group_Remove:
3290 return Group__MUIM_Remove(cl, obj, (APTR) msg);
3291 case MUIM_AskMinMax:
3292 return Group__MUIM_AskMinMax(cl, obj, (APTR) msg);
3293 case MUIM_Group_ExitChange:
3294 return Group__MUIM_ExitChange(cl, obj, (APTR) msg);
3295 case MUIM_Group_InitChange:
3296 return Group__MUIM_InitChange(cl, obj, (APTR) msg);
3297 case MUIM_Group_Sort:
3298 return Group__MUIM_Sort(cl, obj, (APTR) msg);
3299 case MUIM_Group_DoMethodNoForward:
3300 return Group__MUIM_DoMethodNoForward(cl, obj, (APTR) msg);
3301 case MUIM_ConnectParent:
3302 return Group__MUIM_ConnectParent(cl, obj, (APTR) msg);
3303 case MUIM_DisconnectParent:
3304 return Group__MUIM_DisconnectParent(cl, obj, (APTR) msg);
3305 case MUIM_Layout:
3306 return Group__MUIM_Layout(cl, obj, (APTR) msg);
3307 case MUIM_Setup:
3308 return Group__MUIM_Setup(cl, obj, (APTR) msg);
3309 case MUIM_Cleanup:
3310 return Group__MUIM_Cleanup(cl, obj, (APTR) msg);
3311 case MUIM_Draw:
3312 return Group__MUIM_Draw(cl, obj, (APTR) msg);
3314 case MUIM_FindUData:
3315 return Group__MUIM_FindUData(cl, obj, (APTR) msg);
3316 case MUIM_GetUData:
3317 return Group__MUIM_GetUData(cl, obj, (APTR) msg);
3318 case MUIM_SetUData:
3319 return Group__MUIM_SetUData(cl, obj, (APTR) msg);
3320 case MUIM_SetUDataOnce:
3321 return Group__MUIM_SetUDataOnce(cl, obj, (APTR) msg);
3322 case MUIM_Show:
3323 return Group__MUIM_Show(cl, obj, (APTR) msg);
3324 case MUIM_Hide:
3325 return Group__MUIM_Hide(cl, obj, (APTR) msg);
3326 case MUIM_HandleEvent:
3327 return Group__MUIM_HandleEvent(cl, obj, (APTR) msg);
3328 case MUIM_DrawBackground:
3329 return Group__MUIM_DrawBackground(cl, obj, (APTR) msg);
3330 case MUIM_DragQueryExtended:
3331 return Group__MUIM_DragQueryExtended(cl, obj, (APTR) msg);
3332 case MUIM_FindAreaObject:
3333 return Group__MUIM_FindAreaObject(cl, obj, (APTR) msg);
3334 case MUIM_Export:
3335 return Group__MUIM_Export(cl, obj, (APTR) msg);
3336 case MUIM_Import:
3337 return Group__MUIM_Import(cl, obj, (APTR) msg);
3339 //#if 0
3340 #if 1
3341 /* Disabled. See above */
3342 case MUIM_Notify:
3343 return Group_Notify(cl, obj, (APTR) msg);
3344 #endif
3345 case MUIM_Set:
3346 case MUIM_MultiSet:
3347 case MUIM_CallHook:
3348 case MUIM_DrawParentBackground:
3349 case MUIM_DragBegin:
3350 case MUIM_DragDrop:
3351 case MUIM_DragQuery:
3352 case MUIM_DragFinish:
3353 case MUIM_DoDrag:
3354 case MUIM_CreateDragImage:
3355 case MUIM_DeleteDragImage:
3356 case MUIM_GoActive:
3357 case MUIM_GoInactive:
3358 case MUIM_CreateBubble:
3359 case MUIM_DeleteBubble:
3360 case MUIM_CreateShortHelp:
3361 case MUIM_DeleteShortHelp:
3362 case OM_ADDTAIL:
3363 case OM_REMOVE:
3364 return DoSuperMethodA(cl, obj, (APTR) msg);
3365 /* Needs not to be forwarded? */
3368 /* sometimes you want to call a superclass method,
3369 * but not dispatching to child.
3370 * But what to do with list methods in a listview ?
3372 Group_DispatchMsg(cl, obj, (APTR) msg);
3374 return DoSuperMethodA(cl, obj, msg);
3376 BOOPSI_DISPATCHER_END
3379 * Class descriptor.
3381 const struct __MUIBuiltinClass _MUI_Group_desc =
3383 MUIC_Group,
3384 MUIC_Area,
3385 sizeof(struct MUI_GroupData),
3386 (void *) Group_Dispatcher