Moved DejaVue fonts to contrib. They are only needed by applications
[AROS-Contrib.git] / bgui / groupclass.c
blobff62d5af965a5ce4b7a3bc32b2f976a9801dbe3e
1 /*
2 * @(#) $Header$
4 * BGUI library
5 * groupclass.c
7 * (C) Copyright 1998 Manuel Lemos.
8 * (C) Copyright 1996-1997 Ian J. Einman.
9 * (C) Copyright 1993-1996 Jaba Development.
10 * (C) Copyright 1993-1996 Jan van den Baard.
11 * All Rights Reserved.
13 * $Log$
14 * Revision 42.4 2004/06/16 20:16:48 verhaegs
15 * Use METHODPROTO, METHOD_END and REGFUNCPROTOn where needed.
17 * Revision 42.3 2000/08/10 17:52:47 stegerg
18 * temp fix for relayout refresh bug which only happens in AROS. temp. solved
19 * by doing a RefreshGList in windowclass.c/WindowClassRelease method.
21 * Revision 42.2 2000/05/15 19:27:01 stegerg
22 * another hundreds of REG() macro replacements in func headers/protos.
24 * Revision 42.1 2000/05/14 23:32:47 stegerg
25 * changed over 200 function headers which all use register
26 * parameters (oh boy ...), because the simple REG() macro
27 * doesn't work with AROS. And there are still hundreds
28 * of headers left to be fixed :(
30 * Many of these functions would also work with stack
31 * params, but since i have fixed every single one
32 * I encountered up to now, I guess will have to do
33 * the same for the rest.
35 * Revision 42.0 2000/05/09 22:09:07 mlemos
36 * Bumped to revision 42.0 before handing BGUI to AROS team
38 * Revision 41.11 2000/05/09 19:54:22 mlemos
39 * Merged with the branch Manuel_Lemos_fixes.
41 * Revision 41.10.2.11 1999/08/30 04:57:41 mlemos
42 * Made the methods that change group members on-fly setup the gadget
43 * attributes using the window WINDOW_SETUPGADGET method.
45 * Revision 41.10.2.10 1999/08/29 20:21:59 mlemos
46 * Renamed the GRM_RELAYOUT to BASE_RELAYOUT.
48 * Revision 41.10.2.9 1999/08/29 18:58:51 mlemos
49 * Added support to the LGO_Relayout attribute to be able to not relayout a
50 * when calling GRM_ADDMEMBER, GRM_INSERTMEMBER, GRM_REPLACEMEMBER.
52 * Revision 41.10.2.8 1998/10/14 04:07:55 mlemos
53 * Fixed the computation of group members minimum, maximum and nominam sizes.
55 * Revision 41.10.2.7 1998/07/05 19:19:26 mlemos
56 * Fixed bug of Group node instance data pointer be passed instead of the
57 * object pointer.
59 * Revision 41.10.2.6 1998/03/02 01:31:21 mlemos
60 * Fixed members list memory leak when group has no members.
61 * Added functions to obtain and release the members list.
63 * Revision 41.10.2.5 1998/03/02 00:22:18 mlemos
64 * Fixed extention determination when a group is empty.
66 * Revision 41.10.2.4 1998/03/01 17:36:48 mlemos
67 * Fixed memory leaks from group member list allocations.
68 * Prevented nest calls to retrieve copies of the group members list.
70 * Revision 41.10.2.3 1998/02/28 02:44:06 mlemos
71 * Made GRM_DIMENSIONS method obtain the group members list.
73 * Revision 41.10.2.2 1998/02/26 22:37:53 mlemos
74 * Commented out unused variables.
75 * Added casts to GadgetInfo structure where BaseInfo is passed.
76 * Fixed Group Node Class RM_REMOVE call.
78 * Revision 41.10.2.1 1998/02/26 17:45:40 mlemos
79 * Added support for getting a group node object
81 * Revision 41.10 1998/02/25 21:12:11 mlemos
82 * Bumping to 41.10
84 * Revision 1.1 1998/02/25 17:08:29 mlemos
85 * Ian sources
90 #define WW(x)
92 #include "include/classdefs.h"
94 /// Class definitions.
96 * Group member node.
98 typedef struct {
99 Object *md_Object; /* The object itself. */
100 struct IBox md_Bounds; /* Bounds relative to group. */
101 ULONG md_Weight; /* The object weight. */
102 WORD md_FixedWidth; /* Fixed width. */
103 WORD md_FixedHeight; /* Fixed height. */
104 UWORD md_AspectX, md_AspectY; /* Fixed Aspect ratio. */
105 UWORD md_MinWidth, md_MinHeight; /* Minimum width and height. */
106 UWORD md_MaxWidth, md_MaxHeight; /* Maximum width and height. */
107 UWORD md_NomWidth, md_NomHeight; /* Nominal width and height. */
108 UWORD md_HBLeft; /* Hit-box leftedge. */
109 UWORD md_ScaledSize; /* Scaled size. */
110 UWORD md_Flags; /* See below. */
111 UBYTE md_HAlign, md_VAlign; /* Alignment. */
112 Object *md_Group; /* The group we are in. */
113 } MD;
115 #define MDF_FIXEDWIDTH (1<<0) /* Fix width. */
116 #define MDF_FIXEDHEIGHT (1<<1) /* Fix height. */
117 #define MDF_SPACING (1<<2) /* This is a spacing object. */
118 #define MDF_NO_ALIGN (1<<3) /* Object must not be aligned. */
119 #define MDF_RESERVED (1<<4) /* Reserved bit. */
120 #define MDF_NO_NO_ALIGN (1<<5) /* Force alignment. */
121 #define MDF_HIDDEN (1<<6) /* Member hidden? */
124 /// SetGroupNodeAttrs
126 STATIC IPTR ASM SetGroupNodeAttrs(REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct opSet *ops)
128 MD *md = INST_DATA(cl, obj);
129 IPTR data;
130 struct TagItem *tstate = ops->ops_AttrList;
131 struct TagItem *tag;
132 BOOL relayout = FALSE;
135 * Scan LGO attributes.
137 while ((tag = NextTagItem(&tstate)))
139 data = tag->ti_Data;
140 switch (tag->ti_Tag)
142 case LGO_Left:
143 md->md_Bounds.Left = data;
144 break;
146 case LGO_Top:
147 md->md_Bounds.Top = data;
148 break;
150 case LGO_Width:
151 md->md_Bounds.Width = data;
152 break;
154 case LGO_Height:
155 md->md_Bounds.Height = data;
156 break;
158 case LGO_Object:
159 md->md_Object = (Object *)data;
160 break;
162 case LGO_Group:
163 md->md_Group = (Object *)data;
164 break;
166 case LGO_SpaceObject:
167 md->md_Object = BGUI_NewObjectA(BGUI_SPACING_OBJECT, NULL);
168 md->md_Weight = data;
169 md->md_Flags |= MDF_SPACING;
170 break;
172 case LGO_Weight:
173 if (data < 1) data = 1;
174 if (md->md_Weight != data)
176 md->md_Weight = data;
177 relayout = TRUE;
179 break;
181 case LGO_FixWidth:
182 md->md_FixedWidth = (WORD)data;
183 break;
185 case LGO_FixHeight:
186 md->md_FixedHeight = (WORD)data;
187 break;
189 case LGO_FixMinWidth:
190 md->md_FixedWidth = data ? 0 : -1;
191 break;
193 case LGO_FixMinHeight:
194 md->md_FixedHeight = data ? 0 : -1;
195 break;
197 case LGO_Align:
198 if (data)
199 md->md_Flags |= MDF_NO_NO_ALIGN;
200 break;
202 case LGO_NoAlign:
203 if (data)
204 md->md_Flags |= MDF_NO_ALIGN;
205 break;
207 case LGO_FixAspect:
208 if ((md->md_AspectX != ((data>>16) & 0xffff)) &&
209 (md->md_AspectY != ((data>> 0) & 0xffff)))
211 md->md_AspectX = (data>>16) & 0xffff;
212 md->md_AspectY = (data>> 0) & 0xffff;
213 relayout = TRUE;
215 break;
217 case LGO_Visible:
218 if (data)
220 if (md->md_Flags & MDF_HIDDEN)
222 md->md_Flags &= ~MDF_HIDDEN;
223 relayout = TRUE;
226 else
228 if (!(md->md_Flags & MDF_HIDDEN))
230 md->md_Flags |= MDF_HIDDEN;
231 relayout = TRUE;
234 break;
237 if (relayout && md->md_Group) RelayoutGroup(md->md_Group);
239 return 1;
243 /// OM_NEW
245 * Create a shiny new object.
247 METHOD(GroupNodeClassNew, struct opSet *, ops)
249 MD *md;
250 IPTR rc;
253 * First we let the superclass
254 * create the object.
256 if ((rc = AsmDoSuperMethodA(cl, obj, (Msg)ops)))
258 md = INST_DATA(cl, rc);
259 md->md_Weight = DEFAULT_WEIGHT;
260 md->md_FixedWidth = -1;
261 md->md_FixedHeight = -1;
264 * Setup user attributes.
266 SetGroupNodeAttrs(cl, (Object *)rc, ops);
268 if (md->md_Object && md->md_Group)
270 DoSetMethodNG(md->md_Object, BT_GroupMember, rc,
271 BT_ParentGroup, md->md_Group,
272 BT_ParentWindow, BASE_DATA(md->md_Group)->bc_Window,
273 TAG_DONE);
275 return rc;
278 AsmCoerceMethod(cl, (Object *)rc, OM_DISPOSE);
280 return 0;
282 METHOD_END
284 /// OM_SET
286 * Change one or more of the object
287 * it's attributes.
289 METHOD(GroupNodeClassSet, struct opSet *, ops)
291 AsmDoSuperMethodA(cl, obj, (Msg)ops);
293 return SetGroupNodeAttrs(cl, obj, ops);
295 METHOD_END
297 /// OM_GET
299 * Give one of the attributes.
301 METHOD(GroupNodeClassGet, struct opGet *, opg)
303 MD *md = INST_DATA(cl, obj);
304 ULONG rc = 1;
305 IPTR *store = opg->opg_Storage;
307 switch (opg->opg_AttrID)
309 case LGO_MinWidth:
310 STORE md->md_MinWidth;
311 break;
312 case LGO_MinHeight:
313 STORE md->md_MinHeight;
314 break;
315 case LGO_MaxWidth:
316 STORE md->md_MaxWidth;
317 break;
318 case LGO_MaxHeight:
319 STORE md->md_MaxHeight;
320 break;
321 case LGO_NomWidth:
322 STORE md->md_NomWidth;
323 break;
324 case LGO_NomHeight:
325 STORE md->md_NomHeight;
326 break;
327 case LGO_Object:
328 STORE md->md_Object;
329 break;
330 default:
331 rc = AsmDoSuperMethodA(cl, obj, (Msg)opg);
332 break;
334 return rc;
336 METHOD_END
338 /// RM_REMOVE
340 * Remove node.
342 METHOD(GroupNodeClassRemove, Msg, msg)
344 MD *md = INST_DATA(cl, obj);
346 if (md->md_Object)
347 DoSetMethodNG(md->md_Object, BT_ParentWindow, NULL, BT_ParentGroup, NULL, BT_GroupMember, NULL, TAG_DONE);
349 return AsmDoSuperMethodA(cl, obj, msg);
351 METHOD_END
354 /// Class initialization.
356 * Function table.
358 STATIC DPFUNC ClassFuncNode[] = {
359 { OM_NEW, GroupNodeClassNew },
360 { OM_SET, GroupNodeClassSet },
361 { OM_GET, GroupNodeClassGet },
362 { RM_REMOVE, GroupNodeClassRemove },
363 { DF_END },
366 static Class *GMClass = NULL;
367 #define MEMBER_DATA(m) ((MD *)INST_DATA(GMClass, ((Object *)m)))
370 * Simple class initialization.
372 makeproto Class *InitGroupNodeClass(void)
374 return GMClass = BGUI_MakeClass(CLASS_SuperClassBGUI, BGUI_ROOT_OBJECT,
375 CLASS_ObjectSize, sizeof(MD),
376 CLASS_DFTable, ClassFuncNode,
377 TAG_DONE);
382 /// Class definitions.
384 typedef struct {
385 UWORD td_Min;
386 UWORD td_Nom;
387 UWORD td_Max;
388 UWORD td_Size;
389 UWORD td_Weight;
390 UWORD td_PreAlign;
391 UWORD td_PostAlign;
392 } TD;
395 * Object instance data.
397 typedef struct {
398 struct SignalSemaphore gd_Lock; /* Semaphore for safety purposes. */
399 Object **gd_MD; /* Array of object pointers. */
400 struct MinList gd_Members; /* Group members. */
401 UWORD gd_NumMembers; /* Number of members. */
402 UWORD gd_Rows; /* Rows in HGroup. */
403 UWORD gd_Columns; /* Columns in VGroup. */
404 UBYTE gd_HAlign; /* Horizontal alignment. */
405 UBYTE gd_VAlign; /* Vertical alignment. */
406 UWORD gd_SpaceX; /* Inner-object spacing. */
407 UWORD gd_SpaceY; /* Inner-object spacing. */
408 TD *gd_RD, *gd_CD; /* Row and column descriptors. */
409 Object *gd_Active; /* Active member. */
410 UBYTE gd_Flags; /* See below. */
411 UBYTE gd_Style; /* Style. */
412 } GD;
414 #define GDF_ADD_FAILURE (1<<0) /* Error flag. */
415 #define GDF_HAS_TITLE (1<<1) /* Group is framed with a title. */
416 #define GDF_IS_MASTER (1<<2) /* This is the master group. */
417 #define GDF_EQUAL_MINWIDTH (1<<3) /* All objects have the same min width. */
418 #define GDF_EQUAL_MINHEIGHT (1<<4) /* All objects have the same min height.*/
419 #define GDF_BUFFER (1<<5) /* Buffer rendering. */
422 /// NextGroupNode
424 STATIC ASM MD *NextGroupNode(REG(a0) GD *gd, REG(a1) Object **node, REG(d0) ULONG flags)
426 Object *next;
427 MD *md;
431 if (*node) next = (Object *)AsmDoMethod(*node, RM_NEXT);
432 else next = ListHeadObject((struct List *)&gd->gd_Members);
434 if (!(*node = next)) return NULL;
436 md = MEMBER_DATA(next);
438 } while (md->md_Flags & flags);
440 return md;
443 /// GroupNode
445 STATIC ASM Object *GroupNode(REG(a0) GD *gd, REG(d0) int i, REG(d1) int j)
447 switch (gd->gd_Style)
449 case GRSTYLE_HORIZONTAL:
450 j = (i * gd->gd_Columns) + j;
451 break;
452 case GRSTYLE_VERTICAL:
453 j = (j * gd->gd_Rows) + i;
454 break;
455 default:
456 return NULL;
459 if (j < gd->gd_NumMembers) return gd->gd_MD[j];
461 return NULL;
465 /// VSpace, HSpace
467 static UWORD VSpace(UWORD space)
469 switch (space)
471 case (UWORD)GRSPACE_NARROW:
472 return (UWORD)GetTagData(GROUP_DefVSpaceNarrow, 2, BGUI_GetDefaultTags(BGUI_GROUP_GADGET));
473 case (UWORD)GRSPACE_NORMAL:
474 return (UWORD)GetTagData(GROUP_DefVSpaceNormal, 4, BGUI_GetDefaultTags(BGUI_GROUP_GADGET));
475 case (UWORD)GRSPACE_WIDE:
476 return (UWORD)GetTagData(GROUP_DefVSpaceWide, 8, BGUI_GetDefaultTags(BGUI_GROUP_GADGET));
478 return space;
480 static UWORD HSpace(UWORD space)
482 switch (space)
484 case (UWORD)GRSPACE_NARROW:
485 return (UWORD)GetTagData(GROUP_DefHSpaceNarrow, 2, BGUI_GetDefaultTags(BGUI_GROUP_GADGET));
486 case (UWORD)GRSPACE_NORMAL:
487 return (UWORD)GetTagData(GROUP_DefHSpaceNormal, 4, BGUI_GetDefaultTags(BGUI_GROUP_GADGET));
488 case (UWORD)GRSPACE_WIDE:
489 return (UWORD)GetTagData(GROUP_DefHSpaceWide, 8, BGUI_GetDefaultTags(BGUI_GROUP_GADGET));
491 return space;
494 /// FindObNode
496 STATIC ASM Object *FindObNode(REG(a0) GD *gd, REG(a1) Object *find)
498 Object *m = NULL;
499 MD *md;
501 if (find)
504 * Try to locate the object.
506 while ((md = NextGroupNode(gd, &m, 0)))
509 * Is this the one?
511 if (md->md_Object == find) return m;
514 return NULL;
518 /// GROUPM_NEWMEMBER
520 METHOD(GroupClassNewMember, struct opSet *, ops)
522 return (IPTR)BGUI_NewObjectA(BGUI_GROUP_NODE, ops->ops_AttrList);
524 METHOD_END
526 /// NewGroupMember
528 * Allocate and initialize a member structure.
530 STATIC Object *NewGroupMember(Object *obj, Object *member, struct TagItem *attrs)
532 struct TagItem tags[3], *t = tags;
534 t->ti_Tag = LGO_Group; t->ti_Data = (IPTR)obj; t++;
535 t->ti_Tag = LGO_Object; t->ti_Data = (IPTR)member; t++;
536 t->ti_Tag = TAG_MORE; t->ti_Data = (IPTR)attrs;
538 return (Object *)AsmDoMethod(obj, GROUPM_NEWMEMBER, tags, NULL);
541 /// NewSpaceObject
543 * Add a spacing object.
545 STATIC Object *NewSpaceObject(Object *obj, ULONG weight)
547 struct TagItem tags[3], *t = tags;
549 t->ti_Tag = LGO_Group; t->ti_Data = (IPTR)obj; t++;
550 t->ti_Tag = LGO_SpaceObject; t->ti_Data = (IPTR)weight; t++;
551 t->ti_Tag = TAG_DONE;
553 return (Object *)AsmDoMethod(obj, GROUPM_NEWMEMBER, tags, NULL);
556 /// GroupSetAttrs
558 * Pass an attribute on to the members.
560 STATIC ASM void PassAttr(REG(a0) GD *gd, REG(a1) struct TagItem *tag, REG(a2) struct GadgetInfo *gi)
562 Object *m;
563 MD *md;
564 struct opSet ops;
565 struct TagItem T[2];
567 T[0].ti_Tag = tag->ti_Tag;
568 T[0].ti_Data = tag->ti_Data;
569 T[1].ti_Tag = TAG_DONE;
571 ops.MethodID = OM_SET;
572 ops.ops_AttrList = T;
573 ops.ops_GInfo = gi;
575 m = NULL;
576 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN | MDF_SPACING)))
577 AsmDoMethodA(md->md_Object, (Msg)&ops);
581 * Set one or more attributes.
583 IPTR ASM GroupSetAttrs(REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct opSet *ops)
585 GD *gd = INST_DATA(cl, obj);
586 BC *bc = BASE_DATA(obj);
587 struct TagItem *tstate = ops->ops_AttrList, *tag;
588 ULONG data, attr;
589 struct GadgetInfo *gi = ops->ops_GInfo;
592 * Make sure we stay with the correct activation flags.
594 GADGET(obj)->Activation |= GACT_RELVERIFY;
597 * Pass on some tags.
599 while ((tag = BGUI_NextTagItem(&tstate)))
601 data = tag->ti_Data;
602 switch (tag->ti_Tag)
604 case FRM_Title:
605 if (data) gd->gd_Flags |= GDF_HAS_TITLE;
606 else gd->gd_Flags &= ~GDF_HAS_TITLE;
607 break;
609 case FRM_Type:
610 if (data == FRTYPE_DEFAULT)
612 data = GetTagData(FRM_DefaultType, FRTYPE_NEXT, BGUI_GetDefaultTags(BGUI_GROUP_GADGET));
613 DoSetMethodNG(bc->bc_Frame, FRM_Type, data, TAG_DONE);
615 break;
617 case GA_TopBorder:
618 case GA_BottomBorder:
619 case GA_LeftBorder:
620 case GA_RightBorder:
621 case BT_TextAttr:
622 case BT_ParentWindow:
623 case BT_ParentView:
624 case GA_Disabled:
625 case FRM_ThinFrame:
626 PassAttr(gd, tag, gi);
627 break;
629 case GROUP_IsMaster:
631 * Master group?
633 if (data) gd->gd_Flags |= GDF_IS_MASTER;
634 else gd->gd_Flags &= ~GDF_IS_MASTER;
635 break;
637 case GROUP_Spacing:
638 gd->gd_SpaceX = gd->gd_SpaceY = data;
639 break;
641 case GROUP_HorizSpacing:
642 gd->gd_SpaceX = data;
643 break;
645 case GROUP_VertSpacing:
646 gd->gd_SpaceY = data;
647 break;
649 case GROUP_Rows:
650 gd->gd_Rows = data;
651 break;
653 case GROUP_Columns:
654 gd->gd_Columns = data;
655 break;
657 case GROUP_Style:
658 gd->gd_Style = data;
659 break;
661 case GROUP_HorizAlign:
662 gd->gd_HAlign = data;
663 break;
665 case GROUP_VertAlign:
666 gd->gd_VAlign = data;
667 break;
669 case GROUP_EqualWidth:
670 if (data) gd->gd_Flags |= GDF_EQUAL_MINWIDTH;
671 else gd->gd_Flags &= ~GDF_EQUAL_MINWIDTH;
672 break;
674 case GROUP_EqualHeight:
675 if (data) gd->gd_Flags |= GDF_EQUAL_MINHEIGHT;
676 else gd->gd_Flags &= ~GDF_EQUAL_MINHEIGHT;
677 break;
679 case GROUP_BackFill:
680 attr = FRM_BackFill;
681 goto frame_attr;
683 case GROUP_BackPen:
684 attr = FRM_BackPen;
685 goto frame_attr;
687 case GROUP_BackDriPen:
688 attr = FRM_BackDriPen;
689 goto frame_attr;
691 frame_attr:
692 DoSuperSetMethodNG(cl, obj, bc->bc_Frame ? TAG_IGNORE : FRM_Type, FRTYPE_NONE, attr, data, TAG_DONE);
693 break;
696 return 1;
700 /// OM_NEW
702 * Create a new object.
704 METHOD(GroupClassNew, struct opSet *, ops)
706 GD *gd;
707 struct TagItem *tags = ops->ops_AttrList, *tstate, *tag;
708 IPTR rc;
709 IPTR data;
710 BOOL invert = FALSE;
711 Object *m;
712 struct Node *n1, *n2;
713 int l = 0, r = 0, t = 0, b = 0;
716 * Let the superclass make an object.
718 if ((rc = NewSuperObject(cl, obj, tags)))
721 * Obtain instance data.
723 gd = INST_DATA(cl, rc);
726 * Initialize member list and other data.
728 NewList((struct List *)&gd->gd_Members);
729 InitSemaphore(&gd->gd_Lock);
730 gd->gd_MD=NULL;
731 gd->gd_NumMembers=0;
733 tstate = tags;
735 * Initialize the rest of the instance data.
737 while ((tag = BGUI_NextTagItem(&tstate)))
739 data = tag->ti_Data;
741 switch (tag->ti_Tag)
743 case GROUP_Offset:
744 l = r = t = b = data;
745 break;
747 case GROUP_HorizOffset:
748 l = r = data;
749 break;
751 case GROUP_VertOffset:
752 t = b = data;
753 break;
755 case GROUP_LeftOffset:
756 l = data;
757 break;
759 case GROUP_TopOffset:
760 t = data;
761 break;
763 case GROUP_RightOffset:
764 r = data;
765 break;
767 case GROUP_BottomOffset:
768 b = data;
769 break;
771 case GROUP_Inverted:
772 invert = data;
773 break;
775 case GROUP_SpaceObject:
776 if ((m = NewSpaceObject((Object *)rc, data)))
778 AsmDoMethod(m, RM_ADDTAIL, &gd->gd_Members);
779 break;
781 gd->gd_Flags |= GDF_ADD_FAILURE;
782 break;
784 case GROUP_Member:
785 if (data)
787 if ((m = NewGroupMember((Object *)rc, (Object *)data, tag + 1)))
789 AsmDoMethod(m, RM_ADDTAIL, &gd->gd_Members);
790 break;
793 * Dispose of the member.
795 AsmDoMethod((Object *)data, OM_DISPOSE);
798 * Set error flag.
800 gd->gd_Flags |= GDF_ADD_FAILURE;
801 break;
805 if (invert)
807 struct List *membList = (struct List *)&gd->gd_Members;
808 n1 = membList->lh_Head;
810 while ((n2 = n1->ln_Succ)->ln_Succ)
812 Remove(n2);
813 AddHead(membList, n2);
818 * All OK?
820 if (!(gd->gd_Flags & GDF_ADD_FAILURE))
822 if (GADGET(rc)->Activation & GACT_TOPBORDER) l++;
824 DoSuperSetMethodNG(cl, (Object *)rc, BT_LeftOffset, HSpace(l), BT_RightOffset, HSpace(r),
825 BT_TopOffset, VSpace(t), BT_BottomOffset, VSpace(b), TAG_DONE);
827 GroupSetAttrs(cl, (Object *)rc, ops);
829 else
832 * Noop... dump the object.
834 AsmCoerceMethod(cl, (Object *)rc, OM_DISPOSE);
835 rc = 0;
838 return rc;
840 METHOD_END
842 /// OM_SET
844 * Set one or more attributes.
846 METHOD(GroupClassSet, struct opSet *, ops)
849 * First we let the superclass have a go at it.
851 AsmDoSuperMethodA(cl, obj, (Msg)ops);
853 return GroupSetAttrs(cl, obj, ops);
855 METHOD_END
857 /// OM_GET
858 METHOD(GroupClassGet, struct opGet *, opg)
860 GD *gd = INST_DATA(cl, obj);
861 ULONG rc = 1;
862 IPTR *store = opg->opg_Storage;
864 switch (opg->opg_AttrID)
866 case GROUP_Rows:
867 STORE gd->gd_Rows;
868 break;
870 case GROUP_Columns:
871 STORE gd->gd_Columns;
872 break;
874 case GROUP_HorizSpacing:
875 STORE gd->gd_SpaceX;
876 break;
878 case GROUP_VertSpacing:
879 STORE gd->gd_SpaceY;
880 break;
882 case GROUP_HorizAlign:
883 STORE gd->gd_HAlign;
884 break;
886 case GROUP_VertAlign:
887 STORE gd->gd_VAlign;
888 break;
890 case GROUP_Members:
891 STORE gd->gd_MD;
892 break;
894 case GROUP_NumMembers:
895 STORE gd->gd_NumMembers;
896 break;
898 case GROUP_Style:
899 STORE gd->gd_Style;
900 break;
902 default:
903 rc = AsmDoSuperMethodA(cl, obj, (Msg)opg);
904 break;
906 return rc;
908 METHOD_END
910 static Object **ObtainMembers(Object *obj,GD *gd,BOOL *got_members_list)
912 if(gd->gd_MD==NULL)
914 if (!(gd->gd_MD = (Object **)AsmDoMethod(obj, GROUPM_OBTAINMEMBERS, GROMF_WAIT|GROMF_VISIBLE, &gd->gd_NumMembers)))
915 return 0;
916 *got_members_list=TRUE;
918 else
919 *got_members_list=FALSE;
920 return(gd->gd_MD);
923 static VOID ReleaseMembers(Object *obj,GD *gd,BOOL got_members_list)
925 if(got_members_list)
927 AsmDoMethod(obj, GROUPM_RELEASEMEMBERS, gd->gd_MD);
928 gd->gd_MD=NULL;
929 gd->gd_NumMembers=0;
934 /// BASE_LAYOUT
936 * Layout the group.
938 METHOD(GroupClassLayout, struct bmLayout *, bml)
940 GD *gd = INST_DATA(cl, obj);
941 BC *bc = BASE_DATA(obj);
942 Object *m;
943 MD *md;
944 TD *td, *cd, *rd;
945 BOOL changed,got_members_list;
946 ULONG totalweight, totalsize, tw, th;
947 /* UBYTE align; */
948 int r, c, size, last;
949 int x, y, dx, dy, w, h;
952 * Layout the baseclass.
954 AsmDoSuperMethodA(cl, obj, (Msg)bml);
956 if (!(bml->bml_Flags & BLF_CUSTOM_GROUP))
958 w = bc->bc_InnerBox.Width - (HSpace(gd->gd_SpaceX) * (gd->gd_Columns - 1));
959 h = bc->bc_InnerBox.Height - (VSpace(gd->gd_SpaceY) * (gd->gd_Rows - 1));
961 if(ObtainMembers(obj,gd,&got_members_list)==NULL)
962 return(0);
963 if (!gd->gd_NumMembers)
965 ReleaseMembers(obj,gd,got_members_list);
966 return 1;
970 * Set starting values.
972 tw = 0;
973 for (c = 0, td = gd->gd_CD; c < gd->gd_Columns; c++, td++)
975 tw += (td->td_Size = td->td_Nom);
978 last = 0;
982 * First loop to get total weight.
984 totalweight = 0;
985 totalsize = w;
986 for (c = 0, td = gd->gd_CD; c < gd->gd_Columns; c++, td++)
988 if (td->td_Size < td->td_Max)
989 totalweight += td->td_Weight;
990 else
991 totalsize -= td->td_Size;
994 if ((totalweight == 0) || (totalsize <= 0)) break;
997 * Loop to get all scaled sizes.
999 tw = 0;
1000 for (c = 0, td = gd->gd_CD; c < gd->gd_Columns; c++, td++)
1002 if (td->td_Size < td->td_Max)
1004 size = ScaleWeight(totalsize, totalweight, td->td_Weight);
1005 td->td_Size = range(size, td->td_Min, td->td_Max);
1009 * Keep track of the total size.
1011 tw += td->td_Size;
1013 if (tw == last) break;
1014 last = tw;
1015 } while (tw < w);
1018 * Constantly adjust the size of objects which where scaled larger
1019 * than their minimum size when the total scaled size was larger than
1020 * the area the objects must fit in.
1022 while (tw > w)
1024 changed = FALSE;
1025 for (c = 0, td = gd->gd_CD; c < gd->gd_Columns; c++, td++)
1028 * Was the size bigger than the minimum size?
1030 if (td->td_Size > td->td_Min)
1032 changed = TRUE;
1033 td->td_Size--;
1034 tw--;
1038 * Are we done yet?
1040 if (tw <= w) break;
1042 if (!changed) break;
1046 * Set starting values.
1048 th = 0;
1049 for (r = 0, td = gd->gd_RD; r < gd->gd_Rows; r++, td++)
1051 th += (td->td_Size = td->td_Nom);
1054 last = 0;
1058 * First loop to get total weight.
1060 totalweight = 0;
1061 totalsize = h;
1063 for (r = 0, td = gd->gd_RD; r < gd->gd_Rows; r++, td++)
1065 if (td->td_Size < td->td_Max)
1066 totalweight += td->td_Weight;
1067 else
1068 totalsize -= td->td_Size;
1071 if ((totalweight == 0) || (totalsize <= 0)) break;
1074 * Loop to get all scaled sizes.
1076 th = 0;
1077 for (r = 0, td = gd->gd_RD; r < gd->gd_Rows; r++, td++)
1079 if (td->td_Size < td->td_Max)
1081 size = ScaleWeight(totalsize, totalweight, td->td_Weight);
1082 td->td_Size = range(size, td->td_Min, td->td_Max);
1086 * Keep track of the total size.
1088 th += td->td_Size;
1090 if (th == last) break;
1091 last = th;
1092 } while (th < h);
1095 * Constantly adjust the size of objects which where scaled larger
1096 * than their minimum size when the total scaled size was larger than
1097 * the area the objects must fit in.
1099 while (th > h)
1101 changed = FALSE;
1102 for (r = 0, td = gd->gd_RD; r < gd->gd_Rows; r++, td++)
1105 * Was the size bigger than the minimum size?
1107 if (td->td_Size > td->td_Min)
1109 changed = TRUE;
1110 td->td_Size--;
1111 th--;
1115 * Are we done yet?
1117 if (th <= h) break;
1119 if (!changed) break;
1123 * Scan through the members.
1125 x = bc->bc_InnerBox.Left;
1127 for (c = 0, cd = gd->gd_CD; c < gd->gd_Columns; c++, cd++)
1129 y = bc->bc_InnerBox.Top;
1131 for (r = 0, rd = gd->gd_RD; r < gd->gd_Rows; r++, rd++)
1133 if ((m = GroupNode(gd, r, c)))
1135 md = MEMBER_DATA(m);
1137 md->md_Bounds.Width = range(cd->td_Size - md->md_HBLeft, md->md_MinWidth, md->md_MaxWidth);
1138 md->md_Bounds.Height = range(rd->td_Size, md->md_MinHeight, md->md_MaxHeight);
1140 switch (md->md_HAlign ? md->md_HAlign : gd->gd_HAlign)
1142 default:
1143 case GRALIGN_LEFT:
1144 dx = md->md_HBLeft;
1145 break;
1146 case GRALIGN_CENTER:
1147 dx = max(0, cd->td_Size - md->md_Bounds.Width) >> 1;
1148 break;
1149 case GRALIGN_RIGHT:
1150 dx = max(0, cd->td_Size - md->md_Bounds.Width);
1151 break;
1154 switch (md->md_VAlign ? md->md_VAlign : gd->gd_VAlign)
1156 default:
1157 case GRALIGN_TOP:
1158 dy = 0;
1159 break;
1160 case GRALIGN_CENTER:
1161 dy = max(0, rd->td_Size - md->md_Bounds.Height) >> 1;
1162 break;
1163 case GRALIGN_BOTTOM:
1164 dy = max(0, rd->td_Size - md->md_Bounds.Height);
1165 break;
1168 md->md_Bounds.Left = x + dx;
1169 md->md_Bounds.Top = y + dy;
1171 y += rd->td_Size + VSpace(gd->gd_SpaceY);
1173 x += cd->td_Size + HSpace(gd->gd_SpaceX);
1175 ReleaseMembers(obj,gd,got_members_list);
1178 return 1;
1180 METHOD_END
1182 /// BASE_RENDER
1184 struct SubRender
1186 struct Message sr_Message;
1187 Object *sr_Object;
1188 struct GadgetInfo *sr_GInfo;
1189 struct RastPort *sr_RPort;
1192 VOID SAVEDS subrender_task(void)
1194 struct Process *proc = (struct Process *)FindTask(NULL);
1195 struct SubRender *sr;
1197 WaitPort(&proc->pr_MsgPort);
1198 sr = (struct SubRender *)GetMsg(&proc->pr_MsgPort);
1200 AsmDoMethod(sr->sr_Object, GM_RENDER, sr->sr_GInfo, sr->sr_RPort, GREDRAW_REDRAW);
1202 ReplyMsg((struct Message *)sr);
1206 * Render the group.
1208 METHOD(GroupClassRender, struct bmRender *, bmr)
1210 GD *gd = INST_DATA(cl, obj);
1211 BC *bc = BASE_DATA(obj);
1212 Object *m;
1213 MD *md;
1214 struct BaseInfo *bi = bmr->bmr_BInfo;
1215 struct RastPort *rp = bi->bi_RPort;
1216 struct Process *proc;
1217 struct MsgPort *msgport = NULL;
1218 struct SubRender *sr;
1219 /* ULONG rc = 0; */
1220 BOOL subrender = FALSE; // no multitasking yet
1221 int render_count = 0;
1223 if (gd->gd_Flags & GDF_IS_MASTER)
1225 if (!bc->bc_Frame)
1227 BSetDPenA(bi, BACKGROUNDPEN);
1228 BBoxFillA(bi, &bc->bc_OuterBox);
1232 WW(kprintf("***GroupClassRender*** obj = %x\n", obj));
1235 * Render the baseclass.
1237 AsmDoSuperMethodA(cl, obj, (Msg)bmr);
1240 * Top-border group hack...
1242 if (GADGET(obj)->Activation & GACT_TOPBORDER)
1244 BSetDPenA(bi, SHADOWPEN);
1245 Move(rp, bc->bc_OuterBox.Left, bc->bc_OuterBox.Top + 1);
1246 Draw(rp, bc->bc_OuterBox.Left, bc->bc_OuterBox.Top + bc->bc_OuterBox.Height - 1);
1249 if (subrender)
1251 msgport = CreateMsgPort();
1252 if (!msgport) subrender = FALSE;
1255 m = NULL;
1256 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN | MDF_SPACING)))
1259 * Setup object position/dimensions and render it.
1261 SetGadgetBounds(md->md_Object, &md->md_Bounds);
1263 proc = NULL;
1265 if (subrender)
1267 if ((sr = BGUI_AllocPoolMem(sizeof(struct SubRender))))
1269 proc = CreateNewProcTags(NP_Entry, subrender_task, NP_Name, "BGUI Render", TAG_DONE);
1271 if (!proc) BGUI_FreePoolMem(sr);
1275 if (proc)
1277 render_count++;
1279 sr->sr_Message.mn_ReplyPort = msgport;
1280 sr->sr_Message.mn_Length = sizeof(struct SubRender);
1281 sr->sr_Object = md->md_Object;
1282 sr->sr_GInfo = (struct GadgetInfo *)bi;
1283 sr->sr_RPort = rp;
1285 PutMsg(&proc->pr_MsgPort, &sr->sr_Message);
1287 else
1289 AsmDoMethod(md->md_Object, GM_RENDER, bi, rp, GREDRAW_REDRAW);
1292 if (subrender)
1295 * Wait for all subtasks to finish.
1297 while (render_count)
1299 WaitPort(msgport);
1300 while ((sr = (struct SubRender *)GetMsg(msgport)))
1302 BGUI_FreePoolMem(sr);
1303 render_count--;
1306 DeleteMsgPort(msgport);
1308 return 1;
1310 METHOD_END
1312 /// GM_HITTEST
1314 * Pass on the hittest message.
1316 METHOD(GroupClassHitTest, struct gpHitTest *, gph)
1318 GD *gd = INST_DATA(cl, obj);
1319 Object *m;
1320 MD *md;
1321 ULONG rc;
1324 * See if a member was "hit".
1326 m = NULL;
1327 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN | MDF_SPACING)))
1330 * This member hit?
1332 if ((rc = ForwardMsg(obj, md->md_Object, (Msg)gph)) == GMR_GADGETHIT)
1335 * Make it the active one.
1337 gd->gd_Active = md->md_Object;
1338 return rc;
1343 * Safety precautions.
1345 gd->gd_Active = NULL;
1347 return 0;
1349 METHOD_END
1351 /// GM_GOACTIVE, GM_HANDLEINPUT
1353 * Forward a GM_GOACTIVE or GM_HANDLEINPUT message.
1355 METHOD(GroupClassActiveInput, struct gpInput *, gpi)
1357 GD *gd = INST_DATA(cl, obj);
1358 ULONG rc = GMR_NOREUSE;
1361 * Do we have an active member?
1363 if (gd->gd_Active)
1366 * Tell'm its active.
1368 if (gpi->MethodID == GM_GOACTIVE)
1369 DoSetMethodNG(gd->gd_Active, BT_IsActive, TRUE, TAG_END);
1371 rc = ForwardMsg(obj, gd->gd_Active, (Msg)gpi);
1374 * Clone the ID.
1376 DoSuperSetMethodNG(cl, obj, GA_ID, GADGET(gd->gd_Active)->GadgetID, TAG_DONE);
1378 return rc;
1380 METHOD_END
1382 /// GM_GOINACTIVE
1384 * Forward the GM_GOINACTIVE message.
1386 METHOD(GroupClassInActive, Msg, msg)
1388 GD *gd = INST_DATA(cl, obj);
1389 ULONG rc = 0;
1392 * Inactivate the active member.
1394 if (gd->gd_Active)
1396 rc = AsmDoMethodA(gd->gd_Active, msg);
1398 * Tell 'm its in-active.
1400 DoSetMethodNG(gd->gd_Active, BT_IsActive, FALSE, TAG_END);
1401 gd->gd_Active = NULL;
1403 return rc;
1405 METHOD_END
1407 /// OM_DISPOSE
1409 * Dispose of the group and all
1410 * it's members.
1412 METHOD(GroupClassDispose, Msg, msg)
1414 GD *gd = INST_DATA(cl, obj);
1415 Object *m, *o;
1416 MD *md;
1419 * Remove and dispose of all members.
1421 while ((m = ListHeadObject((struct List *)&gd->gd_Members)))
1423 md = MEMBER_DATA(m);
1424 o = md->md_Object;
1427 * Free the node.
1429 AsmDoMethod(m, OM_DISPOSE);
1432 * Dispose of the member.
1434 AsmDoMethod(o, OM_DISPOSE);
1437 if (gd->gd_RD) BGUI_FreePoolMem(gd->gd_RD);
1438 if (gd->gd_CD) BGUI_FreePoolMem(gd->gd_CD);
1441 * Leave the rest up to the superclass.
1443 return AsmDoSuperMethodA(cl, obj, msg);
1445 METHOD_END
1447 /// GRM_ADDMEMBER
1449 * Add a member to the group.
1451 METHOD(GroupClassAddMember, struct grmAddMember *, grma)
1453 GD *gd = INST_DATA(cl, obj);
1454 Object *m;
1457 * Allocate and initialize node.
1459 if ((m = NewGroupMember(obj, grma->grma_Member, (struct TagItem *)&grma->grma_Attr)))
1462 * Add it to the list.
1464 AsmDoMethod(m, RM_ADDTAIL, &gd->gd_Members);
1466 WW(kprintf("** GroupClassAddMember\n"));
1467 WW(if(BASE_DATA(obj)->bc_Window) kprintf("** GroupClassAddMember: sending WM_SETUPGADGET to window\n"));
1469 if(BASE_DATA(obj)->bc_Window)
1470 AsmDoMethod(BASE_DATA(obj)->bc_Window, WM_SETUPGADGET, grma->grma_Member, NULL);
1473 * Try to re-layout the group.
1475 WW(kprintf("** GroupClassAddMember: calling relayoutgroup: LGO_Relayout = %d\n",GetTagData(LGO_Relayout, TRUE, (struct TagItem *)&grma->grma_Attr)));
1477 if(GetTagData(LGO_Relayout, TRUE, (struct TagItem *)&grma->grma_Attr)
1478 && !RelayoutGroup(obj))
1480 WW(kprintf("** GroupClassAddMember: relayoutgroup failed. returning FALSE\n"));
1481 DisposeObject(m);
1482 RelayoutGroup(obj);
1484 return FALSE;
1486 WW(kprintf("** GroupClassAddMember: everything done. returning TRUE\n"));
1488 return TRUE;
1490 return FALSE;
1492 METHOD_END
1494 /// GRM_INSERTMEMBER
1496 * Insert a member in the group.
1498 METHOD(GroupClassInsert, struct grmInsertMember *, grmi)
1500 GD *gd = INST_DATA(cl, obj);
1501 Object *m;
1504 * No NULL-objects.
1506 if (!grmi->grmi_Member)
1507 return FALSE;
1510 * Allocate and initialize node.
1512 if ((m = NewGroupMember(obj, grmi->grmi_Member, (struct TagItem *)&grmi->grmi_Attr)))
1514 AsmDoMethod(m, RM_INSERT, &gd->gd_Members, FindObNode(gd, grmi->grmi_Pred));
1516 if(BASE_DATA(obj)->bc_Window)
1517 AsmDoMethod(BASE_DATA(obj)->bc_Window, WM_SETUPGADGET, grmi->grmi_Member, NULL);
1520 * Try to re-layout the group.
1522 if(GetTagData(LGO_Relayout, TRUE, (struct TagItem *)&grmi->grmi_Attr)
1523 && !RelayoutGroup(obj))
1525 DisposeObject(m);
1526 RelayoutGroup(obj);
1528 return FALSE;
1530 return TRUE;
1532 return FALSE;
1534 METHOD_END
1536 /// GRM_REPLACEMEMBER
1538 * Replace a member in the group.
1540 METHOD(GroupClassReplace, struct grmReplaceMember *, grrm)
1542 GD *gd = INST_DATA(cl, obj);
1543 Object *m, *old;
1546 * No NULL-objects.
1548 if (!grrm->grrm_MemberA || !grrm->grrm_MemberB)
1549 return 0;
1552 * Allocate and initialize node.
1554 if ((m = NewGroupMember(obj, grrm->grrm_MemberB, (struct TagItem *)&grrm->grrm_Attr)))
1557 * Find member to replace.
1559 if ((old = FindObNode(gd, grrm->grrm_MemberA)))
1561 AsmDoMethod(m, RM_INSERT, &gd->gd_Members, old);
1562 AsmDoMethod(old, RM_REMOVE);
1564 if(BASE_DATA(obj)->bc_Window)
1565 AsmDoMethod(BASE_DATA(obj)->bc_Window, WM_SETUPGADGET, grrm->grrm_MemberB, NULL);
1568 * Try to re-layout the group.
1570 if(GetTagData(LGO_Relayout, TRUE, (struct TagItem *)&grrm->grrm_Attr)
1571 && !RelayoutGroup(obj))
1573 AsmDoMethod(old, RM_INSERT, &gd->gd_Members);
1574 DisposeObject(m);
1575 RelayoutGroup(obj);
1577 return 0;
1581 * Deallocate replaced node.
1583 DisposeObject(old);
1586 * Set return code.
1588 return (IPTR)grrm->grrm_MemberA;
1591 return 0;
1593 METHOD_END
1595 /// GRM_REMMEMBER
1597 * Remove an object from the list.
1599 METHOD(GroupClassRemMember, struct grmRemMember *, grmr)
1601 GD *gd = INST_DATA(cl, obj);
1602 Object *m;
1604 if ((m = FindObNode(gd, grmr->grmr_Member)))
1607 * Remove and dealocate it.
1609 DisposeObject(m);
1612 * Relayout the group.
1614 RelayoutGroup(obj);
1616 return (IPTR)grmr->grmr_Member;
1618 return (IPTR)NULL;
1620 METHOD_END
1622 /// GROUPM_OBTAINMEMBERS
1624 METHOD(GroupClassObtainMembers, struct gmObtainMembers *, gmom)
1626 GD *gd = INST_DATA(cl, obj);
1627 MD *md;
1628 Object **o, *m;
1629 int n;
1630 ULONG flags = gmom->gmom_Flags;
1632 if (flags & GROMF_WAIT)
1634 if (flags & GROMF_EXCLUSIVE)
1636 ObtainSemaphore(&gd->gd_Lock);
1638 else
1640 ObtainSemaphoreShared(&gd->gd_Lock);
1643 else
1645 if (flags & GROMF_EXCLUSIVE)
1647 n = AttemptSemaphore(&gd->gd_Lock);
1649 else
1651 n = AttemptSemaphoreShared(&gd->gd_Lock);
1653 if (!n) return 0;
1656 n = 0;
1657 m = NULL;
1658 while ((md = NextGroupNode(gd, &m, (flags & GROMF_VISIBLE) ? MDF_HIDDEN : 0)))
1659 n++;
1661 o = BGUI_AllocPoolMem(sizeof(Object *) * (n + 2));
1663 if (!o)
1665 ReleaseSemaphore(&gd->gd_Lock);
1666 return 0;
1669 *o++ = obj; // Marker for safety.
1671 m = NULL;
1672 while ((md = NextGroupNode(gd, &m, (flags & GROMF_VISIBLE) ? MDF_HIDDEN : 0)))
1674 if (flags & GROMF_OBJECTS)
1675 *o++ = md->md_Object;
1676 else
1677 *o++ = m;
1680 if (gmom->gmom_Number) *(gmom->gmom_Number) = n;
1681 return (IPTR)(o - n);
1683 METHOD_END
1685 /// GROUPM_RELEASEMEMBERS
1687 METHOD(GroupClassReleaseMembers, struct gmReleaseMembers *, gmrm)
1689 GD *gd = INST_DATA(cl, obj);
1690 Object **o = gmrm->gmrm_Array - 1;
1691 int rc = -1;
1693 if (*o == obj) // Check for safety marker.
1695 if (gd->gd_Lock.ss_Owner == FindTask(NULL))
1697 rc = gd->gd_Lock.ss_NestCount - 1;
1699 ReleaseSemaphore(&gd->gd_Lock);
1701 BGUI_FreePoolMem(o);
1703 #ifdef DEBUG_BGUI
1704 else
1705 D(bug("*** Attempt to free an invalid group members list\n"));
1706 #endif
1707 return (ULONG)rc; // rc < 0 = failure, rc = 0 is freed, rc > 0 still locked.
1709 METHOD_END
1711 /// RelayoutGroup
1713 makeproto ASM ULONG RelayoutGroup(REG(a0) Object *obj)
1715 BC *bc = BASE_DATA(obj);
1716 struct Window *w = NULL;
1717 struct bmRelayout bmr;
1718 ULONG rc = 1;
1720 WW(kprintf("** GroupClass_RelayoutGrop\n"));
1722 if (bc->bc_Window)
1724 WW(kprintf("** GroupClass_RelayoutGrop: has bc->bc_Window\n"));
1725 Get_Attr(bc->bc_Window, WINDOW_Window, (IPTR *)&w);
1727 if (w)
1729 bmr.MethodID = BASE_RELAYOUT;
1730 bmr.bmr_GInfo = NULL;
1731 bmr.bmr_RPort = w->RPort;
1732 WW(kprintf("** GroupClass_RelayoutGrop: has WINDOW_Window --> sending BASE_RELAYOUT to obj\n"));
1734 rc = BGUI_DoGadgetMethodA(obj, w, NULL, (Msg)&bmr);
1737 return rc;
1741 /// MemberDimensions
1743 * Calculate the minimum size of a group.
1745 STATIC ASM BOOL MemberDimensions(REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct bmDimensions *bmd)
1747 GD *gd = INST_DATA(cl, obj);
1748 Object *m;
1749 MD *md;
1750 TD *td;
1751 ULONG minw, minh, nomw, nomh, maxw, maxh;
1752 ULONG tmin, tmax, tnom;
1753 int r, c;
1754 BOOL got_members_list;
1756 if(ObtainMembers(obj,gd,&got_members_list)==NULL)
1757 return FALSE;
1758 if (gd->gd_NumMembers)
1760 if (gd->gd_Style == GRSTYLE_HORIZONTAL)
1762 gd->gd_Rows = range(gd->gd_Rows, 1, gd->gd_NumMembers);
1763 gd->gd_Columns = (gd->gd_NumMembers + (gd->gd_Rows - 1)) / gd->gd_Rows;
1766 if (gd->gd_Style == GRSTYLE_VERTICAL)
1768 gd->gd_Columns = range(gd->gd_Columns, 1, gd->gd_NumMembers);
1769 gd->gd_Rows = (gd->gd_NumMembers + (gd->gd_Columns - 1)) / gd->gd_Columns;
1772 if (gd->gd_RD) BGUI_FreePoolMem(gd->gd_RD);
1773 if (gd->gd_CD) BGUI_FreePoolMem(gd->gd_CD);
1775 gd->gd_RD = BGUI_AllocPoolMem(sizeof(TD) * gd->gd_Rows);
1776 gd->gd_CD = BGUI_AllocPoolMem(sizeof(TD) * gd->gd_Columns);
1778 if (!(gd->gd_RD && gd->gd_CD)) return FALSE;
1781 * Scan through the members.
1783 for (c = 0, td = gd->gd_CD; c < gd->gd_Columns; c++, td++)
1785 td->td_PreAlign = 0;
1787 for (r = 0; r < gd->gd_Rows; r++)
1789 if ((m = GroupNode(gd, r, c)))
1791 md = MEMBER_DATA(m);
1794 * Find out how much we must adjust for alignment.
1796 AsmDoMethod(md->md_Object, BASE_LEFTEXT, bmd->bmd_BInfo->bi_RPort, &md->md_HBLeft);
1798 if (!md->md_HBLeft)
1800 if (!(md->md_Flags & MDF_NO_NO_ALIGN))
1801 md->md_Flags |= MDF_NO_ALIGN;
1803 else
1805 if (md->md_HBLeft > td->td_PreAlign) td->td_PreAlign = md->md_HBLeft;
1810 tmin = 0;
1811 tnom = 0;
1812 tmax = 0;
1814 for (r = 0; r < gd->gd_Rows; r++)
1816 if ((m = GroupNode(gd, r, c)))
1818 md = MEMBER_DATA(m);
1820 if (!(md->md_Flags & MDF_NO_ALIGN))
1822 md->md_HBLeft = td->td_PreAlign - md->md_HBLeft;
1824 else
1826 md->md_HBLeft = 0;
1829 if (r == 0) td->td_Weight = md->md_Weight;
1832 * Adjust the minimum width with the maximum extention.
1834 tmin = max(tmin, md->md_MinWidth + md->md_HBLeft);
1835 tnom = max(tnom, md->md_NomWidth + md->md_HBLeft);
1836 tmax = max(tmax, md->md_MaxWidth);
1840 td->td_Min = tmin;
1841 td->td_Max = tmax;
1842 td->td_Nom = range(tnom, tmin, tmax);
1846 * Scan through the members.
1848 for (r = 0, td = gd->gd_RD; r < gd->gd_Rows; r++, td++)
1850 tmin = 0;
1851 tnom = 0;
1852 tmax = 0;
1854 for (c = 0; c < gd->gd_Columns; c++)
1856 if ((m = GroupNode(gd, r, c)))
1858 md = MEMBER_DATA(m);
1860 if (c == 0) td->td_Weight = md->md_Weight;
1862 tmin = max(tmin, md->md_MinHeight);
1863 tnom = max(tnom, md->md_NomHeight);
1864 tmax = max(tmax, md->md_MaxHeight);
1868 td->td_Min = tmin;
1869 td->td_Max = tmax;
1870 td->td_Nom = range(tnom, tmin, tmax);
1873 minw = nomw = HSpace(gd->gd_SpaceX) * (gd->gd_Columns - 1);
1874 minh = nomh = VSpace(gd->gd_SpaceY) * (gd->gd_Rows - 1);
1875 maxw = maxh = 0;
1877 for (r = 0, td = gd->gd_RD; r < gd->gd_Rows; r++, td++)
1879 minh += td->td_Min;
1880 nomh += td->td_Nom;
1881 maxh += td->td_Max;
1884 for (c = 0, td = gd->gd_CD; c < gd->gd_Columns; c++, td++)
1886 minw += td->td_Min;
1887 nomw += td->td_Nom;
1888 maxw += td->td_Max;
1891 else
1894 * No members.
1896 minw = minh = 0;
1897 nomw = nomh = 0;
1898 maxw = maxh = 0xFFFF;
1901 ReleaseMembers(obj,gd,got_members_list);
1903 minw = max(minw, 1);
1904 minh = max(minh, 1);
1906 maxw = range(maxw, minw, 0xFFFF);
1907 maxh = range(maxh, minh, 0xFFFF);
1909 nomw = range(nomw, minw, maxw);
1910 nomh = range(nomh, minh, maxh);
1912 bmd->bmd_Extent->be_Min.Width += minw;
1913 bmd->bmd_Extent->be_Min.Height += minh;
1914 bmd->bmd_Extent->be_Nom.Width += nomw;
1915 bmd->bmd_Extent->be_Nom.Height += nomh;
1916 bmd->bmd_Extent->be_Max.Width = maxw;
1917 bmd->bmd_Extent->be_Max.Height = maxh;
1919 return TRUE;
1922 /// BASE_DIMENSIONS
1924 * They want our dimensions info.
1926 METHOD(GroupClassDimensions, struct bmDimensions *, bmd)
1928 GD *gd = INST_DATA(cl, obj);
1929 /* BC *bc = BASE_DATA(obj); */
1930 Object *m;
1931 MD *md;
1932 struct grmDimensions dim;
1933 UWORD mw = 0, mh = 0, minw, minh, maxw, maxh, nomw, nomh;
1934 ULONG rc;
1936 dim.MethodID = GRM_DIMENSIONS;
1937 dim.grmd_GInfo = (struct GadgetInfo *)bmd->bmd_BInfo;
1938 dim.grmd_RPort = bmd->bmd_BInfo->bi_RPort;
1939 dim.grmd_Flags = bmd->bmd_Flags | GDIMF_MAXIMUM|GDIMF_NOMINAL;
1940 dim.grmd_MinSize.Width = &minw;
1941 dim.grmd_MinSize.Height = &minh;
1942 dim.grmd_MaxSize.Width = &maxw;
1943 dim.grmd_MaxSize.Height = &maxh;
1944 dim.grmd_NomSize.Width = &nomw;
1945 dim.grmd_NomSize.Height = &nomh;
1948 * Browse through the members.
1950 m = NULL;
1951 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN)))
1953 if (md->md_Flags & MDF_SPACING)
1955 md->md_MinWidth = 0;
1956 md->md_MinHeight = 0;
1957 md->md_NomWidth = 0;
1958 md->md_NomHeight = 0;
1959 md->md_MaxWidth = 0xFFFF;
1960 md->md_MaxHeight = 0xFFFF;
1962 else
1965 * Ask dimensions.
1967 AsmDoMethodA(md->md_Object, (Msg)&dim);
1970 * Make sure the fixed sizes are not smaller than allowed.
1972 if (md->md_FixedWidth >= 0)
1974 maxw = minw = nomw = range(md->md_FixedWidth, minw, maxw);
1976 if (md->md_FixedHeight >= 0)
1978 maxh = minh = nomh = range(md->md_FixedHeight, minh, maxh);
1980 if (minw > mw) mw = minw;
1981 if (minh > mh) mh = minh;
1983 md->md_MinWidth = minw;
1984 md->md_MinHeight = minh;
1985 md->md_MaxWidth = max(minw, maxw);
1986 md->md_MaxHeight = max(minh, maxh);
1987 md->md_NomWidth = range(nomw, minw, maxw);
1988 md->md_NomHeight = range(nomh, minh, maxh);
1992 m = NULL;
1993 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN)))
1996 * Set equal width/height.
1998 if (gd->gd_Flags & GDF_EQUAL_MINWIDTH) md->md_MinWidth = mw;
1999 if (gd->gd_Flags & GDF_EQUAL_MINHEIGHT) md->md_MinHeight = mh;
2002 if ((rc = AsmDoSuperMethodA(cl, obj, (Msg)bmd)))
2005 * Calculate constraints.
2007 if (!(bmd->bmd_Flags & BDF_CUSTOM_GROUP))
2008 rc = MemberDimensions(cl, obj, bmd);
2010 return rc;
2012 METHOD_END
2014 /// GRM_DIMENSIONS
2015 METHOD(GroupClassDimensionsX, struct grmDimensions *, dim)
2017 GD *gd = INST_DATA(cl, obj);
2018 ULONG rc=0;
2019 BOOL got_members_list;
2021 if(ObtainMembers(obj,gd,&got_members_list)==NULL)
2022 return(0);
2023 rc=AsmDoSuperMethodA(cl, obj, (Msg)dim);
2024 ReleaseMembers(obj,gd,got_members_list);
2025 return(rc);
2027 METHOD_END
2030 /// BASE_SHOWHELP
2032 * Pass on the help message.
2034 METHOD(GroupClassHelp, struct bmShowHelp *, bsh)
2036 GD *gd = INST_DATA(cl, obj);
2037 Object *m;
2038 MD *md;
2039 ULONG rc = BMHELP_NOT_ME;
2041 m = NULL;
2042 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN | MDF_SPACING)) && (rc == BMHELP_NOT_ME))
2045 * Is this the one?
2047 rc = AsmDoMethodA(md->md_Object, (Msg)bsh);
2049 return rc;
2051 METHOD_END
2053 /// GRM_ADDSPACEMEMBER
2055 * Add a spacing object.
2057 METHOD(GroupClassAddSpaceMember, struct grmAddSpaceMember *, grms)
2059 GD *gd = INST_DATA(cl, obj);
2060 Object *m;
2062 if ((m = NewSpaceObject(obj, grms->grms_Weight)))
2064 AsmDoMethod(m, RM_ADDTAIL, &gd->gd_Members);
2065 return TRUE;
2067 return FALSE;
2069 METHOD_END
2071 /// BASE_LEFTEXT
2073 * Route a BASE_LEFTEXT method.
2075 METHOD(GroupClassLeftExt, struct bmLeftExt *, le)
2077 GD *gd = INST_DATA(cl, obj);
2078 BC *bc = BASE_DATA(obj);
2079 IPTR rc;
2082 * Do we have a label or frame? If so we
2083 * take the left-extention of this object.
2085 if (bc->bc_Label)
2087 return AsmDoSuperMethodA(cl, obj, (Msg)le);
2090 if (bc->bc_Frame)
2092 Get_Attr(bc->bc_Frame, FRM_Type, &rc);
2093 if (rc != FRTYPE_NONE)
2095 return AsmDoSuperMethodA(cl, obj, (Msg)le);
2100 * Get the biggest in the first column.
2102 if(gd->gd_CD)
2103 *(le->bmle_Extention) = gd->gd_CD[0].td_PreAlign;
2104 else
2106 *(le->bmle_Extention)=0;
2109 return 1;
2111 METHOD_END
2113 /// GRM_WHICHOBJECT
2115 * Which object is under the mouse?
2117 METHOD(GroupClassWhichObject, struct grmWhichObject *, grwo)
2119 GD *gd = INST_DATA(cl, obj);
2120 struct IBox *ibox;
2121 Object *m;
2122 MD *md;
2123 WORD x, y;
2124 ULONG rc;
2127 * Pick up the coords.
2129 x = grwo->grwo_Coords.X;
2130 y = grwo->grwo_Coords.Y;
2133 * See if the coords are over a member.
2135 m = NULL;
2136 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN | MDF_SPACING)))
2139 * If this is a group/page we pass on the method.
2141 if (IsMulti(md->md_Object))
2143 if ((rc = AsmDoMethodA(md->md_Object, (Msg)grwo)))
2144 return rc;
2148 * Get hitbox bounds.
2150 ibox = GADGETBOX(md->md_Object);
2153 * Over this object?
2155 if (x >= ibox->Left && x < (ibox->Left + ibox->Width ) &&
2156 y >= ibox->Top && y < (ibox->Top + ibox->Height))
2158 * Yes.
2160 return (IPTR)md->md_Object;
2163 return (IPTR)NULL;
2165 METHOD_END
2167 /// BASE_FINDKEY
2169 * Which object has the proper key?
2171 METHOD(GroupClassFindKey, struct bmFindKey *, bmfk)
2173 GD *gd = INST_DATA(cl, obj);
2174 Object *m;
2175 MD *md;
2176 ULONG rc;
2178 if ((rc = AsmDoSuperMethodA(cl, obj, (Msg)bmfk)))
2179 return rc;
2181 m = NULL;
2182 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN | MDF_SPACING)))
2184 if ((rc = AsmDoMethodA(md->md_Object, (Msg)bmfk)))
2185 return rc;
2187 return 0;
2189 METHOD_END
2191 /// #?
2193 * Attach keys from labels.
2195 METHOD(GroupClassAll, Msg, msg)
2197 GD *gd = INST_DATA(cl, obj);
2198 Object *m;
2199 MD *md;
2200 ULONG rc;
2202 rc = AsmDoSuperMethodA(cl, obj, msg);
2204 m = NULL;
2205 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN)))
2207 rc += AsmDoMethodA(md->md_Object, msg);
2209 return rc;
2211 METHOD_END
2213 /// BASE_IS_MULTI
2214 METHOD(GroupClassIsMulti, Msg, msg)
2216 return TRUE;
2218 METHOD_END
2220 /// Class initialization.
2222 * Class function table.
2224 STATIC DPFUNC ClassFunc[] = {
2225 { BASE_RENDER, GroupClassRender },
2226 { BASE_LAYOUT, GroupClassLayout },
2227 { BASE_DIMENSIONS, GroupClassDimensions },
2228 { GRM_DIMENSIONS, GroupClassDimensionsX },
2230 { OM_NEW, GroupClassNew },
2231 { OM_SET, GroupClassSet },
2232 { OM_GET, GroupClassGet },
2233 { OM_DISPOSE, GroupClassDispose },
2234 { GM_GOACTIVE, GroupClassActiveInput },
2235 { GM_HANDLEINPUT, GroupClassActiveInput },
2236 { GM_GOINACTIVE, GroupClassInActive },
2237 { GM_HITTEST, GroupClassHitTest },
2239 { GROUPM_OBTAINMEMBERS, GroupClassObtainMembers },
2240 { GROUPM_RELEASEMEMBERS, GroupClassReleaseMembers },
2241 { GROUPM_NEWMEMBER, GroupClassNewMember },
2242 { GRM_ADDMEMBER, GroupClassAddMember },
2243 { GRM_REMMEMBER, GroupClassRemMember },
2244 { GRM_ADDSPACEMEMBER, GroupClassAddSpaceMember },
2245 { GRM_INSERTMEMBER, GroupClassInsert },
2246 { GRM_REPLACEMEMBER, GroupClassReplace },
2247 { GRM_WHICHOBJECT, GroupClassWhichObject },
2248 { BASE_MOVEBOUNDS, GroupClassAll },
2249 { BASE_FINDKEY, GroupClassFindKey },
2250 { BASE_KEYLABEL, GroupClassAll },
2251 { BASE_LOCALIZE, GroupClassAll },
2252 { BASE_SHOWHELP, GroupClassHelp },
2253 { BASE_LEFTEXT, GroupClassLeftExt },
2254 { BASE_INHIBIT, GroupClassAll },
2255 { BASE_IS_MULTI, GroupClassIsMulti },
2257 { DF_END },
2261 * Simple class initialization.
2263 makeproto Class *InitGroupClass(void)
2265 return BGUI_MakeClass(CLASS_SuperClassBGUI, BGUI_BASE_GADGET,
2266 CLASS_ObjectSize, sizeof(GD),
2267 CLASS_DFTable, ClassFunc,
2268 TAG_DONE);