Fix for a crash which happened when a document couldn't be opened.
[AROS-Contrib.git] / bgui / groupclass.c
blob12ce7b6421d92505ae52fc5455b7a0f59ec9c881
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 n1 = ((struct List *)&gd->gd_Members)->lh_Head;
809 while ((n2 = n1->ln_Succ)->ln_Succ)
811 Remove(n2);
812 AddHead((struct List *)&gd->gd_Members, n2);
817 * All OK?
819 if (!(gd->gd_Flags & GDF_ADD_FAILURE))
821 if (GADGET(rc)->Activation & GACT_TOPBORDER) l++;
823 DoSuperSetMethodNG(cl, (Object *)rc, BT_LeftOffset, HSpace(l), BT_RightOffset, HSpace(r),
824 BT_TopOffset, VSpace(t), BT_BottomOffset, VSpace(b), TAG_DONE);
826 GroupSetAttrs(cl, (Object *)rc, ops);
828 else
831 * Noop... dump the object.
833 AsmCoerceMethod(cl, (Object *)rc, OM_DISPOSE);
834 rc = 0;
837 return rc;
839 METHOD_END
841 /// OM_SET
843 * Set one or more attributes.
845 METHOD(GroupClassSet, struct opSet *, ops)
848 * First we let the superclass have a go at it.
850 AsmDoSuperMethodA(cl, obj, (Msg)ops);
852 return GroupSetAttrs(cl, obj, ops);
854 METHOD_END
856 /// OM_GET
857 METHOD(GroupClassGet, struct opGet *, opg)
859 GD *gd = INST_DATA(cl, obj);
860 ULONG rc = 1;
861 IPTR *store = opg->opg_Storage;
863 switch (opg->opg_AttrID)
865 case GROUP_Rows:
866 STORE gd->gd_Rows;
867 break;
869 case GROUP_Columns:
870 STORE gd->gd_Columns;
871 break;
873 case GROUP_HorizSpacing:
874 STORE gd->gd_SpaceX;
875 break;
877 case GROUP_VertSpacing:
878 STORE gd->gd_SpaceY;
879 break;
881 case GROUP_HorizAlign:
882 STORE gd->gd_HAlign;
883 break;
885 case GROUP_VertAlign:
886 STORE gd->gd_VAlign;
887 break;
889 case GROUP_Members:
890 STORE gd->gd_MD;
891 break;
893 case GROUP_NumMembers:
894 STORE gd->gd_NumMembers;
895 break;
897 case GROUP_Style:
898 STORE gd->gd_Style;
899 break;
901 default:
902 rc = AsmDoSuperMethodA(cl, obj, (Msg)opg);
903 break;
905 return rc;
907 METHOD_END
909 static Object **ObtainMembers(Object *obj,GD *gd,BOOL *got_members_list)
911 if(gd->gd_MD==NULL)
913 if (!(gd->gd_MD = (Object **)AsmDoMethod(obj, GROUPM_OBTAINMEMBERS, GROMF_WAIT|GROMF_VISIBLE, &gd->gd_NumMembers)))
914 return 0;
915 *got_members_list=TRUE;
917 else
918 *got_members_list=FALSE;
919 return(gd->gd_MD);
922 static VOID ReleaseMembers(Object *obj,GD *gd,BOOL got_members_list)
924 if(got_members_list)
926 AsmDoMethod(obj, GROUPM_RELEASEMEMBERS, gd->gd_MD);
927 gd->gd_MD=NULL;
928 gd->gd_NumMembers=0;
933 /// BASE_LAYOUT
935 * Layout the group.
937 METHOD(GroupClassLayout, struct bmLayout *, bml)
939 GD *gd = INST_DATA(cl, obj);
940 BC *bc = BASE_DATA(obj);
941 Object *m;
942 MD *md;
943 TD *td, *cd, *rd;
944 BOOL changed,got_members_list;
945 ULONG totalweight, totalsize, tw, th;
946 /* UBYTE align; */
947 int r, c, size, last;
948 int x, y, dx, dy, w, h;
951 * Layout the baseclass.
953 AsmDoSuperMethodA(cl, obj, (Msg)bml);
955 if (!(bml->bml_Flags & BLF_CUSTOM_GROUP))
957 w = bc->bc_InnerBox.Width - (HSpace(gd->gd_SpaceX) * (gd->gd_Columns - 1));
958 h = bc->bc_InnerBox.Height - (VSpace(gd->gd_SpaceY) * (gd->gd_Rows - 1));
960 if(ObtainMembers(obj,gd,&got_members_list)==NULL)
961 return(0);
962 if (!gd->gd_NumMembers)
964 ReleaseMembers(obj,gd,got_members_list);
965 return 1;
969 * Set starting values.
971 tw = 0;
972 for (c = 0, td = gd->gd_CD; c < gd->gd_Columns; c++, td++)
974 tw += (td->td_Size = td->td_Nom);
977 last = 0;
981 * First loop to get total weight.
983 totalweight = 0;
984 totalsize = w;
985 for (c = 0, td = gd->gd_CD; c < gd->gd_Columns; c++, td++)
987 if (td->td_Size < td->td_Max)
988 totalweight += td->td_Weight;
989 else
990 totalsize -= td->td_Size;
993 if ((totalweight == 0) || (totalsize <= 0)) break;
996 * Loop to get all scaled sizes.
998 tw = 0;
999 for (c = 0, td = gd->gd_CD; c < gd->gd_Columns; c++, td++)
1001 if (td->td_Size < td->td_Max)
1003 size = ScaleWeight(totalsize, totalweight, td->td_Weight);
1004 td->td_Size = range(size, td->td_Min, td->td_Max);
1008 * Keep track of the total size.
1010 tw += td->td_Size;
1012 if (tw == last) break;
1013 last = tw;
1014 } while (tw < w);
1017 * Constantly adjust the size of objects which where scaled larger
1018 * than their minimum size when the total scaled size was larger than
1019 * the area the objects must fit in.
1021 while (tw > w)
1023 changed = FALSE;
1024 for (c = 0, td = gd->gd_CD; c < gd->gd_Columns; c++, td++)
1027 * Was the size bigger than the minimum size?
1029 if (td->td_Size > td->td_Min)
1031 changed = TRUE;
1032 td->td_Size--;
1033 tw--;
1037 * Are we done yet?
1039 if (tw <= w) break;
1041 if (!changed) break;
1045 * Set starting values.
1047 th = 0;
1048 for (r = 0, td = gd->gd_RD; r < gd->gd_Rows; r++, td++)
1050 th += (td->td_Size = td->td_Nom);
1053 last = 0;
1057 * First loop to get total weight.
1059 totalweight = 0;
1060 totalsize = h;
1062 for (r = 0, td = gd->gd_RD; r < gd->gd_Rows; r++, td++)
1064 if (td->td_Size < td->td_Max)
1065 totalweight += td->td_Weight;
1066 else
1067 totalsize -= td->td_Size;
1070 if ((totalweight == 0) || (totalsize <= 0)) break;
1073 * Loop to get all scaled sizes.
1075 th = 0;
1076 for (r = 0, td = gd->gd_RD; r < gd->gd_Rows; r++, td++)
1078 if (td->td_Size < td->td_Max)
1080 size = ScaleWeight(totalsize, totalweight, td->td_Weight);
1081 td->td_Size = range(size, td->td_Min, td->td_Max);
1085 * Keep track of the total size.
1087 th += td->td_Size;
1089 if (th == last) break;
1090 last = th;
1091 } while (th < h);
1094 * Constantly adjust the size of objects which where scaled larger
1095 * than their minimum size when the total scaled size was larger than
1096 * the area the objects must fit in.
1098 while (th > h)
1100 changed = FALSE;
1101 for (r = 0, td = gd->gd_RD; r < gd->gd_Rows; r++, td++)
1104 * Was the size bigger than the minimum size?
1106 if (td->td_Size > td->td_Min)
1108 changed = TRUE;
1109 td->td_Size--;
1110 th--;
1114 * Are we done yet?
1116 if (th <= h) break;
1118 if (!changed) break;
1122 * Scan through the members.
1124 x = bc->bc_InnerBox.Left;
1126 for (c = 0, cd = gd->gd_CD; c < gd->gd_Columns; c++, cd++)
1128 y = bc->bc_InnerBox.Top;
1130 for (r = 0, rd = gd->gd_RD; r < gd->gd_Rows; r++, rd++)
1132 if ((m = GroupNode(gd, r, c)))
1134 md = MEMBER_DATA(m);
1136 md->md_Bounds.Width = range(cd->td_Size - md->md_HBLeft, md->md_MinWidth, md->md_MaxWidth);
1137 md->md_Bounds.Height = range(rd->td_Size, md->md_MinHeight, md->md_MaxHeight);
1139 switch (md->md_HAlign ? md->md_HAlign : gd->gd_HAlign)
1141 default:
1142 case GRALIGN_LEFT:
1143 dx = md->md_HBLeft;
1144 break;
1145 case GRALIGN_CENTER:
1146 dx = max(0, cd->td_Size - md->md_Bounds.Width) >> 1;
1147 break;
1148 case GRALIGN_RIGHT:
1149 dx = max(0, cd->td_Size - md->md_Bounds.Width);
1150 break;
1153 switch (md->md_VAlign ? md->md_VAlign : gd->gd_VAlign)
1155 default:
1156 case GRALIGN_TOP:
1157 dy = 0;
1158 break;
1159 case GRALIGN_CENTER:
1160 dy = max(0, rd->td_Size - md->md_Bounds.Height) >> 1;
1161 break;
1162 case GRALIGN_BOTTOM:
1163 dy = max(0, rd->td_Size - md->md_Bounds.Height);
1164 break;
1167 md->md_Bounds.Left = x + dx;
1168 md->md_Bounds.Top = y + dy;
1170 y += rd->td_Size + VSpace(gd->gd_SpaceY);
1172 x += cd->td_Size + HSpace(gd->gd_SpaceX);
1174 ReleaseMembers(obj,gd,got_members_list);
1177 return 1;
1179 METHOD_END
1181 /// BASE_RENDER
1183 struct SubRender
1185 struct Message sr_Message;
1186 Object *sr_Object;
1187 struct GadgetInfo *sr_GInfo;
1188 struct RastPort *sr_RPort;
1191 VOID SAVEDS subrender_task(void)
1193 struct Process *proc = (struct Process *)FindTask(NULL);
1194 struct SubRender *sr;
1196 WaitPort(&proc->pr_MsgPort);
1197 sr = (struct SubRender *)GetMsg(&proc->pr_MsgPort);
1199 AsmDoMethod(sr->sr_Object, GM_RENDER, sr->sr_GInfo, sr->sr_RPort, GREDRAW_REDRAW);
1201 ReplyMsg((struct Message *)sr);
1205 * Render the group.
1207 METHOD(GroupClassRender, struct bmRender *, bmr)
1209 GD *gd = INST_DATA(cl, obj);
1210 BC *bc = BASE_DATA(obj);
1211 Object *m;
1212 MD *md;
1213 struct BaseInfo *bi = bmr->bmr_BInfo;
1214 struct RastPort *rp = bi->bi_RPort;
1215 struct Process *proc;
1216 struct MsgPort *msgport = NULL;
1217 struct SubRender *sr;
1218 /* ULONG rc = 0; */
1219 BOOL subrender = FALSE; // no multitasking yet
1220 int render_count = 0;
1222 if (gd->gd_Flags & GDF_IS_MASTER)
1224 if (!bc->bc_Frame)
1226 BSetDPenA(bi, BACKGROUNDPEN);
1227 BBoxFillA(bi, &bc->bc_OuterBox);
1231 WW(kprintf("***GroupClassRender*** obj = %x\n", obj));
1234 * Render the baseclass.
1236 AsmDoSuperMethodA(cl, obj, (Msg)bmr);
1239 * Top-border group hack...
1241 if (GADGET(obj)->Activation & GACT_TOPBORDER)
1243 BSetDPenA(bi, SHADOWPEN);
1244 Move(rp, bc->bc_OuterBox.Left, bc->bc_OuterBox.Top + 1);
1245 Draw(rp, bc->bc_OuterBox.Left, bc->bc_OuterBox.Top + bc->bc_OuterBox.Height - 1);
1248 if (subrender)
1250 msgport = CreateMsgPort();
1251 if (!msgport) subrender = FALSE;
1254 m = NULL;
1255 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN | MDF_SPACING)))
1258 * Setup object position/dimensions and render it.
1260 SetGadgetBounds(md->md_Object, &md->md_Bounds);
1262 proc = NULL;
1264 if (subrender)
1266 if ((sr = BGUI_AllocPoolMem(sizeof(struct SubRender))))
1268 proc = CreateNewProcTags(NP_Entry, subrender_task, NP_Name, "BGUI Render", TAG_DONE);
1270 if (!proc) BGUI_FreePoolMem(sr);
1274 if (proc)
1276 render_count++;
1278 sr->sr_Message.mn_ReplyPort = msgport;
1279 sr->sr_Message.mn_Length = sizeof(struct SubRender);
1280 sr->sr_Object = md->md_Object;
1281 sr->sr_GInfo = (struct GadgetInfo *)bi;
1282 sr->sr_RPort = rp;
1284 PutMsg(&proc->pr_MsgPort, &sr->sr_Message);
1286 else
1288 AsmDoMethod(md->md_Object, GM_RENDER, bi, rp, GREDRAW_REDRAW);
1291 if (subrender)
1294 * Wait for all subtasks to finish.
1296 while (render_count)
1298 WaitPort(msgport);
1299 while ((sr = (struct SubRender *)GetMsg(msgport)))
1301 BGUI_FreePoolMem(sr);
1302 render_count--;
1305 DeleteMsgPort(msgport);
1307 return 1;
1309 METHOD_END
1311 /// GM_HITTEST
1313 * Pass on the hittest message.
1315 METHOD(GroupClassHitTest, struct gpHitTest *, gph)
1317 GD *gd = INST_DATA(cl, obj);
1318 Object *m;
1319 MD *md;
1320 ULONG rc;
1323 * See if a member was "hit".
1325 m = NULL;
1326 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN | MDF_SPACING)))
1329 * This member hit?
1331 if ((rc = ForwardMsg(obj, md->md_Object, (Msg)gph)) == GMR_GADGETHIT)
1334 * Make it the active one.
1336 gd->gd_Active = md->md_Object;
1337 return rc;
1342 * Safety precautions.
1344 gd->gd_Active = NULL;
1346 return 0;
1348 METHOD_END
1350 /// GM_GOACTIVE, GM_HANDLEINPUT
1352 * Forward a GM_GOACTIVE or GM_HANDLEINPUT message.
1354 METHOD(GroupClassActiveInput, struct gpInput *, gpi)
1356 GD *gd = INST_DATA(cl, obj);
1357 ULONG rc = GMR_NOREUSE;
1360 * Do we have an active member?
1362 if (gd->gd_Active)
1365 * Tell'm its active.
1367 if (gpi->MethodID == GM_GOACTIVE)
1368 DoSetMethodNG(gd->gd_Active, BT_IsActive, TRUE, TAG_END);
1370 rc = ForwardMsg(obj, gd->gd_Active, (Msg)gpi);
1373 * Clone the ID.
1375 DoSuperSetMethodNG(cl, obj, GA_ID, GADGET(gd->gd_Active)->GadgetID, TAG_DONE);
1377 return rc;
1379 METHOD_END
1381 /// GM_GOINACTIVE
1383 * Forward the GM_GOINACTIVE message.
1385 METHOD(GroupClassInActive, Msg, msg)
1387 GD *gd = INST_DATA(cl, obj);
1388 ULONG rc = 0;
1391 * Inactivate the active member.
1393 if (gd->gd_Active)
1395 rc = AsmDoMethodA(gd->gd_Active, msg);
1397 * Tell 'm its in-active.
1399 DoSetMethodNG(gd->gd_Active, BT_IsActive, FALSE, TAG_END);
1400 gd->gd_Active = NULL;
1402 return rc;
1404 METHOD_END
1406 /// OM_DISPOSE
1408 * Dispose of the group and all
1409 * it's members.
1411 METHOD(GroupClassDispose, Msg, msg)
1413 GD *gd = INST_DATA(cl, obj);
1414 Object *m, *o;
1415 MD *md;
1418 * Remove and dispose of all members.
1420 while ((m = ListHeadObject((struct List *)&gd->gd_Members)))
1422 md = MEMBER_DATA(m);
1423 o = md->md_Object;
1426 * Free the node.
1428 AsmDoMethod(m, OM_DISPOSE);
1431 * Dispose of the member.
1433 AsmDoMethod(o, OM_DISPOSE);
1436 if (gd->gd_RD) BGUI_FreePoolMem(gd->gd_RD);
1437 if (gd->gd_CD) BGUI_FreePoolMem(gd->gd_CD);
1440 * Leave the rest up to the superclass.
1442 return AsmDoSuperMethodA(cl, obj, msg);
1444 METHOD_END
1446 /// GRM_ADDMEMBER
1448 * Add a member to the group.
1450 METHOD(GroupClassAddMember, struct grmAddMember *, grma)
1452 GD *gd = INST_DATA(cl, obj);
1453 Object *m;
1456 * Allocate and initialize node.
1458 if ((m = NewGroupMember(obj, grma->grma_Member, (struct TagItem *)&grma->grma_Attr)))
1461 * Add it to the list.
1463 AsmDoMethod(m, RM_ADDTAIL, &gd->gd_Members);
1465 WW(kprintf("** GroupClassAddMember\n"));
1466 WW(if(BASE_DATA(obj)->bc_Window) kprintf("** GroupClassAddMember: sending WM_SETUPGADGET to window\n"));
1468 if(BASE_DATA(obj)->bc_Window)
1469 AsmDoMethod(BASE_DATA(obj)->bc_Window, WM_SETUPGADGET, grma->grma_Member, NULL);
1472 * Try to re-layout the group.
1474 WW(kprintf("** GroupClassAddMember: calling relayoutgroup: LGO_Relayout = %d\n",GetTagData(LGO_Relayout, TRUE, (struct TagItem *)&grma->grma_Attr)));
1476 if(GetTagData(LGO_Relayout, TRUE, (struct TagItem *)&grma->grma_Attr)
1477 && !RelayoutGroup(obj))
1479 WW(kprintf("** GroupClassAddMember: relayoutgroup failed. returning FALSE\n"));
1480 DisposeObject(m);
1481 RelayoutGroup(obj);
1483 return FALSE;
1485 WW(kprintf("** GroupClassAddMember: everything done. returning TRUE\n"));
1487 return TRUE;
1489 return FALSE;
1491 METHOD_END
1493 /// GRM_INSERTMEMBER
1495 * Insert a member in the group.
1497 METHOD(GroupClassInsert, struct grmInsertMember *, grmi)
1499 GD *gd = INST_DATA(cl, obj);
1500 Object *m;
1503 * No NULL-objects.
1505 if (!grmi->grmi_Member)
1506 return FALSE;
1509 * Allocate and initialize node.
1511 if ((m = NewGroupMember(obj, grmi->grmi_Member, (struct TagItem *)&grmi->grmi_Attr)))
1513 AsmDoMethod(m, RM_INSERT, &gd->gd_Members, FindObNode(gd, grmi->grmi_Pred));
1515 if(BASE_DATA(obj)->bc_Window)
1516 AsmDoMethod(BASE_DATA(obj)->bc_Window, WM_SETUPGADGET, grmi->grmi_Member, NULL);
1519 * Try to re-layout the group.
1521 if(GetTagData(LGO_Relayout, TRUE, (struct TagItem *)&grmi->grmi_Attr)
1522 && !RelayoutGroup(obj))
1524 DisposeObject(m);
1525 RelayoutGroup(obj);
1527 return FALSE;
1529 return TRUE;
1531 return FALSE;
1533 METHOD_END
1535 /// GRM_REPLACEMEMBER
1537 * Replace a member in the group.
1539 METHOD(GroupClassReplace, struct grmReplaceMember *, grrm)
1541 GD *gd = INST_DATA(cl, obj);
1542 Object *m, *old;
1545 * No NULL-objects.
1547 if (!grrm->grrm_MemberA || !grrm->grrm_MemberB)
1548 return 0;
1551 * Allocate and initialize node.
1553 if ((m = NewGroupMember(obj, grrm->grrm_MemberB, (struct TagItem *)&grrm->grrm_Attr)))
1556 * Find member to replace.
1558 if ((old = FindObNode(gd, grrm->grrm_MemberA)))
1560 AsmDoMethod(m, RM_INSERT, &gd->gd_Members, old);
1561 AsmDoMethod(old, RM_REMOVE);
1563 if(BASE_DATA(obj)->bc_Window)
1564 AsmDoMethod(BASE_DATA(obj)->bc_Window, WM_SETUPGADGET, grrm->grrm_MemberB, NULL);
1567 * Try to re-layout the group.
1569 if(GetTagData(LGO_Relayout, TRUE, (struct TagItem *)&grrm->grrm_Attr)
1570 && !RelayoutGroup(obj))
1572 AsmDoMethod(old, RM_INSERT, &gd->gd_Members);
1573 DisposeObject(m);
1574 RelayoutGroup(obj);
1576 return 0;
1580 * Deallocate replaced node.
1582 DisposeObject(old);
1585 * Set return code.
1587 return (IPTR)grrm->grrm_MemberA;
1590 return 0;
1592 METHOD_END
1594 /// GRM_REMMEMBER
1596 * Remove an object from the list.
1598 METHOD(GroupClassRemMember, struct grmRemMember *, grmr)
1600 GD *gd = INST_DATA(cl, obj);
1601 Object *m;
1603 if ((m = FindObNode(gd, grmr->grmr_Member)))
1606 * Remove and dealocate it.
1608 DisposeObject(m);
1611 * Relayout the group.
1613 RelayoutGroup(obj);
1615 return (IPTR)grmr->grmr_Member;
1617 return (IPTR)NULL;
1619 METHOD_END
1621 /// GROUPM_OBTAINMEMBERS
1623 METHOD(GroupClassObtainMembers, struct gmObtainMembers *, gmom)
1625 GD *gd = INST_DATA(cl, obj);
1626 MD *md;
1627 Object **o, *m;
1628 int n;
1629 ULONG flags = gmom->gmom_Flags;
1631 if (flags & GROMF_WAIT)
1633 if (flags & GROMF_EXCLUSIVE)
1635 ObtainSemaphore(&gd->gd_Lock);
1637 else
1639 ObtainSemaphoreShared(&gd->gd_Lock);
1642 else
1644 if (flags & GROMF_EXCLUSIVE)
1646 n = AttemptSemaphore(&gd->gd_Lock);
1648 else
1650 n = AttemptSemaphoreShared(&gd->gd_Lock);
1652 if (!n) return 0;
1655 n = 0;
1656 m = NULL;
1657 while ((md = NextGroupNode(gd, &m, (flags & GROMF_VISIBLE) ? MDF_HIDDEN : 0)))
1658 n++;
1660 o = BGUI_AllocPoolMem(sizeof(Object *) * (n + 2));
1662 if (!o)
1664 ReleaseSemaphore(&gd->gd_Lock);
1665 return 0;
1668 *o++ = obj; // Marker for safety.
1670 m = NULL;
1671 while ((md = NextGroupNode(gd, &m, (flags & GROMF_VISIBLE) ? MDF_HIDDEN : 0)))
1673 if (flags & GROMF_OBJECTS)
1674 *o++ = md->md_Object;
1675 else
1676 *o++ = m;
1679 if (gmom->gmom_Number) *(gmom->gmom_Number) = n;
1680 return (IPTR)(o - n);
1682 METHOD_END
1684 /// GROUPM_RELEASEMEMBERS
1686 METHOD(GroupClassReleaseMembers, struct gmReleaseMembers *, gmrm)
1688 GD *gd = INST_DATA(cl, obj);
1689 Object **o = gmrm->gmrm_Array - 1;
1690 int rc = -1;
1692 if (*o == obj) // Check for safety marker.
1694 if (gd->gd_Lock.ss_Owner == FindTask(NULL))
1696 rc = gd->gd_Lock.ss_NestCount - 1;
1698 ReleaseSemaphore(&gd->gd_Lock);
1700 BGUI_FreePoolMem(o);
1702 #ifdef DEBUG_BGUI
1703 else
1704 D(bug("*** Attempt to free an invalid group members list\n"));
1705 #endif
1706 return (ULONG)rc; // rc < 0 = failure, rc = 0 is freed, rc > 0 still locked.
1708 METHOD_END
1710 /// RelayoutGroup
1712 makeproto ASM ULONG RelayoutGroup(REG(a0) Object *obj)
1714 BC *bc = BASE_DATA(obj);
1715 struct Window *w = NULL;
1716 struct bmRelayout bmr;
1717 ULONG rc = 1;
1719 WW(kprintf("** GroupClass_RelayoutGrop\n"));
1721 if (bc->bc_Window)
1723 WW(kprintf("** GroupClass_RelayoutGrop: has bc->bc_Window\n"));
1724 Get_Attr(bc->bc_Window, WINDOW_Window, (IPTR *)&w);
1726 if (w)
1728 bmr.MethodID = BASE_RELAYOUT;
1729 bmr.bmr_GInfo = NULL;
1730 bmr.bmr_RPort = w->RPort;
1731 WW(kprintf("** GroupClass_RelayoutGrop: has WINDOW_Window --> sending BASE_RELAYOUT to obj\n"));
1733 rc = BGUI_DoGadgetMethodA(obj, w, NULL, (Msg)&bmr);
1736 return rc;
1740 /// MemberDimensions
1742 * Calculate the minimum size of a group.
1744 STATIC ASM BOOL MemberDimensions(REG(a0) Class *cl, REG(a2) Object *obj, REG(a1) struct bmDimensions *bmd)
1746 GD *gd = INST_DATA(cl, obj);
1747 Object *m;
1748 MD *md;
1749 TD *td;
1750 ULONG minw, minh, nomw, nomh, maxw, maxh;
1751 ULONG tmin, tmax, tnom;
1752 int r, c;
1753 BOOL got_members_list;
1755 if(ObtainMembers(obj,gd,&got_members_list)==NULL)
1756 return FALSE;
1757 if (gd->gd_NumMembers)
1759 if (gd->gd_Style == GRSTYLE_HORIZONTAL)
1761 gd->gd_Rows = range(gd->gd_Rows, 1, gd->gd_NumMembers);
1762 gd->gd_Columns = (gd->gd_NumMembers + (gd->gd_Rows - 1)) / gd->gd_Rows;
1765 if (gd->gd_Style == GRSTYLE_VERTICAL)
1767 gd->gd_Columns = range(gd->gd_Columns, 1, gd->gd_NumMembers);
1768 gd->gd_Rows = (gd->gd_NumMembers + (gd->gd_Columns - 1)) / gd->gd_Columns;
1771 if (gd->gd_RD) BGUI_FreePoolMem(gd->gd_RD);
1772 if (gd->gd_CD) BGUI_FreePoolMem(gd->gd_CD);
1774 gd->gd_RD = BGUI_AllocPoolMem(sizeof(TD) * gd->gd_Rows);
1775 gd->gd_CD = BGUI_AllocPoolMem(sizeof(TD) * gd->gd_Columns);
1777 if (!(gd->gd_RD && gd->gd_CD)) return FALSE;
1780 * Scan through the members.
1782 for (c = 0, td = gd->gd_CD; c < gd->gd_Columns; c++, td++)
1784 td->td_PreAlign = 0;
1786 for (r = 0; r < gd->gd_Rows; r++)
1788 if ((m = GroupNode(gd, r, c)))
1790 md = MEMBER_DATA(m);
1793 * Find out how much we must adjust for alignment.
1795 AsmDoMethod(md->md_Object, BASE_LEFTEXT, bmd->bmd_BInfo->bi_RPort, &md->md_HBLeft);
1797 if (!md->md_HBLeft)
1799 if (!(md->md_Flags & MDF_NO_NO_ALIGN))
1800 md->md_Flags |= MDF_NO_ALIGN;
1802 else
1804 if (md->md_HBLeft > td->td_PreAlign) td->td_PreAlign = md->md_HBLeft;
1809 tmin = 0;
1810 tnom = 0;
1811 tmax = 0;
1813 for (r = 0; r < gd->gd_Rows; r++)
1815 if ((m = GroupNode(gd, r, c)))
1817 md = MEMBER_DATA(m);
1819 if (!(md->md_Flags & MDF_NO_ALIGN))
1821 md->md_HBLeft = td->td_PreAlign - md->md_HBLeft;
1823 else
1825 md->md_HBLeft = 0;
1828 if (r == 0) td->td_Weight = md->md_Weight;
1831 * Adjust the minimum width with the maximum extention.
1833 tmin = max(tmin, md->md_MinWidth + md->md_HBLeft);
1834 tnom = max(tnom, md->md_NomWidth + md->md_HBLeft);
1835 tmax = max(tmax, md->md_MaxWidth);
1839 td->td_Min = tmin;
1840 td->td_Max = tmax;
1841 td->td_Nom = range(tnom, tmin, tmax);
1845 * Scan through the members.
1847 for (r = 0, td = gd->gd_RD; r < gd->gd_Rows; r++, td++)
1849 tmin = 0;
1850 tnom = 0;
1851 tmax = 0;
1853 for (c = 0; c < gd->gd_Columns; c++)
1855 if ((m = GroupNode(gd, r, c)))
1857 md = MEMBER_DATA(m);
1859 if (c == 0) td->td_Weight = md->md_Weight;
1861 tmin = max(tmin, md->md_MinHeight);
1862 tnom = max(tnom, md->md_NomHeight);
1863 tmax = max(tmax, md->md_MaxHeight);
1867 td->td_Min = tmin;
1868 td->td_Max = tmax;
1869 td->td_Nom = range(tnom, tmin, tmax);
1872 minw = nomw = HSpace(gd->gd_SpaceX) * (gd->gd_Columns - 1);
1873 minh = nomh = VSpace(gd->gd_SpaceY) * (gd->gd_Rows - 1);
1874 maxw = maxh = 0;
1876 for (r = 0, td = gd->gd_RD; r < gd->gd_Rows; r++, td++)
1878 minh += td->td_Min;
1879 nomh += td->td_Nom;
1880 maxh += td->td_Max;
1883 for (c = 0, td = gd->gd_CD; c < gd->gd_Columns; c++, td++)
1885 minw += td->td_Min;
1886 nomw += td->td_Nom;
1887 maxw += td->td_Max;
1890 else
1893 * No members.
1895 minw = minh = 0;
1896 nomw = nomh = 0;
1897 maxw = maxh = 0xFFFF;
1900 ReleaseMembers(obj,gd,got_members_list);
1902 minw = max(minw, 1);
1903 minh = max(minh, 1);
1905 maxw = range(maxw, minw, 0xFFFF);
1906 maxh = range(maxh, minh, 0xFFFF);
1908 nomw = range(nomw, minw, maxw);
1909 nomh = range(nomh, minh, maxh);
1911 bmd->bmd_Extent->be_Min.Width += minw;
1912 bmd->bmd_Extent->be_Min.Height += minh;
1913 bmd->bmd_Extent->be_Nom.Width += nomw;
1914 bmd->bmd_Extent->be_Nom.Height += nomh;
1915 bmd->bmd_Extent->be_Max.Width = maxw;
1916 bmd->bmd_Extent->be_Max.Height = maxh;
1918 return TRUE;
1921 /// BASE_DIMENSIONS
1923 * They want our dimensions info.
1925 METHOD(GroupClassDimensions, struct bmDimensions *, bmd)
1927 GD *gd = INST_DATA(cl, obj);
1928 /* BC *bc = BASE_DATA(obj); */
1929 Object *m;
1930 MD *md;
1931 struct grmDimensions dim;
1932 UWORD mw = 0, mh = 0, minw, minh, maxw, maxh, nomw, nomh;
1933 ULONG rc;
1935 dim.MethodID = GRM_DIMENSIONS;
1936 dim.grmd_GInfo = (struct GadgetInfo *)bmd->bmd_BInfo;
1937 dim.grmd_RPort = bmd->bmd_BInfo->bi_RPort;
1938 dim.grmd_Flags = bmd->bmd_Flags | GDIMF_MAXIMUM|GDIMF_NOMINAL;
1939 dim.grmd_MinSize.Width = &minw;
1940 dim.grmd_MinSize.Height = &minh;
1941 dim.grmd_MaxSize.Width = &maxw;
1942 dim.grmd_MaxSize.Height = &maxh;
1943 dim.grmd_NomSize.Width = &nomw;
1944 dim.grmd_NomSize.Height = &nomh;
1947 * Browse through the members.
1949 m = NULL;
1950 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN)))
1952 if (md->md_Flags & MDF_SPACING)
1954 md->md_MinWidth = 0;
1955 md->md_MinHeight = 0;
1956 md->md_NomWidth = 0;
1957 md->md_NomHeight = 0;
1958 md->md_MaxWidth = 0xFFFF;
1959 md->md_MaxHeight = 0xFFFF;
1961 else
1964 * Ask dimensions.
1966 AsmDoMethodA(md->md_Object, (Msg)&dim);
1969 * Make sure the fixed sizes are not smaller than allowed.
1971 if (md->md_FixedWidth >= 0)
1973 maxw = minw = nomw = range(md->md_FixedWidth, minw, maxw);
1975 if (md->md_FixedHeight >= 0)
1977 maxh = minh = nomh = range(md->md_FixedHeight, minh, maxh);
1979 if (minw > mw) mw = minw;
1980 if (minh > mh) mh = minh;
1982 md->md_MinWidth = minw;
1983 md->md_MinHeight = minh;
1984 md->md_MaxWidth = max(minw, maxw);
1985 md->md_MaxHeight = max(minh, maxh);
1986 md->md_NomWidth = range(nomw, minw, maxw);
1987 md->md_NomHeight = range(nomh, minh, maxh);
1991 m = NULL;
1992 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN)))
1995 * Set equal width/height.
1997 if (gd->gd_Flags & GDF_EQUAL_MINWIDTH) md->md_MinWidth = mw;
1998 if (gd->gd_Flags & GDF_EQUAL_MINHEIGHT) md->md_MinHeight = mh;
2001 if ((rc = AsmDoSuperMethodA(cl, obj, (Msg)bmd)))
2004 * Calculate constraints.
2006 if (!(bmd->bmd_Flags & BDF_CUSTOM_GROUP))
2007 rc = MemberDimensions(cl, obj, bmd);
2009 return rc;
2011 METHOD_END
2013 /// GRM_DIMENSIONS
2014 METHOD(GroupClassDimensionsX, struct grmDimensions *, dim)
2016 GD *gd = INST_DATA(cl, obj);
2017 ULONG rc=0;
2018 BOOL got_members_list;
2020 if(ObtainMembers(obj,gd,&got_members_list)==NULL)
2021 return(0);
2022 rc=AsmDoSuperMethodA(cl, obj, (Msg)dim);
2023 ReleaseMembers(obj,gd,got_members_list);
2024 return(rc);
2026 METHOD_END
2029 /// BASE_SHOWHELP
2031 * Pass on the help message.
2033 METHOD(GroupClassHelp, struct bmShowHelp *, bsh)
2035 GD *gd = INST_DATA(cl, obj);
2036 Object *m;
2037 MD *md;
2038 ULONG rc = BMHELP_NOT_ME;
2040 m = NULL;
2041 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN | MDF_SPACING)) && (rc == BMHELP_NOT_ME))
2044 * Is this the one?
2046 rc = AsmDoMethodA(md->md_Object, (Msg)bsh);
2048 return rc;
2050 METHOD_END
2052 /// GRM_ADDSPACEMEMBER
2054 * Add a spacing object.
2056 METHOD(GroupClassAddSpaceMember, struct grmAddSpaceMember *, grms)
2058 GD *gd = INST_DATA(cl, obj);
2059 Object *m;
2061 if ((m = NewSpaceObject(obj, grms->grms_Weight)))
2063 AsmDoMethod(m, RM_ADDTAIL, &gd->gd_Members);
2064 return TRUE;
2066 return FALSE;
2068 METHOD_END
2070 /// BASE_LEFTEXT
2072 * Route a BASE_LEFTEXT method.
2074 METHOD(GroupClassLeftExt, struct bmLeftExt *, le)
2076 GD *gd = INST_DATA(cl, obj);
2077 BC *bc = BASE_DATA(obj);
2078 IPTR rc;
2081 * Do we have a label or frame? If so we
2082 * take the left-extention of this object.
2084 if (bc->bc_Label)
2086 return AsmDoSuperMethodA(cl, obj, (Msg)le);
2089 if (bc->bc_Frame)
2091 Get_Attr(bc->bc_Frame, FRM_Type, &rc);
2092 if (rc != FRTYPE_NONE)
2094 return AsmDoSuperMethodA(cl, obj, (Msg)le);
2099 * Get the biggest in the first column.
2101 if(gd->gd_CD)
2102 *(le->bmle_Extention) = gd->gd_CD[0].td_PreAlign;
2103 else
2105 *(le->bmle_Extention)=0;
2108 return 1;
2110 METHOD_END
2112 /// GRM_WHICHOBJECT
2114 * Which object is under the mouse?
2116 METHOD(GroupClassWhichObject, struct grmWhichObject *, grwo)
2118 GD *gd = INST_DATA(cl, obj);
2119 struct IBox *ibox;
2120 Object *m;
2121 MD *md;
2122 WORD x, y;
2123 ULONG rc;
2126 * Pick up the coords.
2128 x = grwo->grwo_Coords.X;
2129 y = grwo->grwo_Coords.Y;
2132 * See if the coords are over a member.
2134 m = NULL;
2135 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN | MDF_SPACING)))
2138 * If this is a group/page we pass on the method.
2140 if (IsMulti(md->md_Object))
2142 if ((rc = AsmDoMethodA(md->md_Object, (Msg)grwo)))
2143 return rc;
2147 * Get hitbox bounds.
2149 ibox = GADGETBOX(md->md_Object);
2152 * Over this object?
2154 if (x >= ibox->Left && x < (ibox->Left + ibox->Width ) &&
2155 y >= ibox->Top && y < (ibox->Top + ibox->Height))
2157 * Yes.
2159 return (IPTR)md->md_Object;
2162 return (IPTR)NULL;
2164 METHOD_END
2166 /// BASE_FINDKEY
2168 * Which object has the proper key?
2170 METHOD(GroupClassFindKey, struct bmFindKey *, bmfk)
2172 GD *gd = INST_DATA(cl, obj);
2173 Object *m;
2174 MD *md;
2175 ULONG rc;
2177 if ((rc = AsmDoSuperMethodA(cl, obj, (Msg)bmfk)))
2178 return rc;
2180 m = NULL;
2181 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN | MDF_SPACING)))
2183 if ((rc = AsmDoMethodA(md->md_Object, (Msg)bmfk)))
2184 return rc;
2186 return 0;
2188 METHOD_END
2190 /// #?
2192 * Attach keys from labels.
2194 METHOD(GroupClassAll, Msg, msg)
2196 GD *gd = INST_DATA(cl, obj);
2197 Object *m;
2198 MD *md;
2199 ULONG rc;
2201 rc = AsmDoSuperMethodA(cl, obj, msg);
2203 m = NULL;
2204 while ((md = NextGroupNode(gd, &m, MDF_HIDDEN)))
2206 rc += AsmDoMethodA(md->md_Object, msg);
2208 return rc;
2210 METHOD_END
2212 /// BASE_IS_MULTI
2213 METHOD(GroupClassIsMulti, Msg, msg)
2215 return TRUE;
2217 METHOD_END
2219 /// Class initialization.
2221 * Class function table.
2223 STATIC DPFUNC ClassFunc[] = {
2224 { BASE_RENDER, GroupClassRender },
2225 { BASE_LAYOUT, GroupClassLayout },
2226 { BASE_DIMENSIONS, GroupClassDimensions },
2227 { GRM_DIMENSIONS, GroupClassDimensionsX },
2229 { OM_NEW, GroupClassNew },
2230 { OM_SET, GroupClassSet },
2231 { OM_GET, GroupClassGet },
2232 { OM_DISPOSE, GroupClassDispose },
2233 { GM_GOACTIVE, GroupClassActiveInput },
2234 { GM_HANDLEINPUT, GroupClassActiveInput },
2235 { GM_GOINACTIVE, GroupClassInActive },
2236 { GM_HITTEST, GroupClassHitTest },
2238 { GROUPM_OBTAINMEMBERS, GroupClassObtainMembers },
2239 { GROUPM_RELEASEMEMBERS, GroupClassReleaseMembers },
2240 { GROUPM_NEWMEMBER, GroupClassNewMember },
2241 { GRM_ADDMEMBER, GroupClassAddMember },
2242 { GRM_REMMEMBER, GroupClassRemMember },
2243 { GRM_ADDSPACEMEMBER, GroupClassAddSpaceMember },
2244 { GRM_INSERTMEMBER, GroupClassInsert },
2245 { GRM_REPLACEMEMBER, GroupClassReplace },
2246 { GRM_WHICHOBJECT, GroupClassWhichObject },
2247 { BASE_MOVEBOUNDS, GroupClassAll },
2248 { BASE_FINDKEY, GroupClassFindKey },
2249 { BASE_KEYLABEL, GroupClassAll },
2250 { BASE_LOCALIZE, GroupClassAll },
2251 { BASE_SHOWHELP, GroupClassHelp },
2252 { BASE_LEFTEXT, GroupClassLeftExt },
2253 { BASE_INHIBIT, GroupClassAll },
2254 { BASE_IS_MULTI, GroupClassIsMulti },
2256 { DF_END },
2260 * Simple class initialization.
2262 makeproto Class *InitGroupClass(void)
2264 return BGUI_MakeClass(CLASS_SuperClassBGUI, BGUI_BASE_GADGET,
2265 CLASS_ObjectSize, sizeof(GD),
2266 CLASS_DFTable, ClassFunc,
2267 TAG_DONE);