home *** CD-ROM | disk | FTP | other *** search
-
- /*
-
- OOPExample 1.0 - Copyright © 07-Feb-1997 by Maik "BLiZZeR" Schreiber
- ====================================================================
-
- Hello, this is OOPExample. As stated in OOPExample.readme, it should show you
- how to write an object-oriented program with MUI. However, OOP with MUI does
- not mean C++ here. We are programming in normal C, but using MUI's OOP
- features, ok? ;)
-
- First of all you should have little knowledge of OOP, because it's quite
- complicated to explain it all here. I think it's the best if you get a good
- C++ book from your library or something. Second, you should know how to write
- MUI programs. You should know about MUI's classes, their attributes, methods
- and stuff.
-
- Ok, now let's go on. Before I got to know how MUI's OOP features work,
- I didn't have a clue about it. I didn't know about dispatchers, method
- handlers and all that stuff. I looked at psi.c, but that was so complex
- that I really didn't get the point!
-
- All I wanted was a small example. It should open a window with a button
- inside it. Clicking on this button should open a second window, clicking
- it again should close the second window. Nothing more, nothing less.
- (Maybe a few guys on the MUI mailing remember ;)
-
- Fine, I knew how to do it with return IDs: Clicking the button returns an
- ID to the main input loop, and the loop decides to open or close the second
- window. But damn, that was no OOP!
-
- Then, two guys (Kai Hofmann and Jan Hendrik Schulz, hello there :)) told me
- to implement my example using an attribute. But how?! How to add a new
- attribute to what?
-
- Anyway, I finally had a look at Klaus Melchior's Tron.mcc example and - click!
- Of course! That's how it works!
-
-
- You're still reading? Fine :-))) Let's go on then:
-
- Of course there are may ways to implement a new custom class which satisfies
- my wish for that example above. I want to show you two of them:
-
- 1. Implement a new custom class which is derived from Window.mui. Inside it,
- it shall create a button itself. Clicking on this button should read an
- attribute specifying the other window to open/close and open or close
- that window.
-
- 2. Implement a new custom class which is derived from Text.mui (adding an
- input mode, of course, so that we have a button). Clicking on this button
- again reads out the attribute and opens or closes the other window.
-
- In this example we implement the first way, because it shows general data
- hiding techniques of OOP: The first window object we create from our custom
- class does not know about the button inside the other window and vice versa.
- Furthermore, we have less global identifiers and stuff.
-
- Now let me explain how we're going to implement the new custom class:
-
- First, when creating an object from that class, we create the window itself
- (of course). Next, we create the button inside it. Third, we setup a
- notification on that button to send the object (our window) a method.
-
- "But how does that method know about the other window, which should be
- opened or closed depending on its state?" you may ask.
- This is done via a new attribute called MUIA_OOPWindow_WindowToOpenOrClose.
-
- Look, after the complete application object tree is created within the
- main program, we know about our two window objects, which are objects of
- our new OOPWindow class.
-
- All we have to do now, is to "say" window 1, that its button has to open or
- close window 2. And window 2 has to know that its button should open or close
- window 1. This is done via a more than simple set() call.
-
- THAT'S IT!
-
- Now, when you click the button in window 1, its notification sends the
- specified method (called MUIM_OOPWindow_OpenOrCloseWindow) to the object,
- which is again window 1. Using the attribute window 1 now knows about
- window 2 and is able to open/close it! (Of course, this works the other
- way round in this example.)
-
-
- Ok, girls and guys, now you have the knowledge of how to write object-oriented
- progams with MUI! Isn't that simple? :-)))
- The following source code has loads of comments inside, so just have a look.
-
- Happy programming,
-
- Maik "BLiZZeR" Schreiber
- (why don't you call me "MUI genius", huh? ;)
-
-
-
- ****** IMPORTANT NOTE ****** IMPORTANT NOTE ****** IMPORTANT NOTE ******
-
-
- This source code uses the tag base TAGBASE_BLIZZY, which is defined as
- (TAG_USER | (1465UL << 16)).
-
- PLEASE *DO* *NOT* *USE* THIS TAG BASE IN YOUR OWN APPLICATIONS!!!!!!!!!!!!
- It is a personalized tag base!!!!!!!!!!
-
- If you need such a tag base for creating attributes and methods identifiers,
- you *MUST* write to mccreg@sasg.com and ask for a new tag base for you!!!!!
-
- This is really important!!!
-
-
- ****** IMPORTANT NOTE ****** IMPORTANT NOTE ****** IMPORTANT NOTE *****/
-
-
-
- /*** include files ***/
- #include <exec/types.h>
- #if defined __MAXON__ || defined __STORM__
- #include <pragma/exec_lib.h>
- #else
- #include <proto/exec.h>
- #endif
- #include <exec/libraries.h>
- #if defined __MAXON__ || defined __STORM__
- #include <pragma/muimaster_lib.h>
- #else
- #include <proto/muimaster.h>
- #endif
- #include <libraries/mui.h>
- #include <intuition/classusr.h>
- #if defined __MAXON__ || defined __STORM__
- #include <pragma/utility_lib.h>
- #else
- #include <proto/utility.h>
- #endif
- #include <utility/utility.h>
- #include <clib/alib_protos.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #if defined __MAXON__ || defined __STORM__
- #include <wbstartup.h>
- #endif
-
- #include "OOPExample.h"
-
-
- /*** constants ***/
- /* attributes */
- #define MUIA_OOPWindow_ButtonTitle (TAGBASE_BLIZZY + 1) /* [I.G] STRPTR */
- #define MUIA_OOPWindow_ButtonShortHelp (TAGBASE_BLIZZY + 2) /* [I.G] STRPTR */
- #define MUIA_OOPWindow_WindowToOpenOrClose (TAGBASE_BLIZZY + 3) /* [ISG] Object * */
-
-
- /* methods */
- #define MUIM_OOPWindow_OpenOrCloseWindow (TAGBASE_BLIZZY + 1)
-
- /* miscellaneous */
- #define STR_VERSI_OOPEXAMPLE "1.0"
- #define STR_DATE_OOPEXAMPLE "07-Feb-1997"
- #define STR_VDATE_OOPEXAMPLE "7.2.97"
-
-
- /*** structures ***/
- /* class instance data structure */
- struct OOPWindowData
- {
- Object *wiWindowToOpenOrClose;
- char *button_title;
- char *button_shorthelp;
- };
-
-
- /*** macros ***/
- /* this macro simplifies creation of OOPWindow objects */
- #define OOPWindowObject NewObject(clOOPWindow->mcc_Class, NULL
-
- /* copied from <blizzy/macros.h> */
- #ifndef MAKE_ID
- #define MAKE_ID(a,b,c,d) ((((ULONG) (a)) << 24) | (((ULONG) (b)) << 16) | (((ULONG) (c)) << 8) | ((ULONG) (d)))
- #endif
-
-
- /*** prototypes ***/
- BOOL gui_init(void);
- void gui_exit(void);
- BOOL classes_init(void);
- void classes_exit(void);
-
- ULONG ASM SAVEDS OOPWindow_Dispatcher(REG(a0) struct IClass *, REG(a2) Object *, REG(a1) Msg);
-
- ULONG DoSuperNew(struct IClass *, Object *, ULONG, ...);
- ULONG xget(Object *, ULONG);
-
-
- /*** global identifiers ***/
- Object *apMain = NULL;
- Object * owWindow1;
- Object * owWindow2;
-
- struct MUI_CustomClass *clOOPWindow = NULL;
-
- struct Library *MUIMasterBase = NULL;
- struct Library *UtilityBase = NULL;
-
-
- /*** program ***/
- int main(void)
- {
- int ret = RETURN_FAIL;
-
- /* open all necessary libraries */
- if (!(UtilityBase = OpenLibrary(UTILITYNAME, 37UL)))
- {
- printf("Couldn't open %s 37!\n", UTILITYNAME);
- goto bye;
- }
- if (!(MUIMasterBase = OpenLibrary(MUIMASTER_NAME, MUIMASTER_VMIN)))
- {
- printf("Couldn't open %s %lu!\n", MUIMASTER_NAME, (ULONG) MUIMASTER_VMIN);
- goto bye;
- }
-
- /* create custom classes */
- if (!classes_init())
- {
- puts("Couldn't create custom classes!");
- goto bye;
- }
-
- /* create application */
- if (!gui_init())
- {
- puts("Couldn't create application!");
- goto bye;
- }
-
- /* This is the ideal input loop for an object oriented MUI
- ** application. Everything is encapsulated in classes, no
- ** return IDs need to be used, we just check if the program
- ** shall terminate.
- **
- ** (Quote and source part copied from the original MUI distribution.)
- */
- {
- ULONG sigs = 0UL;
-
- while (DoMethod(apMain, MUIM_Application_NewInput, &sigs) != MUIV_Application_ReturnID_Quit)
- {
- if (sigs)
- {
- sigs = Wait(sigs | SIGBREAKF_CTRL_C);
- if (sigs & SIGBREAKF_CTRL_C)
- break;
- }
- }
- }
-
- ret = RETURN_OK;
-
- bye:
- /* free all resources */
- gui_exit();
- classes_exit();
- if (MUIMasterBase) CloseLibrary(MUIMasterBase);
- if (UtilityBase) CloseLibrary(UtilityBase);
- exit(ret);
- }
-
- /* setup application and do all necessary initializations */
- BOOL gui_init(void)
- {
- if (apMain = ApplicationObject,
- MUIA_Application_Title, "OOPExample",
- MUIA_Application_Version, "$VER: OOPExample " STR_VERSI_OOPEXAMPLE " (" STR_VDATE_OOPEXAMPLE ") © by Maik \"BLiZZeR\" Schreiber, "
- #ifdef __SASC
- "SAS/C"
- #else
- #ifdef __MAXON__
- "MaxonC++"
- #else
- #ifdef __STORM__
- "StormC"
- #else
- #ifdef _DCC
- "Dice C"
- #else
- #ifdef _GCC
- "GNU C"
- #else
- "unknown compiler"
- #endif
- #endif
- #endif
- #endif
- #endif
- " version",
- MUIA_Application_Copyright, "Copyright © " STR_DATE_OOPEXAMPLE " by Maik Schreiber <BLiZZeR@dame.de>",
- MUIA_Application_Author, "Maik Schreiber <BLiZZeR@dame.de>",
- MUIA_Application_Description, "Learn OOP with MUI!",
- MUIA_Application_Base, "OOPEXAMPLE",
-
- SubWindow, owWindow1 = OOPWindowObject,
- MUIA_Window_Title, "OOPExample · Window 1",
- MUIA_Window_ID, MAKE_ID('W', 'I', 'N', '1'),
- MUIA_OOPWindow_ButtonTitle, "Open/close window _2",
- MUIA_OOPWindow_ButtonShortHelp, "This button opens or closes window 2.",
- End,
-
- SubWindow, owWindow2 = OOPWindowObject,
- MUIA_Window_Title, "OOPExample · Window 2",
- MUIA_Window_ID, MAKE_ID('W', 'I', 'N', '2'),
- MUIA_OOPWindow_ButtonTitle, "Open/close window _1",
- MUIA_OOPWindow_ButtonShortHelp, "This button opens or closes window 1.",
- End,
-
- End)
- {
- /* specify which window's button should open/close which window */
- set(owWindow1, MUIA_OOPWindow_WindowToOpenOrClose, owWindow2);
- set(owWindow2, MUIA_OOPWindow_WindowToOpenOrClose, owWindow1);
-
- /* this lets us quit the program by closing one of the windows directly */
- DoMethod(owWindow1, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, apMain, 2, MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
- DoMethod(owWindow2, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, apMain, 2, MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
-
- /* open window 1 and let MUI do the work :-) */
- set(owWindow1, MUIA_Window_Open, TRUE);
-
- return(TRUE);
- }
- return(FALSE);
- }
- /* dispose application */
- void gui_exit(void)
- {
- if (apMain) MUI_DisposeObject(apMain);
- }
- /* create custom classes */
- BOOL classes_init(void)
- {
- /* This is one of the essential parts when fiddling with custom classes.
- ** Here we create our own custom classes, which can be derived from
- ** any MUI class. Our new classes can also be derived from themselves,
- ** e.g. a new window class derived from Window.mui and a second new
- ** window class which is derived from that first window class.
- **
- ** In this OOPExample program, we create a new window class which is
- ** derived from Window.mui.
- **
- ** A note about the datasize parameter of MUI_CreateCustomClass():
- ** This parameter is used to tell MUI, how much memory a class instance
- ** of this class needs for its data. MUI allocates a memory block of
- ** that given size, which you may use for anything you want.
- **
- ** You can always get a pointer to an object's instance data using
- **
- ** INST_DATA(class, obj); // see <intuition/classes.h>
- */
- clOOPWindow = MUI_CreateCustomClass(NULL, MUIC_Window, NULL, sizeof(struct OOPWindowData), OOPWindow_Dispatcher);
- if (clOOPWindow)
- return(TRUE);
- return(FALSE);
- }
- /* dispose custom classes */
- void classes_exit(void)
- {
- if (clOOPWindow) MUI_DeleteCustomClass(clOOPWindow);
- }
-
- /* This is the constructor of our newly created OOPWindow custom class. */
- ULONG OOPWindow_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
- {
- Object *button;
- struct TagItem *tag;
- char *button_title;
- char *button_shorthelp;
-
- /* The MUIA_OOPWindow_ButtonTitle attribute needs a bit more overhead... */
- if (tag = FindTagItem(MUIA_OOPWindow_ButtonTitle, msg->ops_AttrList))
- button_title = (char *) tag->ti_Data;
- else
- button_title = "";
-
- /* ... as does the MUIA_OOPWindow_ButtonShortHelp attribute... */
- if (tag = FindTagItem(MUIA_OOPWindow_ButtonShortHelp, msg->ops_AttrList))
- button_shorthelp = (char *) tag->ti_Data;
- else
- button_shorthelp = "";
-
-
- /* This is the point where we create the new object from our class.
- ** There exist two ways for doing this:
- **
- ** 1. Just creating the new object.
- ** This is done via DoSuperMethodA():
- **
- ** if (obj = (Object *) DoSuperMethodA(cl, obj, (Msg)))
- ** {
- ** ...
- ** }
- **
- ** 2. Creating the object plus adding some new tags to its initial
- ** taglist.
- ** This is done via DoSuperNew(), which is an extra function calling
- ** DoSuperMethodA() again:
- **
- **
- ** if (obj = (Object *) DoSuperNew(cl, obj,
- ** ... add new ("hardcoded") tags here ...
- ** TAG_MORE, msg->ops_AttrList))
- ** {
- ** ...
- ** }
- **
- **
- ** In OOPExample, we want to create a button inside the window object.
- ** Since the main program should not create the button but we want to
- ** create it ourselves, we have to do a call to DoSuperNew().
- */
- if (obj = (Object *) DoSuperNew(cl, obj,
- WindowContents, VGroup,
- Child, TextObject,
- MUIA_Text_Contents, "\033c\033b\033uOOPExample " STR_VERSI_OOPEXAMPLE,
- MUIA_Font, MUIV_Font_Big,
- End,
- Child, TextObject,
- MUIA_Text_Contents, "\033cCopyright © " STR_DATE_OOPEXAMPLE " by Maik \"BLiZZeR\" Schreiber/IQ Computing",
- End,
- Child, TextObject,
- MUIA_Text_Contents, "\033c(This version was compiled using "
- #ifdef __SASC
- "SAS/C."
- #else
- #ifdef __MAXON__
- "MaxonC++."
- #else
- #ifdef __STORM__
- "StormC."
- #else
- #ifdef _DCC
- "Dice C."
- #else
- #ifdef _GCC
- "GNU C."
- #else
- "an unknown C compiler?!"
- #endif
- #endif
- #endif
- #endif
- #endif
- ")",
- MUIA_Font, MUIV_Font_Tiny,
- End,
- Child, button = SimpleButton(button_title),
- End,
- TAG_MORE, msg->ops_AttrList))
- {
- /* get instance data of the newly created object */
- struct OOPWindowData *data = INST_DATA(cl, obj);
-
- /* set default values */
- memset(data, 0, sizeof(struct OOPWindowData));
- data->button_title = button_title;
- data->button_shorthelp = button_shorthelp;
-
- /* use a trick to set the initial values */
- msg->MethodID = OM_SET;
- DoMethodA(obj, (Msg) msg);
- msg->MethodID = OM_NEW;
-
- /* set cycle chain */
- set(button, MUIA_CycleChain, TRUE);
-
- /* bubble help */
- set(button, MUIA_ShortHelp, button_shorthelp);
-
- /* Setup notification for the button to open/close a window.
- ** We simply send ourselves the MUIM_OOPWindow_OpenOrCloseWindow
- ** method.
- */
- DoMethod(button, MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, MUIM_OOPWindow_OpenOrCloseWindow);
-
- /* return address of the new object */
- return((ULONG) obj);
- }
- /* return failure indicator */
- return(0);
- }
- /* This is the OM_SET method handler of our class. */
- ULONG OOPWindow_SET(struct IClass *cl, Object *obj, struct opSet *msg)
- {
- /* get instance data of our object */
- struct OOPWindowData *data = INST_DATA(cl, obj);
- struct TagItem *tags = msg->ops_AttrList;
- struct TagItem *tag;
-
- /* The identifier no_notify gives us the possibility to handle
- ** the MUIA_NoNotify attribute in our class. If you need this feature
- ** in your class, simple remove the comment markers here and a few
- ** lines below. Then, you have to write
- **
- ** SetAttrs(obj, MUIA_NoNotify, no_notify, ...);
- **
- ** to take care of MUIA_NoNotify.
- */
- /*
- BOOL no_notify = FALSE;
- */
-
- /* There are many ways to find out what tag items provided by set()
- ** we do know. The best way should be using NextTagItem() and simply
- ** browsing through the list.
- */
- while (tag = NextTagItem(&tags))
- {
- switch (tag->ti_Tag)
- {
- /* handle MUIA_NoNotify in our class */
- /*
- case MUIA_NoNotify:
- no_notify = (BOOL) tag->ti_Data;
- break;
- */
-
- /* specify the window, which should be opened/closed on pressing
- ** the button
- */
- case MUIA_OOPWindow_WindowToOpenOrClose:
- data->wiWindowToOpenOrClose = (Object *) tag->ti_Data;
- break;
- }
- }
- return(DoSuperMethodA(cl, obj, (Msg) msg));
- }
- /* Here comes the OM_GET method handler. */
- ULONG OOPWindow_GET(struct IClass *cl, Object *obj, struct opGet *msg)
- {
- /* The OM_GET handler also has a very special behaviour:
- ** Since you can only get() one attribute value at a time,
- ** it has to return() TRUE if it does "understand" your
- ** attribute identifier.
- ** If not, the handler must pass the attribute identifier
- ** to the superclass via DoSuperMethodA().
- */
-
- /* small macro to simplify return value storage */
- #define STORE *(msg->opg_Storage)
-
- /* get instance data of our object */
- struct OOPWindowData *data = INST_DATA(cl, obj);
-
- switch(msg->opg_AttrID)
- {
- /* Return button title of our button.
- ** This attribute isn't used in this example program,
- ** but it shows another time how the technique is done.
- **
- ** NOTE: In this example, the returned string must not be
- ** modified!
- */
- case MUIA_OOPWindow_ButtonTitle:
- STORE = (ULONG) data->button_title;
- return(TRUE);
-
- /* return bubble help string of our button */
- case MUIA_OOPWindow_ButtonShortHelp:
- STORE = (ULONG) data->button_shorthelp;
- return(TRUE);
-
- /* return the specified window, which should be opened/closed
- ** on pressing the button
- */
- case MUIA_OOPWindow_WindowToOpenOrClose:
- STORE = (ULONG) data->wiWindowToOpenOrClose;
- return(TRUE);
- }
-
- /* our handler didn't understand the attribute, we simply pass
- ** it to our superclass now
- */
- return(DoSuperMethodA(cl, obj, (Msg) msg));
- #undef STORE
- }
- /* MUIM_OOPWindow_OpenOrCloseWindow method handler */
- ULONG OOPWindow_OpenOrCloseWindow(Object *obj)
- {
- /* This is the "main" method of our OOPWindow class.
- ** Here's what it does:
- **
- ** 1. Get the window, which should be opened or closed.
- ** 2. Open/close the window depending on its current state
- ** (ie. if it's open, close it, otherwise open).
- **
- ** Since this is a new method, we don't have anything to pass to
- ** our superclass here. Instead, we return() TRUE to indicate that
- ** all went ok.
- **
- ** NOTE: In this example, the return value of this method isn't used,
- ** so we could also return() FALSE here. When writing custom classes,
- ** it can sometimes be very handy to have return values. You can get
- ** them from your DoMethod() call to this method.
- */
-
- Object *window;
-
- if (window = (Object *) xget(obj, MUIA_OOPWindow_WindowToOpenOrClose))
- set(window, MUIA_Window_Open, xget(window, MUIA_Window_Open) ? FALSE : TRUE);
- return(TRUE);
- }
- /* Here you see the dispatcher of our class. */
- ULONG ASM SAVEDS OOPWindow_Dispatcher(REG(a0) struct IClass *cl, REG(a2) Object *obj, REG(a1) Msg msg)
- {
- /* The class dispatcher is the function, that will be called when a
- ** method is sent to your class. The dispatcher has to determine
- ** if it understands the method. If yes, it calls the according
- ** method handler (you can also write the handler here instead of
- ** return()ing a function return value - that's a style question).
- ** If not, it has to pass the method to the superclass.
- */
-
- /* watch out for methods we do understand */
- switch (msg->MethodID)
- {
- /* Whenever an object shall be created using NewObject(), it will be
- ** sent a OM_NEW method.
- */
- case OM_NEW: return(OOPWindow_NEW(cl, obj, (struct opSet *) msg));
-
- /* SetAttrs() sends a OM_SET method */
- case OM_SET: return(OOPWindow_SET(cl, obj, (struct opSet *) msg));
-
- /* GetAttr() sends a OM_GET method */
- case OM_GET: return(OOPWindow_GET(cl, obj, (struct opGet *) msg));
-
- /* one of our own methods */
- case MUIM_OOPWindow_OpenOrCloseWindow: return(OOPWindow_OpenOrCloseWindow(obj));
- }
-
- /* we didn't understand the last method, so call our superclass */
- return(DoSuperMethodA(cl, obj, msg));
- }
-
- /* This is a small function to add some new capabilities to
- ** DoSuperMethodA().
- */
- ULONG DoSuperNew(struct IClass *cl, Object *obj, ULONG tag1, ...)
- {
- return(DoSuperMethod(cl, obj, OM_NEW, &tag1, NULL));
- }
- /* This simplifies get()ting values of attributes. */
- ULONG xget(Object *obj, ULONG attr)
- {
- ULONG ret;
-
- get(obj, attr, &ret);
- return(ret);
- }
-
-