Boopsi in Release 3 by John Orr and Peter Cherna For Release 3, Intuition gained significant improvements that directly affect Intuition's object oriented programming subsystem, Boopsi. Intuition now has gadget help features, bounding boxes for gadgets and their imagery, special custom layout gadgetry, and several other features that all have an impact on Boopsi. This article assumes that the reader already has a basic understanding of Boopsi and the object oriented programming model. For those not comfortable with these concepts, see the ``Boopsi--Object Oriented Intuition'' chapter of the ROM Kernel Reference Manual: Libraries. DoGadgetMethodA() One Boopsi-related function added to intuition.library for Release 3 is DoGadgetMethodA(): ULONG DoGadgetMethodA(struct Gadget *object, struct Window *win, struct Requester *req, Msg msg); This function is similar to amiga.lib's DoMethod(), except DoGadgetMethod() passes along the object's graphical environment in the form of a GadgetInfo structure. DoGadgetMethodA() gets this structure from the gadget's window (or requester). The GadgetInfo structure is important because a Boopsi gadget needs the information in that structure to render itself. Note that if you give DoGadgetMethodA() a NULL Window and NULL Requester pointer, DoGadgetMethodA() will pass a NULL GadgetInfo pointer. The name and first parameter of DoGadgetMethodA() may lead you to believe that this function applies only to gadgets. Although you can certainly use this function on gadget objects, the function is not restricted to gadget objects. You can use this function on any Boopsi object. This is important for an object such as a model object, which may react to any Boopsi message by invoking some method on gadget objects connected to it. Because the model object receives environment information in the form of a GadgetInfo structure, the model can pass that information on to any objects connected to it. Before this function, passing the environment information was not that easy. A good example of this is the rkmmodelclass example in the ``Boopsi--Object Oriented Intuition'' chapter of RKRM: Libraries (page 312-315). In that example, the rkmmodelclass is a subclass of modelclass. The rkmmodelclass object inherits the behavior of modelclass, so its sends updates to its member objects. One feature rkmmodelclass adds to modelclass is that these objects maintain an internal integer value. If that value changes, the rkmmodelclass object propagates that change to its member objects. If one of the member objects happens to be a gadget object, changing the rkmmodelclass object's internal value may change the visual state of the gadgets. For the gadget's to update their visual state, they need the environment information from the GadgetInfo structure as well as the new internal value of the rkmmodelclass object. If an application used DoMethod() or SetAttrs() to set the rkmmodelclass object's internal value, the rkmmodelclass object would not get a GadgetInfo structure. When the rkmmodelclass object propagates the internal value change to its member objects, it has no environment information to pass on to its member objects. As a result, the member gadgets can not update their visual state directly. This is particularly annoying for a propgclass object, because the visual state of the propgclass object can depend on the rkmmodelclass object's integer value. DoGadgetMethodA() corrects this problem. It passes a pointer to a GadgetInfo structure for the window (or requester) you pass to it. If you plan on implementing new Boopsi methods that need a GadgetInfo structure in their Boopsi message, make sure the second long word of the Boopsi message is a pointer to the GadgetInfo structure. DoGadgetMethodA() assumes that every method (except for OM_NEW, OM_SET, OM_NOTIFY, and OM_UPDATE; see the next paragraph) expects a GadgetInfo pointer in that place. Iif you use DoGadgetMethodA() to send an OM_SET message to a Boopsi gadget, DoGadgetMethodA() passes a pointer to a GadgetInfo structure as the third long word in the Boopsi message. DoGadgetMethodA() is smart enough to know that the OM_SET method uses an opSet structure as its message, which has a pointer to a GadgetInfo structure as its third long word. DoGadgetMethodA() passes a GadgetInfo structure as the third parameter for the OM_NEW, OM_SET, OM_NOTIFY, and OM_UPDATE methods. For all other methods, like the gadgetclass methods, DoGadgetMethodA() passes a GadgetInfo structure as the second long word. For more information, see the V39 Autodoc for DoGadgetMethodA() and its varargs equivalent, DoGadgetMethod(). Bounding Boxes for Gadgets Before Release 3, gadgets only had a select (or hit) box. This box is the area of the window that the user can click in to select the gadget. The select box limits certain gadgets because the gadget's imagery could lie outside of the gadget's hit box. For example, the hit box for a string gadget is the area that text appears when the user types into the gadget (see Figure 1). Many string gadgets have a border and a label, which the programmer sets using the Gadget structure's GadgetRender and GadgetText fields. The border and label imagery appears outside of the hit box. Figure 1 - String Gadget without Bounding Box The major disadvantage of a gadget's imagery being external to its hit box has to do with relative gadgets (these are sometimes referred to as GREL gadgets). Intuition positions one of these gadgets relative to the edge's of the gadget's window. When the window changes dimensions, Intuition repositions the gadget within the window. The gadget can also make its size relative to the window's dimensions. When Intuition resizes a window, it remembers which portions of the window's display sustained visual damage. When Intuition refreshes the visual state of the window, it only redisplays the parts of the window that sustained visual damage. When Intuition resizes a window that has a GREL gadget, Intuition erases the old gadget by erasing the hit box of the gadget and remembers that area as a region it will have to refresh. Intuition also figures out where the the new hit box will be and remembers that area as a region Intuition will have to refresh. Because Intuition will not erase or redraw any imagery that falls outside of the GREL gadget's hit box, Intuition will ignore any part of the gadget's label or border that is outside the hit box. To remedy this situation, Intuition added a bounding box to gadgets. If a gadget has a bounding box, Intuition uses the bounding box in place of the hit box when figuring out what areas to refresh. With a bounding box, a gadget can extend its hit area to encompass all of its imagery. The bounding box feature is not specific to Boopsi gadgets. Any Intuition gadget can have one. If the gadget doesn't supply a bounding box, Intuition will use the gadget's normal hit box as the bounding box, which yields the same result as previous versions of the OS. Adding the bounding box feature to Intuition gadgets required extending the Gadget structure. There is a new structure called ExtGadget that is a superset of the old Gadget structure (from <intuition/intuition.h>): struct ExtGadget { /* The first fields match struct Gadget exactly */ struct ExtGadget *NextGadget; /* Matches struct Gadget */ WORD LeftEdge, TopEdge; /* Matches struct Gadget */ WORD Width, Height; /* Matches struct Gadget */ UWORD Flags; /* Matches struct Gadget */ APTR SpecialInfo; /* Matches struct Gadget */ UWORD GadgetID; /* Matches struct Gadget */ APTR UserData; /* Matches struct Gadget */ /* These fields only exist under V39 and only if GFLG_EXTENDED is set */ ULONG MoreFlags; WORD BoundsLeftEdge; /* Bounding extent for gadget, valid */ WORD BoundsTopEdge; /* only if the GMORE_BOUNDS bit in */ /* the MoreFlags field is set. The */ WORD BoundsWidth; /* GFLG_RELxxx flags affect these */ WORD BoundsHeight; /* coordinates as well. */ }; Intuition discerns a Gadget from an ExtGadget by testing the Flags field. If the GFLG_EXTENDED bit is set, the Gadget structure is really an ExtGadget structure. Intuition will use the bounding box only if the GMORE_BOUNDS bit in the ExtGadget.MoreFlags field is set. Gadgetclass supports an attribute called GA_Bounds that sets up a Boopsi gadget's bounding box. It expects a pointer to an IBox structure (from <intuition/intuition.h>) in the ti_Data field, which gadgetclass copies into the bounding box fields in the ExtGadget structure. Gadget Help Intuition V39 introduces a help system designed around Intuition gadgets. Window-based applications can elect to receive a new type of IDCMP message called IDCMP_GADGETHELP when the user positions the pointer over one of the window's gadgets. The pointer is over the gadget if the pointer is within the gadget's bounding box (which defaults to the gadget's hit box). Using the intuition.library function HelpControl(), an application can turn Gadget Help on and off for a window: VOID HelpControl(struct Window *my_win, ULONG help_flags); Currently, the only flag defined for the help_flags field is HC_GADGETHELP (from <intuition/intuition.h>). If the HC_GADGETHELP bit is set, HelpControl() turns on help for my_win, otherwise HelpControl() turns off gadget help for the window. When gadget help is on for the active window, Intuition sends IDCMP_GADGETHELP messages to the window's IDCMP port. Each time the user moves the pointer from one gadget to another, Intuition sends an IDCMP_GADGETHELP message about the new gadget. Intuition also sends an IDCMP_GADGETHELP message when the user positions the pointer over a gadgetless portion of the window, and when the user moves the pointer outside of the window. An application can find out what caused the IDCMP_GADGETHELP message by first examining the IntuiMessage's IAddress field. If IAddress is NULL, the pointer is outside of the window. If IAddress is the window address, the pointer is inside of the window, but not over any gadget. If IAddress is some other value, the user positioned the pointer over one of the window's gadgets and IAddress is the address of that gadget. To discern between the different system gadgets (window drag bar, window close gadget, etc.) an application has to check the GadgetType field of the gadget: #define GTYP_SYSTYPEMASK 0xF0 /* This constant did not appear in */ /* earlier V39 include files. */ sysgtype = ((struct Gadget *)imsg->IAddress)->GadgetType & GTYP_SYSTYPEMASK; The constant GTYP_SYSTYPEMASK (defined in <intuition/intuition.h>) corresponds to the bits of the GadgetType field used for the system gadget type. There are several possible values for sysgtype (defined in <intuition/intuition.h>): GTYP_SIZING Window sizing gadget GTYP_WDRAGGING Window drag bar GTYP_WUPFRONT Window depth gadget GTYP_WDOWNBACK Window zoom gadget GTYP_CLOSE Window close gadget If sysgtype is zero, IAddress is the address of an application's gadget. Not all gadget's will trigger an IDCMP_GADGETHELP event. Intuition will send gadget help events when the pointer is over a gadget with an extended Gadget structure (struct ExtGadget from <intuition/intuition.h>) and a set GMORE_GADGETHELP flag (from the ExtGadget.MoreFlags field). To set or clear this flag for a Boopsi gadget, an application can use the GA_GadgetHelp attribute. Setting GA_GadgetHelp to TRUE sets the flags and setting it to FALSE clears the flag. Only the OM_SET method applies to this gadgetclass attribute. To support gadget help, gadgetclass gained a new method called GM_HELPTEST. This method uses the same message structure as GM_HITTEST, struct gpHitTest (defined in <intuition/gadgetclass.h>), and operates similarly to GM_HITTEST. While a window is in help mode, when the user positions the pointer within the bounding box of a Boopsi gadget that supports gadget help, Intuition sends the gadget a GM_HELPTEST message. Like the GM_HITTEST method, the GM_HELPTEST method asks a gadget if a point is within the gadget's bounds. Like the GM_HITTEST method, the GM_HELPTEST method allows a Boopsi gadget to have a non-rectangular hit area (or in this case, a help area). If the point is within the gadget, the gadget uses one of two return codes. If the gadget returns a value of GMR_HELPHIT, Intuition places a value of 0xFFFF in the Code field of the IDCMP_GADGETHELP message. The gadget also has the option of using GMR_HELPCODE instead of GMR_HELPHIT. GMR_HELPCODE is a little peculiar as a return value. Although GMR_HELPCODE is 32 bits long, Intuition identifies GMR_HELPCODE using only its upper 16 bits. If the upper 16 bits of GM_HELPTEST's return value matches the upper 16 bits of GMR_HELPCODE, Intuition copies the lower word of the return value into the Code field of the IDCMP_GADGETHELP message. The Boopsi gadget is free to set the return value's lower word to any 16-bit value. If the point is not within the gadget's ``help spot'', the gadget returns GMR_NOHELPHIT. Intuition will then look under that gadget for more gadgets. If a gadget's dispatcher passes the GM_HELPTEST method on to the gadgetclass dispatcher, the gadgetclass dispatcher will always return GMR_HELPHIT. An application can put several windows in the same help group. This feature groups several windows so that as long as one of those windows is active, the application can receive IDCMP_GADGETHELP messages about all of those windows. For more information, See the HelpControl() Autodoc and the WA_HelpGroup section of the OpenWindow() Autodoc (both are in intuition.doc). Boopsi Gadgets and ScrollRaster() This section covers some fairly complicated aspects of Intuition Windows and how Intuition interacts with the Layers system. For a more in depth explanation, see the article, ``Optimized Window Refreshing'' from the July/August 1992 issue of Amiga Mail. Scrolling an Intuition display with ScrollRaster() is unlike many other rendering operations because it can damage a window layer. ScrollRaster() is both a rendering function and a layering function (ScrollRaster() is a graphics.library function, but it is aware of the Layers system). If window X overlaps window Y, scrolling window Y can scroll a portion of window Y out from underneath window X: Figure 2 - Damage from a Scrolling Operation For both smart refresh and super bitmap windows, this does not present a problem. Layers remembers what was underneath window X and restores that portion of the display when a layers operation reveals it. When a scrolling operation damages a smart refresh or super bitmap window, Layers repairs the display. What is Layer Damage? The Layers definition of ``damage'' is a little confusing. Looking at the illustration, two areas of window Y need to be refreshed: the area that used to be underneath window X, and the area that was scrolled into view at the bottom of window Y (the partially visible ``RSTUVWXY''). Layers considers only one of these areas to be ``damaged'', the area the was scrolled out from underneath window X. Layers considers it damaged because the damage was caused by the presence of another window layer. The task that called ScrollRaster() has no idea that there is an overlapping window layer, so it is up to Layers to account for that damage. On the other hand, the task that called ScrollRaster() knows that the area at the bottom of window Y needs refreshing, so the task knows to fix that area of the window. To Layers, damage is the area of a layer that needs refreshing as a result of layers operation. For a simple refresh window, Layers remembers which portion of the window layer is damaged. Layers does nothing to repair the damage. Layers leaves repairing the damage to Intuition. After performing a layers operation on a window (such as scrolling a portion of a window), Intuition must check if that operation caused layer damage (either to that window or other windows on the display). In Release 2, when Intuition told a scrolling Boopsi gadget to render itself, Intuition did not check for layer damage. If that gadget damaged the display with the ScrollRaster() function, Intuition did not repair that damage. As of Release 3.0, Intuition has a flag set aside in the MoreFlags field of the ExtGadget structure called GMORE_SCROLLRASTER (defined in <intuition/intuition.h>). If this flag is set, when the gadget damages the display with ScrollRaster() (or ScrollRasterBF()), Intuition redraws all GMORE_SCROLLRASTER gadgets in the damaged window. The class dispatcher should take care of setting this flag when creating the gadget in the OM_NEW method. ScrollRaster() is not the only function in Release 3 that can scroll a window's contents. For Release 3, the Intuition library gained a function specifically to scroll a window's raster. That function, ScrollWindowRaster(), is similar to ScrollRaster(), but ScrollWindowRaster() is Intuition-friendly. Boopsi gadgets must not use this function, they should use ScrollRaster() or ScrollRasterBF() instead. Also any applications that use GMORE_SCROLLRASTER gadgets must use ScrollWindowRaster() for scrolling. Note that gadgets supplied by third parties or Commodore may be GMORE_SCROLLRASTER gadgets. For more information on ScrollWindowRaster(), see its Autodoc. Smart Layout Gadgets Prior to Release 3, Intuition took care of laying out GREL gadgets whenever the dimensions of the window change. This was a limitation for a GREL Boopsi gadget that wants to perform its own layout. Because Intuition didn't tell the Boopsi gadget that the window's dimensions changed, the gadget cannot react to the change. As of Release 3, Intuition uses a new gadget method called GM_LAYOUT to tell a GREL Boopsi gadget that its window's dimensions have changed. The GM_LAYOUT method uses the gpLayout structure (defined in <intuition/gadgetclass.h>) as its message: struct gpLayout { ULONG MethodID; struct GadgetInfo *gpl_GInfo; ULONG gpl_Initial; /* This field is non-zero if this method was invoked * during AddGList() or OpenWindow(). zero if this * method was invoked during window resizing. */ }; For GREL gadgets, Intuition sends a GM_LAYOUT message just after erasing the gadget's bounding box. Intuition does not touch the values of the gadget's bounding box or hit box, it lets the gadget handle recalculating these values. Thee gadget can lay itself out based on the new window (or requester) dimensions found in the GadgetInfo structure passed in the GM_LAYOUT message (gpl_GInfo from the gpLayout structure). Intuition also adds the old and new bounding box of the gadget to the window's damaged regions so that the gadget will appear is its new position. The gadget must not perform any rendering inside the GM_LAYOUT method. Intuition will send a GM_RENDER message to the gadget when its time to redraw the gadget in its new position. There are two cases where Intuition sends a GM_LAYOUT message: When the gadget's window is resized When Intuition adds the gadget to a window There are two ways for a window to gain gadgets. An application can explicitly add gadgets using the AddGList() function. The window can also gain gadgets during it initialization in the OpenWindow() call. For most GREL Boopsi gadgets, Intuition expects the values in the LeftEdge, TopEdge, Width, and Height fields to follow the existing convention for regular GREL gadgets. Each of these fields has a flag in the Gadget.Flags field (GFLG_RELRIGHT, GFLG_RELBOTTOM, GFLG_RELWIDTH, and GFLG_RELHEIGHT, respectively). If that field's flag is set, Intuition expects the value in that field to be relative to either the window border or the window dimensions. For example, if GFLG_RELRIGHT is set, Intuition expects the value in the gadget's LeftEdge field to be relative to the right window border. There is a special kind of GREL Boopsi gadget called a custom relativity gadget. For this type of gadget, Intuition expects the values in the LeftEdge, TopEdge, Width, and Height fields to be absolute measurements, just like the values Intuition expects in these fields for non-GREL gadgets. Setting the GFLG_RELSPECIAL bit in the Gadget.Flags field marks the gadget as a custom relativity gadget. The best way to set this bit is by setting the GA_RelSpecial attribute to TRUE when creating the gadget. Disabled Imagery Certain gadget types support using a Boopsi Image as its imagery. These gadget types include the non-Boopsi boolean gadget, the frbuttonclass gadget, and the buttongclass gadget (This can also include private gadget classes that support the GA_Image attribute). The Gadget structure contains a field called GadgetRender which can point to a Boopsi Image object (the GFLG_GADGIMAGE bit in the Gadget.Flags field should also be set, see the``Intuition Gadgets'' chapter of the RKRM:Libraries for more information). Intuition uses this image as the gadget's imagery. When a gadget uses this scheme for its imagery, Intuition sends IM_DRAW (and IM_DRAWFRAME) messages to the image object. These imageclass methods not only tell a Boopsi image to render itself, they also tell the image which state to draw itself. Some examples of image states include normal, selected, and disabled. Prior to Release 3, when Intuition sent a draw message to one of these images, it only used the normal and selected states. If the image was disabled, Intuition told the image to draw itself as normal or selected and would render the standard ``ghosted'' imagery over the gadget. Under Release 3, Intuition will use the disabled state as well, but only if the gadget's image object supports it. When Intuition adds the gadget to a window, Intuition tests the IA_SupportsDisabled attribute of the gadget's image object. This imageclass attribute was introduced in Release 3. If this attribute is TRUE, the image supports disabled imagery (both the IDS_DISABLED and IDS_SELECTEDDISABLED states), so Intuition sets the GFLG_IMAGEDISABLE flag in the Gadget structure's Flags field. Intuition uses this flag to tell if it should use the image's disabled state in the IM_DRAW/IM_DRAWFRAME message. As the IA_SupportsDisabled attribute is a fixed property of an image object, it is only accessible using the OM_GET method. An application cannot set it or clear it. Frameiclass Frame Types For Release 3, the Boopsi image class frameiclass acquired a new attribute, IA_FrameType. This attribute allows applications to choose a frame style for a frame image. Currently there are four styles available: FRAME_DEFAULT - This is the default frame which was the only available frame in V37. It has thin edges. FRAME_BUTTON - This is the imagery for the standard button gadget. It has thicker sides and nicely edged corners. FRAME_RIDGE - This is a ridge commonly used by standard string gadgets. When recessed (using the frameiclass attribute IA_Recessed), this image appears as a groove. Applications and utilities commonly use the recessed image to visually group objects on the display. FRAME_ICONDROPBOX - This broad ridge is the system standard imagery for icon ``drop boxes'' inside of an AppWindow (see the ``Workbench and Icon Library'' chapter of the RKRM:Libraries for more information on AppWindows). The recessed version has no standard purpose. Figure 3 - Frameiclass Frame Types Additions to Sysiclass The class of system images, sysiclass, gained two new images for Release 3, a menu check mark image and an Amiga key image (respectively, MENUCHECK and AMIGAKEY from <intuition/imageclass.h>): Figure 4 - Menu Check Mark and Amiga Key Images This class also gained a new attribute called SYSIA_ReferenceFont. This attribute, which is only accessible with the OM_NEW method, points to a TextFont structure. The MENUCHECK and AMIGAKEY images will use this font to figure out how large to make the image. This attribute overrides the SYSIA_DrawInfo font and SYSIA_Size attributes when calculating the dimensions of the image. About the Example The example at the end of this article, relative.c, uses three features mentioned in this article. The example implements a private subclass of gadgetclass. The new class utilizes the frameiclass IA_FrameType attribute to create a button gadget. The class also takes advantage of the custom relativity feature. The example opens a window placing one of these gadgets in the middle of the window, sizing the gadget relative to the window's dimensions. The example also illustrates the gadget help feature of Intuition. When the private class dispatcher receives a GM_HELPTEST message, it returns the bitwise AND of GMR_HELPCODE and the value 0xC0DE. When the example receives an IDCMP_GADGETHELP message at the window's message port, the example examines the message to find out what object triggered the help message and printf()s the results. relative.c