Aros/Developer/Zune/Classes

From Wikibooks, open books for an open world
Jump to: navigation, search
Navbar for the Aros wikibook
Aros User Docs
Aros User Docs
Aros User FAQs
Aros User Applications
Aros User DOS Shell
Aros/User/AmigaLegacy
Aros Dev Docs
Aros Developer Docs
Porting Software from AmigaOS/SDL
For Zune Beginners
Zune .MUI Classes
For SDL Beginners
Aros Developer BuildSystem
Specific platforms
68k Support
PPC Power Architecture Support
Arm Raspberry Pi Support
Android support
Linux and FreeBSD Support
Windows Mingw and MacOSX Support
Aros x86 Installing
Aros x86 Audio/Video Support
Aros x86 Network Support
Aros x86 Complete System HCL
Aros Storage Support IDE SATA etc
Aros Poseidon USB Support
x86-64 Support
misc
Aros Public License


Introduction[edit]

Magic User Interface MUI

Mui original classes or here are identified by the trailing .mui like List, String, etc

MUI (Zune) replacement classes MCCs came later which include Nlist, BetterString, etc which some developers frown upon.


Please read autodocs/MUI_Application.doc/MUIM_Application_NewInput it has a proper example for a complete main loop. A MUI program has a main loop, but in an ideal application the loop is empty. Main loops as well as hooks are a thing of the past but backwards compatibility is still there.


New method So nowadays, in order to handle notifications from buttons, sliders, etc - you subclass Application.mui, Window.mui or <one of the groups>.mui of your choice, define your own private methods there and setup notifications on your buttons to call these methods. This lets you keep a neat internal structure rather than having everything in a one huge switch statement of return ids.

Methods have no TRUE or FALSE states nor anything other defined states, they are just function calls (well, sort of). The only approach is subclassing technique and overload the method. You can then do anything you wish... The custom class is your way.

To define your own private method and attribute ids you could start with TAG_USER.

Read more here



Old Method With MUI you can also call hooks to avoid creating subclasses, but only the change of an attribute can cause an action.



  • First set Application and Window classes information.
  • Use Horizontal Groups (HGroup) or Vertical Groups (VGroup) to stuff objects (buttons, sliders, directory listers, etc) into them. Objects are not placed directly to some fixed locations but are instead grouped in special containers which are then dynamically displayed and positioned.
  • Use Notification to do something when object changes (pressed, slide, etc)
  • Release them when finished or when ending app


Macros play a part in programming Zune interfaces. Get and Xget can replace GetAttrs and Set and Xset similarly with SetAttrs.


When you read the mui autodocs there is a field after the function name with [ISG] and sometimes also [ISGN]

And that indicates what to can do with the tag.

I = Init
S = SetAttr can be used
G = GetAttr can be used
N = Supports Notify

So if you see [I.G] that means that you can use the Tag at Init and use GetAttr on it.



MUI Zune Base Classes[edit]

BOOPSI rootclass connected to Zune NOTIFY.mui class
Application.mui Window.mui Family.mui Area.mui
Aboutmui Menustrip Rectangle
Menu Balance
Menulist Image
Menubar
Bitmap
Text
Gadget
Gauge
Scale
Colorfield
List
Numeric
Pendisplay
Group

encapsulation is what you have with all the MUI classes. For example to use a listview you have a well defined interface for this gadget, and all implementation details are encapsulated into the class (like private variables etc.)

You would do similar with an advanced application. Locate all sub-systems and implement these as "isolated" modules with interfaces and implementation details encapsulated into the modules, not visible from the outside.


inheritance via subclassing


polymorphism




Subclasses[edit]

Subclassing Classes


Good source examples Snakee, List,



Application[edit]

Read more here and Application Class here

Instead of MUI_NewObject(MUIC_Application, ,etc) is often replaced by the macro ApplicationObject, etc, etc...

When subclassing window or application class there aren't MUIM_Setup/Cleanup methods at all.

If you want to make a gui where everything is made of custom classes (at least the outer stuff, ie application and windows), you define all your classes as usual, and then instantiate your application with a single newobject() call. It's no problem to add elements to a group/window/application when it is being allocated (inside OM_NEW).



Window[edit]

Gui Design

Instead of MUI_NewObject(MUIC_Window, ,etc) is often shortened to the macro WindowObject,etc,etc but can be still used in Child Windows...


The way to do this would be to implement a method in one of your subclasses (MUIC_Window or the window's Root MUIC_Group are always safe picks) and have a MUIA_Window_? notification call it.


For the positions to be remembered, you have to close the windows (set MUIA_Window_Open to false) before disposing the Zune Application object



Classes[edit]

Morphos List SubClass

Instead of MUI_NewObject(MUIC_Group, ,etc) is often erewritten as the macro <Group>GroupObject,etc,etc...


Then class creation:

TheClass = MUI_CreateCustomClass (NULL, supername, NULL, sizeof (MyObjectData), dispatcher);
  if (!(mcc = MUI_CreateCustomClass(NULL,MUIC_Area,NULL,sizeof(struct NewClass_Data),NewClass_Dispatcher)))
  {
    printf("Cannot create custom class.\n");
    return(0);
  }


Is it ok to inherit from other types of object? Yes, to subclass slider class to overload MUIM_Numeric_Stringify method.

How to check for a minimum version of a MUI MCC when building a GUI? [...] Your best chance is that the custom class writer has overloaded MUIA_Version/Revision.




Doing Stuff[edit]

And here is a way to access this inside a method:

LONG AnyMethod (Class *cl, Object *obj, Msg msg)
{
struct MyObjectData *data = INST_DATA(cl,obj);

/*...*/

if (!strcmp ("Amiga", data->Pointer)) DoSomething();

/*...*/
}


Memory allocated for object data structure is freed automatically by MUI in object destructor, you have to do nothing.

struct MyObjectData *data = (MyObjectData *)INST_DATA(cl, obj);



Dispatcher[edit]

Then, in your dispatcher subprogram, you write something like case MUIM_NList_ContextMenuBuild then MyOwnContextMenuBuild(cl,obj,msg); then you write a subprogram MyOwnContextMenuBuild that does what you want your class to do when this method is called.

Every object in your subclass gets its own data area added to the object "structure". Also known as a data instantation. It is your own var area privately for the object with the given size in the MUI_CreateCustomClass() call but do not use the obsolete MUI_GetClass() and MakeClass() pairing. See the Class2.c example in the MUI:Developer/C/Examples dir.



Overloading[edit]

Overloading class methods is the main and best design to use when you need to add some class features or use some specials behaviours. In MUI C version this is done by creating a new MCC (MUI Custom Class) and a dispatcher routine catching incoming BOOPSI messages when DoMethod() is called on instance of this MCC.

To find out when a superclass's attribute changes without using a Notify? Yes, just overload OM_SET. Most (if not all) calls to OM_SET are done with a temporary tag list allocated on the stack, so it'll be lost anyway...


If you need this pointer separate for every object, you should place it in object data structure.

struct MyObjectData
{
char *Pointer;
};


Tag list or method structures should be untouched in any way if it's not documented. If MUI or 3rd party mcc's do this they're broken... Example of documented method structures touched by MUI are MUIM_Draw and MUIM_AskMinMax...

MUI will call MUIM_Hide, MUIM_Show on your object if the dimension change. So no need to check this in MUIM_Draw. MUIM_Draw is btw always called for a reason, so see no reason for you to omit a redraw here.

ULONG NewList_New(struct IClass *cl , Object *obj, Msg msg)
{
    return 0;
}


ULONG NewList_Set(struct IClass *cl , Object *obj, Msg msg)
{                   

    return(DoSuperMethodA(cl, obj, msg));
}


ULONG NewList_Get(struct IClass *cl, Object *obj, Msg msg)
{

    return(DoSuperMethodA(cl, obj, msg));
}




Application & Window[edit]

Need to get a pointer to an active window object at any time. How can i do this? Normally you should search for MUIA_Window_Active in Application window list. Can create MUIC_Window subclass and intercept MUIA_Window_Active, and create your API for this. On the other hand, you can setup notification on this tag and if window receives it you will know about it and set some field in memory to window pointer or so.


MUIA_Window_Screen for your own screen and trying to have window size completely under control. Setting MUIA_Window_Width and MUIA_Window_Height always works when you don't specify window ID (MUIA_Window_ID) for WindowObject. In case you specify this ID, height and width are set automatically by system and are remembered for next time you run application.


     app = ApplicationObject,
         MUIA_Application_Title, (IPTR)"KeyShow",
         MUIA_Application_Version, (IPTR)"$VER: KeyShow 1.0 (24.02.2012)",
         MUIA_Application_Copyright, (IPTR)_(MSG_AppCopyright),
         MUIA_Application_Author, (IPTR)"The AROS Development Team",
         MUIA_Application_Description, (IPTR)_(MSG_AppDescription),
         MUIA_Application_Base, (IPTR)"KEYSHOW",
 
         SubWindow, (IPTR)(win = WindowObject,
             MUIA_Window_Title, (IPTR)_(MSG_WI_TITLE),
             MUIA_Window_ID, MAKE_ID('K','S','W','N'),
             WindowContents, (IPTR)KeyboardGroupObject,
             End,
         End),
     End;
 
     if (app == NULL)


/*
    Copyright © 2002, The AROS Development Team.
    All rights reserved.
 
    $Id: dtpic.c 30792 2009-03-07 22:40:04Z neil $
*/
 
#include <dos/dos.h>
 
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/muimaster.h>
 
#include <libraries/mui.h>
 
struct Library *MUIMasterBase;
 
Object *app;
 
int main(void)
{
    Object *wnd;
 
    MUIMasterBase = (struct Library*)OpenLibrary("muimaster.library",0);
 
    app = ApplicationObject,
   	SubWindow, wnd = WindowObject,
    	    MUIA_Window_Title, "dtpic test",
	    MUIA_Window_Activate, TRUE,
 
    	    WindowContents, VGroup,
    	    	Child, MUI_NewObject("Dtpic.mui",MUIA_Dtpic_Name,"SYS:System/Images/AROS.png",TAG_DONE),
		End,
	    End,
	End;
 
    if (app)
    {
	ULONG sigs = 0;
 
	DoMethod
        (
            wnd, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, (IPTR) app, 
            2, MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit
        );
 
	set(wnd,MUIA_Window_Open,TRUE);
 
	while (DoMethod(app, MUIM_Application_NewInput, (IPTR) &sigs) != MUIV_Application_ReturnID_Quit)
	{
	    if (sigs)
	    {
		sigs = Wait(sigs | SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D);
		if (sigs & SIGBREAKF_CTRL_C) break;
		if (sigs & SIGBREAKF_CTRL_D) break;
	    }
	}
 
	MUI_Object(app);
    }
 
    CloseLibrary(MUIMasterBase);
 
    return 0;
}


You must OM_ADDMEMBER a window to an application before opening it and you must OM_ADDMEMBER an area object to a group object (in a MUIM_Group_InitChange/ExitCHange pair) if you want to show it in a window (be part of your GUI). Mui Dev docs have a section on Dynamic Windows (Dynamic Object Linking/Windows) which are detailed documentation for dynamic window creation.


But you can create a custom class and get notified just by looking for incoming methods. Overloading MUIM_Setup is probably the best idea. You can subclass your window and put screen change detection in MUIM_Setup. E.g. in every MUIM_Setup you can remember screen pointer somewhere in object data, compare it with the previous value and do some things if screen has changed. use _app(obj) after MUIM_Setup.


You could have subclass of MUIC_Group where the all objects are placed... When you detect in MUIM_Setup that _screen() changed, you set MUIA_Window_CloseRequest to TRUE... If you app runs on WB screen user can change WB screen resolution/depth at any time. User can change your screen from the custom screen database at any time. Other thing to consider is iconify from the Exchange or ARexx. You _must_ react for screen changes.


Then you should place MUIM_Application_Save after your main loop.


Overloading MUIM_Window_Snapshot with some code which saves the prefs would do a save whenever the user selects the Snapshot icon on the window border, it would not do that only at program end. If you really want to save on Snapshot, you have to create a new class with CreateCustomClass() which must be a subclass of MUIC_Window. In the dispatcher of this sub class you have to call your own code whenever the dispatcher receives the MUIM_Window_Snapshot method. Then you have to change the application creation code: You have to replace every window creation with creation of an object of your window sub class (Intuition.NewObject()).


There seems to be a general problem when disposing windows: You must *not* reference the application object in a windows dispose method! Why? Because before disposing a window, you unlink it from the application (OM_REMMEMBER). After that, the window does no longer belong to an application and using _app(obj) is illegal. You don't have _app(obj) available during OM_NEW, the same is true for OM_DISPOSE.

Using _app(obj) anywhere in setup/cleanup methods (either before or after the DoSuperMethod) is fine. In fact, there is another layer between new/dispose and setup/cleanup where an object learns about its application context. However, this layer has not been made publically available through methods yet.

MUIA_Window_IsSubWindow does exactly this (skipping OM_REMMEMBER and OM_DISPOSE). However, this is not the solution to all problems which arise from the "global" window list in the application object. Window class should probably be able to have more window children itself.


How do we get the actual Window from my MUI-object ???

struct Window *win;
DoMethod(obj,MUIA_Window,&win);

MUIA_Window is an attribute. You can read attributes by calling GetAttr() from Intuition:

GetAttr(MUIA_Window, obj, &x);

And few warnings:

  • MUIA_Window is only valid when the window is opened
  • MUIA_Window is an area class tag. Use MUIA_Window_Window for window objects.


To call ChangeWindowBox on MUI windows. Just take care that the window is currently open (MUIA_Window_Window!=NULL) need the screen, you can always get MUIA_Window_Window and use window->WScreen etc. like with other intuition windows.



MUIM_Application_PushMethod

So you want to get out of the object context first, and this is easily done with MUIM_Application_PushMethod.

/* From your mail window */
DoMethod(_app(obj), MUIM_Application_PushMethod, _app(obj), 2,
MUIM_MyApplication_RemoveObject, obj);



/* From your application subclass */
case MUIM_MyApplication_RemoveObject:
{
DoMethod(obj, OM_REMMEMBER, msg->Obj);
MUI_DisposeObject(msg->Obj);
}
break;


Basically have a rendering task that does a heap of computation and renders into an offscreen rastport. When it's finished a frame, it sets a flag (Drawn) to FALSE, releases a semaphore, and does a PushMethod.

The main program, waiting patiently in its event loop, at some point will be awakened by the PushMethod, which triggers the custom class to do a MUI_Redraw() internally, which in turn obtains the semaphore, copies the offscreen bitmap to the window, sets Drawn to TRUE, and releases the semaphore. Then it goes back to sleep.

The rendering subtask, in the meantime, has done an ObtainSemaphore. When it gets it, it checks to see if Drawn is TRUE. If so, it can continue because it means the main task successfully copied the bitmap. Otherwise it releases it, does a little Delay, and tries again, to try and give the main task time to snatch the semaphore and do its work.

So, the semaphore is, in effect, protecting the offscreen rastport (and the Drawn flag) - neither task can access or change it without obtaining the semaphore first, and the flag is used to tell the draw task whether the main task has had a chance to do it's job or not. Most of the time (ie. almost always) the main task does, in fact, manage to grab the semaphore immediately so the draw task doesn't manage to steal it back again too quickly. If the main task finishes quickly, then when the draw task tries to obtain the semaphore it succeeds immediately, so there's no penalty.

PushMethod should not be too heavily overloaded, however, for inter-process communication recommends using private messageports for more intensive stuff. Use it as a signalling mechanism to wake up the main task and get it to send a MUI_Redraw to the custom class, and only ever send one at once (although there are 30-60 of them per second). Never had any problems with it in those circumstances.

struct SignalSemaphore lock_AddEntry_Sem;
...
InitSemaphore(&lock_AddEntry_Sem);
...
while(something){
...
   ObtainSemaphore(lock_AddEntry_Sem);     // Grab the semaphore
   while (lock_AddEntry){                  // Main task completed its job?
      ReleaseSemaphore(lock_AddEntry_Sem); // No, release semaphore
      Delay(1);                            // Wait a bit
      ObtainSemaphore(lock_AddEntry_Sem);  // Try again
   }
...
   lock_AddEntry = TRUE;                   // Set flag
   ReleaseSemaphore(lock_AddEntry_Sem);    // Give CPU back to main task
...
}

MsgPorts are slower. Semaphores are better to use under any AmigaOS like.



MUIM_HandleEvent is called for each event handler node unless MUI_EventHandlerRC_Eat is returned. MUIM_HandleInput is called for an active object if it was not eaten by MUIM_HandleEvent.

Should use EventHandler and not request ? To avoid getting events even if my object is not the active one ? Yes - eventhandlers respect properties such as the active and default object, priorites etc. HotkeyString is a subclass of BetterString. It adds a new eventhandler with a higher priority, thus get the events before its superclass, which wouldn't be the case for the other approach (one could override MUIM_HandleInput, but this was just *one* example...)

Eventhandlers also allow you to swallow input, another thing which isn't possible with IDCMP-requesting. This means many objects (which use the old way) will only interpret input when they are the active or default object, which is bad e.g. in IProbe where I want HTMLview to react on arrow up/down, even when the URL-string is active - and this is AFAIK only possible with eventhandlers.

the proper way to know the current active window (IDCMP_ACTIVEWINDOW/IDCMP_INACTIVEWINDOW was what I tracked before). What is the proper MUI way to know a window has become active? You can attempt a setup dispatcher that add the required actions to the event handler.

struct your_data
{
struct MUI_EventHandlerNode ehnode;
};

ULONG yoursetup(struct IClass *cl, Object *obj, Msg msg)
{
struct your_data *data = (struct your_data *)INST_DATA(cl, obj);

if (!DoSuperMethodA(cl, obj, msg))
return(FALSE);

data->ehnode.ehn_Object = obj;
data->ehnode.ehn_Class = cl;
data->ehnode.ehn_Events = IDCMP_ACTIVEWINDOW | IDCMP_INACTIVEWINDOW;

DoMethod(_win(obj), MUIM_Window_AddEventHandler, &data->ehnode);

return(TRUE);
}

add a cleanup dispatcher that looks like :

ULONG yourcleanup(struct IClass *cl, Object *obj, struct MUIP_HandleInput *msg)
{
struct your_data *data = (struct your_data *)INST_DATA(cl, obj);

DoMethod(_win(obj), MUIM_Window_RemEventHandler, &data->ehnode);
return(DoSuperMethodA(cl,obj,msg));
}

Add a dispatcher for MUIM_HandleEvent and take care of your wanted event like :

ULONG yourhandler(struct IClass *cl, Object *obj, struct MUIP_HandleInput *msg)
{
struct your_data *data = (struct your_data *)INST_DATA(cl, obj);

if (msg->imsg)
{
case IDCMP_ACTIVEWINDOW:
// Do what you want here
break;

case IDCMP_INACTIVEWINDOW:
// Do what you want...
break;
}

return(DoSuperMethodA(cl, obj, (Msg)msg));

}

or You could try to install an event listener that triggers every time MUIA_Window_Activate gets changed.


Be able to receive mouse co-ordinates and keypresses into my application, even when the window is not active. You can poll intuition base and read mouse coords at any time. An interrupt routine would be the best solution if your app wants keep an eye on it all the time... (might be possible with other libraries)


Everything is single threaded unless you specifically use MUIM_Application_InputBuffered. No functions are interrupted in order to handle a signal. Would a better method be to offload the MUI interface handling onto a separate task, and communicate with the network task through signals? This would at least solve the problem mentioned above. STRICQ uses MUI and is setup to handle any number of simultaneous sockets, both TCP and UDP. First create another window, then call MUIM_Application_InputBuffered (even though you should really try to avoid this) and then do the PushMethod. What should I do if the InputBuffered had to be used to handle other actions? Come up with a design pattern that avoids this nasty input probing in the middle of your program Creating a subclass of application class for this purpose where you could call one method with the object to be deleted as an argument and the application sub-class then stored a pointer to this object and deleted it after MUIM_Application_Input returned (and a nested counter tells me that there is no other recursive call to MUIM_Application_Method). Remember that the MUIM_PushMethod is not safe in the sense that it can fail (e.g. out of memory) and then your object doesn't get deleted (i.e. memory leak). The solution using a sub class of application class is safe as there is no need to allocate memory for the pointer to the object by adding it to an 'internal' Group class.



Iconify[edit]

#ifdef __amigaos4__
    MUIA_Application_DiskObject , diskobject = GetDiskObject(_ProgramName),
    #endif

MUIA_Application_DiskObject,GetDiskObject("PROGDIR:mp3player"),

where PROGDIR:mp3player is the name of your app. Be aware, that you are leaking the disk object this way. Be sure to assign it to pointer and FreeDiskObject after your application has ended..

struct DiskObject *my_app_icon;
MUIA_Application_DiskObject,my_app_icon=GetDiskObject("PROGDIR:zamp"), 

ULONG iconified=0;
get(application, MUIA_Application_Iconified, &iconified);
if( !iconified)



How to execute an arexx script from your own program as though it was run from your program's arexx port. An application subclass that adds one method called MUIM_Application_RunScript, just call this method with the scriptname and its arguments then it will be launched with your (MUI) port as host and it will make sure your program doesn't quit before the script has completed.


ULONG ListWindows( Object *application )
{
ULONG windows = 0;
struct MinList *windowlist;
windowlist = (struct MinList *) xget( application,
MUIA_Application_WindowList );
if( windowlist && !IsListEmpty( (struct List *) windowlist ) )
{
Object *object, *objectstate = (Object *) windowlist->mlh_Head;
while( object = NextObject( &objectstate ) )
{
Printf( "Object 0x%08lx is %swindow.\n",
object,
IsWindow( object ) ? "" : "not " );
windows++;
}
}
return( windows );
}

You can check whether object class is class or subclass of MUIC_Window, using similar code to below:

BOOL IsWindow( Object *object )
{
struct IClass *windowclass = MUI_GetClass( MUIC_Window );
struct IClass *class;
for( class = OCLASS( object ); class; class = class->cl_Super )
if( class == windowclass )
return( TRUE );
return( FALSE );
}



External input[edit]

 MUIM_Application_AddInputHandler(struct( MUI_InputHandlerNode ) in the MUIM_Setup and MUIM_Cleanup methods of your class (not subclass?).

Remove with MUIM_Application_RemInputHandler



Obsolete Hooks Is it safe to call the MUI_DisposeObject() inside a hook which is called by a notification if it frees the object the notification is attached to? This is not safe. The hook contents are not freed because after returning from the hook, there is still some return code from the calling class which has to be executed.


MUIM_Application_AboutMUI 
MUIM_Application_AddInputHandler 
MUIM_Application_CheckRefresh 
MUIM_Application_InputBuffered 
MUIM_Application_Load 
MUIM_Application_NewInput (replaces old Input) 
MUIM_Application_OpenConfigWindow 
MUIM_Application_PushMethod 
MUIM_Application_RemInputHandler 
MUIM_Application_ReturnID 
MUIM_Application_Save 
MUIM_Application_SetConfigItem  
MUIM_Application_ShowHelp        
MUIA_Application_Active 
MUIA_Application_Author 
MUIA_Application_Base 
MUIA_Application_Broker 
MUIA_Application_BrokerHook 
MUIA_Application_BrokerPort 
MUIA_Application_BrokerPri 
MUIA_Application_Commands 
MUIA_Application_Copyright 
MUIA_Application_Description 
MUIA_Application_DiskObject 
MUIA_Application_DoubleStart 
MUIA_Application_DropObject 
MUIA_Application_ForceQuit 
MUIA_Application_HelpFile 
MUIA_Application_Iconified 
MUIA_Application_MenuAction 
MUIA_Application_MenuHelp 
MUIA_Application_Menustrip 
MUIA_Application_RexxHook 
MUIA_Application_RexxMsg 
MUIA_Application_RexxString 
MUIA_Application_SingleTask 
MUIA_Application_Sleep 
MUIA_Application_Title 
MUIA_Application_UseCommodities 
MUIA_Application_UsedClasses 
MUIA_Application_UseRexx 
MUIA_Application_Version 
MUIA_Application_Window 
MUIA_Application_WindowList 



MUIM_Window_AddEventHandler 
MUIM_Window_Cleanup 
MUIM_Window_RemEventHandler 
MUIM_Window_ScreenToBack 
MUIM_Window_ScreenToFront   
MUIM_Window_Setup 
MUIM_Window_Snapshot 
MUIM_Window_ToBack 
MUIM_Window_ToFront 
MUIA_Window_Activate 
MUIA_Window_ActiveObject 
MUIA_Window_AltHeight 
MUIA_Window_AltLeftEdge 
MUIA_Window_AltTopEdge 
MUIA_Window_AltWidth 
MUIA_Window_AppWindow 
MUIA_Window_Backdrop 
MUIA_Window_Borderless 
MUIA_Window_CloseGadget 
MUIA_Window_CloseRequest 
MUIA_Window_DefaultObject 
MUIA_Window_DepthGadget 
MUIA_Window_DisableKeys 
MUIA_Window_DragBar 
MUIA_Window_FancyDrawing 
MUIA_Window_Height 
MUIA_Window_ID 
MUIA_Window_InputEvent 
MUIA_Window_IsSubWindow 
MUIA_Window_LeftEdge 
MUIA_Window_MenuAction 
MUIA_Window_Menustrip 
MUIA_Window_MouseObject 
MUIA_Window_NeedsMouseObject 
MUIA_Window_NoMenus 
MUIA_Window_Open 
MUIA_Window_PublicScreen 
MUIA_Window_RefWindow 
MUIA_Window_RootObject 
MUIA_Window_Screen 
MUIA_Window_ScreenTitle 
MUIA_Window_SizeGadget 
MUIA_Window_SizeRight 
MUIA_Window_Sleep 
MUIA_Window_Title 
MUIA_Window_TopEdge 
MUIA_Window_UseBottomBorderScroller 
MUIA_Window_UseLeftBorderScroller 
MUIA_Window_UseRightBorderScroller 
MUIA_Window_Width 
MUIA_Window_Window 

Objects[edit]

DoMethod( object1, method, attribute, value, object2, parameter_number, method2, attribute2, value2 [,...])


  • MUIM_Application_NewInput
  • MUIM_Window_
  • MUIM_CallHook MUIM_FindUData MUIM_GetUData MUIM_SetUData
  • MUIM_KillNotify MUIM_NoNotifySet MUIM_Notify
  • MUIM_Set MUIM_MultiSet MUIM_SetAsString
  • MUIM_List_InsertSingle


When a Zune object is created and destroyed several methods are called:

OM_NEW
MUIM_Setup
MUIM_AskMinMax
[ window is opened here ]
MUIM_Show
MUIM_Draw
MUIM_Hide
[ window is closed here ]
MUIM_Cleanup
OM_DISPOSE

If something is only valid between setup and cleanup it means that the first place where you can use it is the setup method and you aren't allowed to use it after cleanup.


If you want to manage a set of invisible objects the class MUIC_Family may be of help.


Need to know size of object while program is between MUIM_GroupInitChange and MUIM_GroupExitChange. Simple asking about MUIA_Width a MUIA_Height returns 0. Anyone have any idea? Width/height is not valid until you call ExitChange again. But if you need to know object dimensions subclassing is probably the best way to do so. You can get object dimensions by using _width()/_height() macros.


The object can't tell the difference of whether OM_SET was invoked through SetAttr() or through MUIM_MultiSet. Some standard MUI classes do indeed modify the taglist passed to them. A good example is Group class; it sets certain tags to TAG_IGNORE, because they either cannot be forwarded to the children, or shouldn't be handled by its superclass (Area).


How do you add a selected image to an app to use a different image for normal and selected states ? Most people are using a Group with PageMode set, so that the group simply toggle the displayed page, when pressed, and each page has the desired image shown on it.



Notifications[edit]

Morphos Notify


Basically, MUI doesn't use BOOPSI's notification system. Notification happens transparently, the only thing you need to do is pass the method up to your superclass, and you should be doing that anyway -- you don't need to use the approach that non-MUI BOOPSI classes use.

You can put notification on every attribute of your custom class. That's assuming your class is derived from MUIC_Notify. Then MUI will automagically execute the notification whenever you set an attribute.

You can place a notification on any (public) attribute of any class (or its ancestors) using the MUIM_Notify method. These notifications can be placed at any time from anywhere.

All you have to do is only change attributes of your object using set(), and in your set method handler add

 return (DoSuperMethodA(cl, obj, msg)); 

at the end. This will pass on the OM_SET up the class hierarchy to Notify.mui, which will then perform the notifications. The other thing, of course, is to always use set() whenever you want to change a value, rather than just assigning it directly. Only within the OM_SET method should you assign it directly.

When some other class puts a notification on one of your attributes, and your attribute gets changed using set() (or OM_SET), then when it does the DoSuperMethod() call the OM_SET will eventually reach the root class of all MUI classes -- Notify.mui. This keeps a list of notifications to be performed.

    /* This is setting up a notification on the MUIA_Robot_FrameRate * attribute */ 
    DoMethod(MyObj,
            MUIM_Notify, MUIA_Robot_FrameRate, MUIV_EveryTime,
            FrameCntObj,
            4,
            MUIM_SetAsString, MUIA_Text_Contents, MUIX_R"%ld", MUIV_TriggerValue
    );

...

    /* This is setting the frame rate (actually done within the class
     * itself -- ie. I use set() rather than just changing the value
     * directly in case there are notifications on it. */
    set(obj, MUIA_Robot_FrameRate, data->framecnt - data->lastframecnt);

...

/* This is the relevant part of my custom class's OM_SET method
 * handler. */
static ULONG mSet(struct IClass * cl, Object * obj, Msg msg)
{
    struct MyData *data = INST_DATA(cl, obj);
    struct TagItem *tags, *tag;

    for (tags = ((struct opSet *) msg)->ops_AttrList; tag = NextTagItem(&tags);)
    {
        switch (tag->ti_Tag) {
...
          case MUIA_Robot_FrameRate:    data->framerate = (ULONG) tag->ti_Data;
                                        break;
...
        }
    }

    return(DoSuperMethodA(cl, obj, msg));
}

If you have an attribute MUIA_Xxx_Yyy that is readable and notifiable, and recognized by mGet() (your OM_GET method). Internally it reads from data->Yyy. Further, let's say you have a private attribute MUIA_Xxx_YyyP (probably best to give it a numeric value quite different to the rest in your class) that is recognized by mSet() and writes to data->Yyy. mSet(), however, does *not* recognize MUIA_Xxx_Yyy. The code in the mSet() switch statement that recognizes MUIA_Xxx_YyyP, in addition to writing to data->Yyy, changes tag->ti_Tag to MUIA_Xxx_Yyy. Then, when the DoSuperMethodA() call is made, Notify class recognizes the attribute that was changed as MUIA_Xxx_Yyy and correctly performs the notification.

Suppose your class use the custom attribute MUIA_HHH_MyAttr. Other objects can ask MUI to be notified whenever this attribute is set()ted to a given value,for example you can have something like this:

DoMethod(obj1,MUIM_Notify,MUIA_HHH_MyAttr,32,obj2,1,MUIM_MyObj_Changed);

Here object obj2 asks to be notified with method MUIM_MyObj_Changed whenever the attribute MUIA_HHH_MyAttr of obj1 is set()ted to 32.


Child, Label2("Allow duplicates:"), Child, ck6 = CheckMark(TRUE),

DoMethod(ck6, MUIM_Notify, MUIA_Selected, MUIV_EveryTime, app, 2, MUIM_Application_ReturnID, ACT_CK6);

The confusing thing about this bug is that it only affects CheckMark(TRUE) objects (or if you use MUIM_Application_Load to restore the state of checkmarks then those that become set to TRUE will exibit the same problem).

The autodocs state you 'simply' add your checkmarks and strings (paired together) even in 'invisible' space. You need the string because the autostring for a checkmark is only place-able at the left side (which would be unintuitive).

Object *MakeCheck(BYTE state, char *label)
{
  return ImageObject,
           ImageButtonFrame,
           MUIA_Text_Contents, label,
           MUIA_InputMode   , MUIV_InputMode_Toggle,
           MUIA_Image_Spec  , MUII_CheckMark,
           MUIA_Background  , MUII_ButtonBack,
           MUIA_ShowSelState, state,
           MUIA_CycleChain  , TRUE,
         End;
}



DoMethod(timerobj,MUIM_Notify,MA_Timer_Seconds,MUIV_EveryTime,obj,2,MM_Clock_Seconds,MUIV_TriggerValue);

This should call MM_Clock_Seconds everytime MA_Timer_Seconds changes.

WaitIO(data->req);
CurrentTime(&raw,&micros);
today = raw%86400;
SetAttrs(obj,MA_Timer_Seconds,today%60,TAG_DONE);

Notify does not happen

data->seconds = today%60+30;
data->minutes = (today%3600)/60;
data->hours = today/3600;
data->req->tr_node.io_Command = TR_ADDREQUEST;
data->req->tr_time.tv_secs = 1;
data->req->tr_time.tv_micro = 0;
SendIO((struct IORequest *)data->req); 

timerobj's OM_GET:

case MA_Timer_Seconds:
 *msg->opg_Storage = data->seconds;
return TRUE; 

Class has no OM_SET but you must implement OM_SET to notify. I would not use MUI notifications for this kind of events because due to BOOPSI latency. The timer does work (tested by getting MA_Timer_Seconds in every MUIM_Draw for the clock class, but MM_Clock_Seconds never gets called.

SetAttrs(NULL, ... In theory you do not have to because SetAttrsA() is only a wrapper call to DoMethodA() which accepts NULL object pointer but this is not documented behaviour either in SetAttrsA() nor DoMethodA(). So do not rely on it but use macro instead to save you from typing if statements.


Restore a button frame - D&D lately but it seems it clashes with MUI's own D&D drawing. You could look into Ambient D&D for ideas. It uses its own dropmarks.


MUI_DISPATCH(NewList_Dispatcher)
{
    switch (msg->MethodID)
    {
        case OM_NEW              : return(NewList_New  (cl,obj,(APTR)msg));
        case OM_SET              : return(NewList_Set  (cl,obj,(APTR)msg));
        case OM_GET              : return(NewList_Get  (cl,obj,(APTR)msg));
        
        case MUIM_DragQuery : return(NewList_DragQuery(cl,obj,(APTR)msg));
        case MUIM_DragDrop   : return(MyList_DragDrop (cl,obj,(APTR)msg));
        case MUIM_ExternDrop :return(MyList_ExternDrop(obj,(APTR)msg));
    }
    return(DoSuperMethodA(cl,obj,msg));
}


added an internal notification method to each checkmark (checkbox) and added the method to the dispatcher,

DISPATCHERPROTO(MCC_BIA_dispatcher)
{
struct mydata *mydata = INST_DATA(cl,obj);

switch (msg->MethodID)
{
case OM_NEW: return( MCC_BIA_New (cl, obj, (APTR) msg));
case OM_SET: return( MCC_BIA_Set (cl, obj, (APTR) msg));
case OM_GET: return( MCC_BIA_Get (cl, obj, (APTR) msg));
case OM_DISPOSE: return( MCC_BIA_Dispose (cl, obj, (APTR) msg));
case MUIM_AskMinMax: return( MCC_BIA_AskMinMax (cl, obj, (APTR) msg));
case MUIM_BIA_Changed: return( MCC_BIA_Changed (cl, obj, (APTR) msg));
default: return( DoSuperMethodA (cl, obj, msg)); 
}
}

Each time click on one of the checkboxes you get an MUIM_BIA_changed and the function get's called. Within the function readout the checkboxes and rebuild the bit array which can be read and set by using the MUIA_BIA_Array tag.

Now a notification for MUIA_BIA_Array is required. Do I need to call the class itself by using set(MUIA_BIA_Array) to cause a notification to take place, or is there a more simple way. Calling the class from inside would cause the checkboxes to change and this would result in another MUIM_BIA_changed and we have an endless loop.

The problem is that setting would cause an update of the class internal gadgets. These would cause a notify and the notify would cause another set().

//The best way to handle it is to filter out tag list..
struct TagItem *tagitem;
tagitem = FindTagItem( MUIA_xxx_name, taglist );
if( tagitem != NULL )
{
tagitem->ti_Tag = TAG_IGNORE;
}

do-notify set might still causing infinite loop in some circumstances that vary on super class attributes implementation, and therefore in one class version working fine, in second crashing system...


MUIA_ApplicationObject 
MUIA_AppMessage 
MUIA_HelpLine 
MUIA_HelpNode 
MUIA_NoNotify 
MUIA_ObjectID 
MUIA_Parent 
MUIA_Revision 
MUIA_UserData 
MUIA_Version 

MUIM_CallHook 
MUIM_Export 
MUIM_FindUData 
MUIM_GetConfigItem 
MUIM_GetUData 
MUIM_Import 
MUIM_KillNotify 
MUIM_KillNotifyObj 
MUIM_MultiSet 
MUIM_NoNotifySet 
MUIM_Notify 
MUIM_Set 
MUIM_SetAsString 
MUIM_SetUData 
MUIM_SetUDataOnce       
MUIM_WriteLong 
MUIM_WriteString 



Layouts[edit]

// Create your GUI 
 app = ApplicationObject,
 SubWindow, wnd = WindowObject,
 MUIA_Window_Title, (IPTR) "Title",
 MUIA_Window_CloseGadget, FALSE,

 WindowContents, (IPTR) VGroup,
  MUIA_Group_SameWidth, TRUE, 

  Child, (IPTR) HGroup,

   Child, (IPTR) VGroup,

    Child, TextObject,
    MUIA_Text_Contents, "33cText1",
    End,

    [...]

   End,

   Child, VGroup,

    Child, TextObject,
    MUIA_Text_Contents, "33cText2",
    End,

    [...]

   End,

   Child, VGroup,

    Child, TextObject,
    MUIA_Text_Contents, "33cText3",
    End,

    [...]

   End,

   Child, VGroup,

    Child, TextObject,
    MUIA_Text_Contents, "33cText4",
    End,

    [...]

   End,

   Child, VGroup,

    Child, TextObject,
    MUIA_Text_Contents, "33cText5",
    End,

    [...]

   End,

   Child, VGroup,

    Child, TextObject,
    MUIA_Text_Contents, "33cText6",
    End,

    [...]

   End,
  End,

  Child, HGroup,

   Child, Scan_button = SimpleButton("Button"),
   Child, Quit_button = SimpleButton("Quit"),
  End,
 End,
 End,
 End;



 Object *exampleWinObj, *exampleVirtGObj, *exampleContainerObj;

Object *exampleChildObj;

exampleWinObj = (Object *)WindowObject,
                    MUIA_Window_CloseGadget, FALSE,
                    MUIA_Window_Title, (IPTR)"Example Window",
                    WindowContents, (IPTR) (exampleVirtGObj= (Object *)GroupObject,
                        MUIA_Group_Virtual, TRUE,
                        Child, (exampleContainerObj = (Object *)VGroup,
                        End),
                    End),
                End;

if (exampleWinObj)
{
    int i;
    for (i = 0; i < 10; i++)
    {
        exampleChildObj = HVSpace;

        if (DoMethod(exampleContainerObj, MUIM_Group_InitChange))
        {
            DoMethod(exampleContainerObj, OM_ADDMEMBER, exampleChildObj);
            DoMethod(exampleContainerObj, MUIM_Group_ExitChange);
        }
    }
}

Change the HVSpace for whatever object (group, etc) you want to add..



Obsolete Hooks.

MUI does not allow own position of Components, only automatic positions available (group concept) but by setting and Layout Hook and add and Left, Top, Width and Height to every Object, you can disable this behaviour

Your notify should preferably invoke a method on the class responsible for the action you want to have executed. Tend to gather more general stuff in window or application subclasses. If there's really no place it belongs then you can have it call a hook instead.

You must add your own layout hook which reports the size. Then simply invoke MUIM_Group_Init/ExitChange on your virtgroup object, as that will cause a re-layout == invocation of your layout hook, which can report new sizes for the contents. If your family tree needs any layout (before rendering) then the layout hook is also the ideal place for this. Take a look at 2b_VRastPort sources on Aminet.

/* gcc syntax */
ULONG __attribute__((regparm(3))) Genealo_Layout(struct Hook *hook, struct
MUI_LayoutMsg *lmsg, Object *obj )
{
ULONG result = 0;
int longueur=500, haut=500;


switch(lmsg->lm_Type)
{
case MUILM_MINMAX:
{
printf("MinMax\n");
lmsg->lm_MinMax.MinWidth = 500;
lmsg->lm_MinMax.MinHeight = 500;
lmsg->lm_MinMax.MaxWidth = 500;
lmsg->lm_MinMax.MaxHeight = 500;
lmsg->lm_MinMax.DefWidth = 500;
lmsg->lm_MinMax.DefHeight = 500;
}
break;

case MUILM_LAYOUT:
{
printf("Layout...\n");

lmsg->lm_Layout.Width = longueur;
lmsg->lm_Layout.Height = haut;
result = TRUE;
}
break;
}
return result;
}


ULONG __attribute__((regparm(3))) Genealo_Dispatcher(struct IClass *cl ,
void *msg,Object *obj )
{
Msg msg2 = ( Msg ) msg;
struct Genealo_Data *data;
ULONG result;


switch (msg2->MethodID)
{
case OM_NEW:
{
result = DoSuperMethodA(cl,obj,msg);
if(obj = (Object *)(result))
{
data = (struct Genealo_Data *)INST_DATA(cl,obj);

data->LayoutHook.h_Entry = (ULONG(*) ())Genealo_Layout;
data->LayoutHook.h_Data = data;
SetAttrs(obj,MUIA_Group_LayoutHook, &data->LayoutHook,TAG_DONE);
}
printf("%p\n",result);
return( result );
}
break;
/* ..... */


How to do multithreading in MUI, where the same thread to be running several times simultaneously? Your application tree may only be accessed by one task, as no MUI object is re-entrant nor thread-safe (the classes are, not the objects). Using

DoMethod(app,MUIM_CallHook,&hook)

works fine for running different threads at the same time but when calling the same hook a second time, the first one pauses. It is because both threads are running on the same OS task. If you want to have them running simultaneously, you have to create a separate task for each thread, e.g. by CreateProc() from dos.library. That method is the same as CallHookA(&hook, app, NULL) which again is the same as just calling the hook-function directly, with 'app' and '&hook' as parameters. Not sure why you think this equals 'multi-threading'. If the second event then terminates, the first one continues, but it seems all the local variables have been altered by the second event. Because your code is not reentrant.



Finding[edit]

there is also a user field in IClass and you can access to this through MUI_CreateCustomClass() as in the Dispatcher.

mcc = MUI_CreateCust..()
mcc->mcc_Class->cl_UserData = data;

in the Dispatcher:

data = cl->cl_UserData;

Locating objects in a tree using MUIA_UserData and MUIM_FindUData

use MUIA_UserData, to store a pointer to a private struct where IT store information that object/related code might need ... e.g pointers to paths/filenames, whatever you like really.

the MUIA_UserData of the object contains the given udata\ and returns the object pointer in this case.




Area (dragging, RMB etc)[edit]

Let the program know if the user clicks anywhere on the map, the answer is yes: Group class is a child of area class, thus you can use any attribute of area class, for example MUIA_Pressed.

Let the program know, if and where the user clicked on the map, the answer is no. That's exactly what custom classes are for. If any drawing will be manipulated by the user (e.g. an area for painting with the mouse), you write a subclass of Area class. Bitmap is just for displaying rastered images. If you want to have a (resizeable) area for application specific drawings, sub-classing Area is still the right thing to do.

If you want to create a bitmap of them tiled, which would then be displayed inside the application. For this application it is certainly simpler to use a Column group with Bitmap objects as children, or even a group with a custom LayoutHook that adapts the number of columns to the size of the area.

// want the image to be the same size as the picture or if the object has to be bigger then the picture,
// it should be centered (horiz and vertically). 
TextAttr ta = { "topaz", 8, 0, 0 };
TextFont *Topaz8Font = OpenFont(&ta);

Object *guiobject = ImageObject,
MUIA_Image_Spec, "5:<path>",
MUIA_Image_FreeHoriz, FALSE,
MUIA_Image_FreeVert, FALSE,
MUIA_Image_FontMatch, TRUE,
MUIA_Font, Topaz8Font,
End;

the Image class is a subclass of Area class, and the Area class can handle input, thus it should be possible to create an Image object that behaves like an ordinary button. Simply try to create an Image object, init the attribute MUIA_Image_Spec as "5:picture_filename" (to load the image via datatypes) and try to init the Area class attribute MUIA_InputMode as MUIV_InputMode_RelVerify.

Then set up a notification for that object, so you will hear from it, open the window...

It might help to have a look at the "default macros" for images and buttons in mui.h to initialize the other attributes (frame type etc.) properly.


  1. Call MUIM_Group_InitChange on a parent group of your object.
  2. OM_REMMEMBER it from the group.
  3. Change the size ie you should modify the data used in MUIM_AskMinMax to fill the structure.
  4. OM_ADDMEMBER it back.
  5. Call MUIM_Group_ExitChange on the group.




MUIM_DRAW[edit]

If you want to code yourself what your class will display (eg the wireframe drawing with the mui class1 demoprogram), you implement this overriding the MUIM_Draw method. (have a look at the three examples of custom classes class1 (ok), class2 (ok), class3 (obsolete).

Is it possible to draw on objects outside of MUIM_Draw? MUIM_Draw adds some overhead compared to direct draw (like in gadtools) but overhead is very minimal and you can't notice any slow down either...

Is there a safe way to do draw into a window/MUI-object outside a Draw-event? You can draw only inside MUIM_Draw because:

  • your object may belong to virtual group and needs clipping (MUI installs this automatically for MUIM_Draw)
  • your application can be hidden (in the iconified state) at anytime and thus _rp(obj) is not always valid.
  • your object could be hidden for some reason (page and register groups)

If you just want draw new parts of your objects without full redraw? Then you could do something like this:

data->DrawCommand = MY_OWN_DRAW_COMMAND;
MUI_Redraw(obj, MADF_DRAWUPDATE);

And then in your MUIM_Draw method:

MyDraw()
{
DoSuperMethod();

if (msg->flags & MADF_DRAWUPDATE)
{
switch (data->DrawCommand)
{
/* UPDATE PARTS YOU WANT TO */
}
}
else
{
/* FULL REDRAW */
}
}


In MUIM_Draw simply call BltBitMapRastPort() to display stuff in screen:

BltBitMapRastPort(data->RastPort.BitMap, 0, 0, rp, mleft, mtop , mwidth, mheight, 0xc0);

create a bitmap, embed it in a rastport and set this rastport in your MUI renderinfo - then simply call MUIM_DrawBackground, and the background is drawn into your off-screen bitmap. Remember to set back the rastport

within your objects area you ae absolutely free to render whatever you want. And about the rastport: You *should* attach an own rastport to the object, if you use any graphics.library functions: Mui expects to find the objects own rastport in the same state after MUIM_Draw is executed.

If MUIM_Draw not being called (blank area with a frame) then check that you are not calling DoSuperMethodA() first in your MUIM_Draw method and that the MUIM_Draw Method only checks for msg->flags == MADF_DRAWOBJECT.

switch( msg->MethodID )
{
[...]
case MUIM_Draw: return( draw( class, object, msg ) );
[...]
}

solved the problem, replaced them in the draw method

if(!DoSuperMethodA(cl,obj,(APTR)msg)) 
    DoSuperMethodA(cl,obj,(APTR)msg);

Typically your object should have an internal buffer where you store the objects state after the last MUIM_Draw. This allows you to draw only those parts of your map, that need to be updated.


Stuff the parameters into you instance data and call MUI_Redraw() with MADF_DRAWUPDATE. That's what it is for. Basically, all rendering is done inside MUIM_Draw. If another method wants to render, it sets up parameters and calls MUI_Redraw(). MUI_Redraw() will then call MUIM_Draw for you.

However, you must be aware that MUIM_Draw might _not_ be called, e.g. when your object is currently hidden. So, MUIM_Draw should only draw; your other methods should do all the other stuff (if you draw an ellipse, you have to store that information somewhere in case a refresh comes in, the application is iconified etc. So, you MUIM_xxx_DrawEllipse will store all that information somewhere (adding the ellipse to a list or drawing it into an offscreen bitmap), then it will call MUI_Redraw(TheObject,MADF_DRAWUPDATE) to update the display).


What about a 3ds viewer with mui buttons ? Titler display opengl and mui on the same window, so what class/methods can use to display/redirect opengl output onto mui win? You need an area subclass to display your GL stuff in MUI window. You only need an offscreen bitmap which is blitted to the window in your MUIM_Draw method.

InitRastPort(&data->RastPort) is done in other part of code! Once opengl context is initialized you can render to your offscreen bitmap using GL calls.

Have a look to aminet/wazp3d in soft3d_opengl.c. Inside there is code to start mesa for aros (seek ifdef aros...) But aros mesa cant render in a bitmap: You will need to use your mui window + glscissor

AreaGL may be helpful.



MUIA_Image_FontMatch
MUIA_Image_FontMatchHeight
MUIA_Image_FontMatchWidth
MUIA_Image_FreeHoriz
MUIA_Image_FreeVert
MUIA_Image_OldImage
MUIA_Image_Spec
MUIA_Image_State



MUI_Redraw[edit]

With MUI_Redraw(), an object tells itself to refresh, e.g. when some internal attributes were changed. Calling MUI_Redraw() is only legal within a custom class dispatcher, "using this function within an applications main part is invalid!". To solve the same problem with my GUI for the UAE port of AROS, and simply wrapped the MUI_Redraw inside a custom method.

MUIM_UAEDisplay_Update, struct MUIP_UAEDisplay_Update *,
({

if (message->top >= data->height || message->bottom <= 0)
return TRUE;

if (message->top <= 0)
message->top = 0;

data->update_top = message->top;
data->update_bottom = message->bottom;

MUI_Redraw(self, MADF_DRAWUPDATE);

return TRUE;
})


case OM_SET:
{
struct TagItem *tagitem, *tagstate = msg->ops_AttrList;
while( tagitem = NextTagItem( &tagstate ) )
{
case MUIA_[classname]_Update:
{
MUI_Redraw( obj, MADF_DRAWUPDATE);
break;
}
}
return( DoSuperMethodA( cl, obj, msg ) );
}

But better might be making just attribute to passing array of vectors.. When it's passed redraw is called.. or custom method:

case MUIM_[classname]_Update:
{
MUI_Redraw( obj, MADF_DRAWUPDATE);
return( 0 );
}

You must not completely intercept OM_SET. Program sub-class some MUI class like Area.mui or Group.mui and they have their own attributes that might be set during program running.. Intercepting them or redrawing window might cause illegal actions..




Groups[edit]

The group class was designed to contain other groups and gadgets not to be a gadget by self. It's a side effect because it's a subclass of area class (making it a subclass of notify class and copying part of methods would be senseless). Well... if you want create an image button (button with text and gfx), apart from the group class it could be done by creating subclass of MUIC_Area which in MUIM_Setup load necessary picture(s) using datatypes.library, draw it in MUIM_Draw and dispose in MUIM_Cleanup. Btw. the group class isn't a subclass of area class by mistake.

Only group objects can have children. To get children there's MUIA_Group_ChildList. Walking through list is done with NextObject(). Example:

	Object *obj1;
	struct List *children	= (struct List *)XGET(_maingrp, MUIA_Group_ChildList);
	APTR pObj               = GetHead(children);
 
	// Parse the children
	while(NULL != (obj1 = NextObject((Object **)&pObj)))
	{
		printf("ID is: %ld\n", XGET(obj1,MUIA_ObjectID));
	}

To get parent there's MUIA_Parent.

If you don't know number of children or it's variable you would have to alloc some memory for the method struct ( sizeof(struct MUIP_Group_Sort) + sizeof(Object *) * num_children) and then call it with DoMethodA(obj, alloc'd_method). Then free the method struct again.

num_children = number of successful NextObject() calls on child list.

There's MUIM_Group_Sort method allowing you to tell the group object the exact order of the childs. NListview.mcc uses this and the group_initchange/group_exitchange stuff to dynamically show/hide scrollbars.

Almost all attributes you set on a group will be forwarded to its children. You can set MUIA_Group_Forward to FALSE to prevent this (while setting the tag in question).

a MUIM_Group_Insert method for inserting a child into a group in specified place, just like MUIM_Family_Insert. Currently move the child list from the group to my private family, insert the new child into my family with MUIM_Family_Insert and move all children from the family to the group again. on groups just like MUIM_Family_Sort on families.


#define MUIM_Group_Sort 0x80427417 /* V4 */
DoMethod(parent, MUIM_Group_InitChange);
    DoMethod(parent, REMMEMBER, obj);
    set(obj, MUIA_FixWidth, 100);  // just some changing beyond the object borders
    DoMethod(parent, ADDMEMBER, obj);
DoMethod(parent, MUIM_Group_ExitChange);

InitChange/ExitChange during setup/cleanup is not a good idea. You should add your childs in MUIM_Setup *before* the DoSuperMethod() and remove your childs in MUIM_Cleanup *after* the DoSuperMethod().

should not disable groups anyway just to disable its children. Better disable each child for itself, since this will indeed look nicer. You can do this with one DoMethod, e.g. MUIM_MultiSet or MUIM_SetUData


Group is intended for objects having visual representation. For a text editor, if you group Buffer and Display objects it will be a bit tricky since Buffer isn't really a gadget. Editor should be a subclass of Display, and Buffer should be attached via pointer in object data structure. It better shows relations between Buffer and Display, they are not just a gadgets in a group.


Basically you have to call MUIM_InitChange before removing or adding objects and then call MUIM_ExitChange:

DoMethod(yourgroup,MUIM_InitChange)
DoMethod(OM_ADDMEMBER,newobject)
DoMethod(yourgroup,MUIM_ExitChange)


to trigger a relayout by doing a MUIM_Group̠InitChange and MUIM_Group_Exitchange on the _parent(obj)


Every object should be responsible for passing the new settings on to its children. Like when you have a subclass of group that contains a numeric and a string gadget, it will have some attribute that holds a structure of the gadgets' respective values, and the group object has to know which value goes to which of its children and set them accordingly when itself it receives an OM_SET. Then you'd ideally do a set(mywindow,PREFS_ATTRIBUTE,&some_structure) and the object tree would handle the rest by itself. Then there's no other way than to remember each object that needs to be updated in some structure and set() it "by hand".



virtgroup.mui are internal part of MUI (they're "inside" muimaster.library).

The virtual group it's like a container that can scroll if the content exceeds its boundaries. A Virtual group makes sense inside a ScrollGroup.


you should not use MUIA_Pressed for notifying toggle gadgets (only for RelVerify ones like buttons). Use MUIA_Selected.

window
---> group (<--- set MaxWidth/MaxHeight here, works)
------> scrollgroup
---------> virtgroup
------------> text
------> rectangle (<--- or here, works, too)


How to get a virtgroup which is only in the horiz direction virtual w/o using scrollgroup (use MUIA_Scrollgroup_FreeVert, FALSE) or is there a way to have the scrollbar hidden all the time?

You need a custom class which uses fixed height or width thus locking virtgroup vertically or horizontally.



Adding objects to a group on the fly. First you have to init the changes you want to do, then perform them and then declare you have finished. Example of code to add or remove a group from another group:

        Object *_maingrp = .... // the container
        Object *_subgrp = .... // the content
 
	// add the subgroup
	if(DoMethod(_maingrp, MUIM_Group_InitChange))
	{
           DoMethod(_maingrp, OM_ADDMEMBER, _subgrp);
           DoMethod(_maingrp, MUIM_Group_ExitChange);
	}
 
	// remove the subgroup
	if(DoMethod(_maingrp, MUIM_Group_InitChange))
	{
           DoMethod(_maingrp, OM_REMMEMBER, _subgrp);
           DoMethod(_maingrp, MUIM_Group_ExitChange);
	}
LONG sigs;
DoMethod(app,MUIM_Application_NewInput,&sigs);

This seems to be a general pitfall for MUI programmers, but 'sigs' really has to be initialized to zero! It's worse: sigs is the result of Wait(), i.e. the signals received by the process. So if you give a random value, MUI will think random signals were received, and act on these. Normally the effect of this is not noticeable, as a message-port signal will just cause a "while(GetMsg(port))", which will immediately terminate, but in theory serious problems could arise


Want to reproduce the Workbench way of dealing with icons. Want to be able to move images from a point to another. Derive a Group (virtual or otherwise) with it's own custom layout hook and make the objects of that group Draggable; when you get a drop event, re-layout the group (force this using MUIM_Group_InitChange = followed by ExitChange)


TableGroup


MUIM_Group_ExitChange
MUIM_Group_InitChange
MUIM_Group_Sort

MUIA_Group_ActivePage
MUIA_Group_Child
MUIA_Group_ChildList
MUIA_Group_Columns
MUIA_Group_Horiz
MUIA_Group_HorizSpacing
MUIA_Group_LayoutHook
MUIA_Group_PageMode
MUIA_Group_Rows
MUIA_Group_SameHeight
MUIA_Group_SameSize
MUIA_Group_SameWidth
MUIA_Group_Spacing
MUIA_Group_VertSpacing
RMB and LMB[edit]

RMB menus to gadgets. Context menus are a feature of the Area class, and can be set with MUIA_ContextMenu, or dynamically created by overloading MUIM_ContextMenuBuild. Is it possible to use rmb as lmb in MUI? Yes, you should override MUIM_ContextMenuBuild for your object. Create fake Menu object (just with one menu and no items), give it to MUIA_ContextMenu attribute and do your job in MUIM_ContextMenuBuild, returning NULL from there (it means no context menu will be drawn). Well, its not easy, because there is no simple method to get informed about RMB _release_. You have to sub-class Text class and write own MUIM_HandleInput, which is mentioned in docs as obsolete. But unfortunately MUI event handlers don't react to RMB (bug in MUI?). can look at the MUIMine source (on Aminet). It does something like that and mentions problems with the RMB. It's also using MUIM_HandleInput.




Dragging[edit]
ULONG MyList_DragQuery(struct IClass *cl,Object *obj,struct MUIP_DragDrop *msg)
{
    if (msg->obj==obj)
    {   //return(MUIV_DragQuery_Accept);
        return(DoSuperMethodA(cl,obj,(Msg)msg));
    }
    else
    {
        if (msg->obj==(Object *)muiUserData(obj))
            return(MUIV_DragQuery_Accept);

        else
            return(MUIV_DragQuery_Refuse);
    }

}

Whenever there are no conflicting inputs, MUI starts the drag operation immediately. If the LMB is needed for anything else (ie on a button), MUI starts dragging like specified in the prefs. set MUI_Draggable, TRUE to an object of a MUI_Area subclass

get(obj,MUIA_List_DropMark,&dropmark);
DoMethod(obj,MUIM_List_InsertSingle,entry,dropmark);

Note that you must get the dropmark from the destination object, *not* from the source.


One mode allows you to enter the data in the string gadget. The other allows dragging. In the dragging mode you make the StringObject a TextObject. That way you have no problems dragging the objects around. You could also try making a Custom string class.

How to get MUI to report the dropped position relative to the object that is accepting the drop? Also is there some way to get mui to report based on the top, left of the object that was grabbed and not report from where you grabbed the object at? If the position is screen based, just substract from it _mleft(obj) and _window(obj)->LeftEdge, _mtop(obj and _window(obj)->TopEdge.


Is there a way to react to a dragging of a workbench icon into an appwindow created with MUI. The desktop manager sends message to an application only when user stopped dragging so it is not possible.

MUI has its way to handle d&d with MUIM_DragBegin/DragFinish for its MUI object.... MUIM_DragBegin/Finish are for internal MUI D&D. AppWindow messages are externally generated and MUI can not control it.

This is typically a method that you usually won't call yourself (like the drag'n'drop methods of area.mui)



MUIM_AskMinMax
MUIM_Cleanup
MUIM_ContextMenuBuild
MUIM_ContextMenuChoice
MUIM_CreateBubble
MUIM_CreateShortHelp
MUIM_DeleteBubble
MUIM_DeleteShortHelp
MUIM_DragBegin
MUIM_DragDrop
MUIM_DragFinish
MUIM_DragQuery
MUIM_DragReport
MUIM_Draw
MUIM_DrawBackground
MUIM_HandleEvent
MUIM_HandleInput
MUIM_Hide
MUIM_Setup
MUIM_Show

MUIA_Background
MUIA_BottomEdge
MUIA_ContextMenu
MUIA_ContextMenuTrigger
MUIA_ControlChar
MUIA_CycleChain
MUIA_Disabled
MUIA_Draggable
MUIA_Dropable
MUIA_ExportID
MUIA_FillArea
MUIA_FixHeight
MUIA_FixHeightTxt
MUIA_FixWidth
MUIA_FixWidthTxt
MUIA_Font
MUIA_Frame
MUIA_FramePhantomHoriz
MUIA_FrameTitle
MUIA_Height
MUIA_HorizDisappear
MUIA_HorizWeight
MUIA_InnerBottom
MUIA_InnerLeft
MUIA_InnerRight
MUIA_InnerTop
MUIA_InputMode
MUIA_LeftEdge
MUIA_MaxHeight
MUIA_MaxWidth
MUIA_Pressed
MUIA_RightEdge
MUIA_Selected
MUIA_ShortHelp
MUIA_ShowMe
MUIA_ShowSelState
MUIA_Timer
MUIA_TopEdge
MUIA_VertDisappear
MUIA_VertWeight
MUIA_Weight
MUIA_Width
MUIA_Window
MUIA_WindowObject




register (TABS)[edit]

how can a child (a tab) of a register (Tab) know that he is currently visible w/o knowning in which tab it is? Have a .mcc which is a subclass of group and creates a row of cycletitle-objects (like cycle gadget, but with a fixed title string within the cyclegadget) where each button represents a window. The group gets notified (by notifyintuition.library) when a new window is opened or one win gets closed. or create a subclass of group which has a new attribute of something like MUIA_XGroup_PageIndex. You could then compare this to the "main groups" objects active page. You should know the "main group" object as it gets this from reading MUIA_Parent of the "tab page".

if tabpage.pageindex = tabpage.parent.activepage then (simple pseudo code) 

Or create a subclass of register which as well as MUIA_Group_ActivePage has say MUIA_Group_ActivePageObj which is set on a change of MUA_Group_ActivePage. You could then compare this to the "tab page".

if tabpage = tabpage.parent.activepageobj then (simple pseudo code) 

Or create a new method of register that returns the current obj (similar to your above idea) but as a method it will be usable for all your register groups.


MUIA_Register_Frame
MUIA_Register_Titles



Lists[edit]

good examples of lists
  • The list class is a class which can hold and manipulate (to some extend) a linked list of data.
  • The listview class is a group subclass which handles the layout and interconnection of a list and the required sliders.

Listtree which uses images for the branches, but afaik doesn't allow you to change the image.


at startup the list is empty and add entries with MUIM_List_InsertSingle.

MUIA_List_SourceArray to assign an strarray then use the MUIA_List_Active result as the index for that array.

MUIM_List_Remove


listview to have the width of the text inside and no more and no less.

MUIA_List_Format - MAXWIDTH/MINWIDTH
MUIA_List_AdjustWidth, TRUE

to insert strings in a ListView:

DoMethod(projectfiles,MUIM_List_InsertSingle,(long unsigned int)TXT.sfilename.c_str(),(IPTR)MUIV_List_Insert_Bottom);



COPYING you will need to define Construct & Destruct hooks as List object does NOT copy your string (just takes a pointer) until you order otherwise. If you got simple one column list and you will be satisfied storing just strings in it, MUI offers builtin construct hooks for this. Refer autodocs for construct & destructs (must set both!).

MUIA_List_ConstructHook, MUIV_List_ConstructHook_String
MUIA_List_DestructHook, MUIV_List_DestructHook_String



MUIV_List_Remove_Selected


You can call MUIM_List_CreateImage as soon as the list class completed its setup method. You must call MUIM_List_DeleteImage before the list completes its cleanup method.


MUIM_Insert_xyz inserts an Object to a List, this meant that the List class can handle any Object and so you can not add only Strings to it. only a Pointer is stored and not the content of it. Note the List is not responsible for Displaying its' content, this was done by the Listview and by default it´s Displayhook expects pointers to a Text in the list.


how to add a small image next to a string in the list? game/think/MUIMastermind.lha might have a simple approach without CreateImage() and so on.


Reading MUIA_List_Active gives me the entry number, but if the order's changed, it doesn't mean anything? GetEntry returns pointer to your entry and you can access any column you wish. It's your private pointer anyway.


If you want a page background for an app, simply use MUIA_Background, MUII_PageBack for your group object.



Obsolete Hooks. If hooks are being used, multicolumn List object to display the result but want to add title to columns

HOOKPROTO(ResultList_Display, ULONG, char **array, struct ReqData *reqdata)
{
if (reqdata) {

int n;
for (n=0; n<reqdata->nb ; n++) {
*array++ = reqdata->columns[n];
}
} else {
reqdata = (struct ReqData *)hook->h_Data; // list titles

int n;

for (n=0; n<reqdata->nb ; n++) {
*array++ = reqdata->columns[n];
}
}

return 0;
}
MakeStaticHook(hook_ResultList_Display, ResultList_Display);

Somewhere in your code you set h_Data:

hook_ResultList_Display.h_Data = (APTR)reqdata_titles;

You can either build a hook object that h_entry part is your function and use MUIM_CallHook or subclass the object id_main_add so that it handles a particular method, and in the case of your method is received call your add some function (it's the recommended way).. the callhook stuff would be something like

struct Hook h = { addsome, NULL, NULL };
DoMethod(id_main_add, MUIM_Notify, MUIA_Pressed, FALSE, id_main_add,2, MUIM_CallHook, &h ); 


for AROS you have to change

#define MUI_LIST_DISP(x,y) ULONG SAVEDS ASM x (REG(a2,char **array), REG(a1, y ))
to
#define MUI_LIST_DISP(x,y) ULONG SAVEDS ASM x (REG(a0,struct Hook *h), REG(a2,char **array), REG(a1, y ))

so that a pointer to the Hook is the 1st argument. Same for other [hook macros http://aros.sourceforge.net/documentation/developers/headerfiles/aros/asmcall.h].

We have some hints about hooks in our [documentation http://aros.sourceforge.net/documentation/developers/app-dev/portable.php#hooks]


MUIM_List_Clear
MUIM_List_CreateImage
MUIM_List_DeleteImage
MUIM_List_Exchange
MUIM_List_GetEntry
MUIM_List_Insert
MUIM_List_InsertSingle
MUIM_List_Jump
MUIM_List_Move
MUIM_List_NextSelected
MUIM_List_Redraw
MUIM_List_Remove
MUIM_List_Select
MUIM_List_Sort
MUIM_List_TestPos

MUIA_List_Active
MUIA_List_AdjustHeight
MUIA_List_AdjustWidth
MUIA_List_AutoVisible
MUIA_List_CompareHook
MUIA_List_ConstructHook
MUIA_List_DestructHook
MUIA_List_DisplayHook
MUIA_List_DragSortable
MUIA_List_DropMark
MUIA_List_Entries
MUIA_List_First
MUIA_List_Format
MUIA_List_InsertPosition
MUIA_List_MinLineHeight
MUIA_List_MultiTestHook
MUIA_List_Pool
MUIA_List_PoolPuddleSize
MUIA_List_PoolThreshSize
MUIA_List_Quiet
MUIA_List_ShowDropMarks
MUIA_List_SourceArray
MUIA_List_Title
MUIA_List_Visible



Strings[edit]

How to get notification of when an MUI string gadget is made active? So I know when the user has clicked on the gadget and activated the cursor, but before they start typing anything? I've tried setting MUIA_InputMode to MUIV_InputMode_RelVerify and MUIV_InputMode_Immediate, but neither work with either the MUIA_Pressed or MUIA_Selected notifications.

Build a subclass of String.mui and do what ever you need to do in the MUIM_GoActive and MUIM_GoInactive methods.


Converts input from a 'text box' (using Zune/MUI) to a floating point number.

get(my-string-object, MUIA_String_Contents, &str);
    stcd_l(str, &num);


Subclass the gadget and overload MUIM_GoInactive. Just be sure you've considered every situation the user may be in, e.g. leaving the gadget temporarily to copy the rest of the value from some other place or similar. Needed to subclass string gadget and add own event handler. As a workaround (imho), return DoSuperMethodA(cl,obj,(Msg)msg); when you return 0.


You can notify MUIA_String_Contents, which will be triggered each time the content change. You can probably also use MUIA_Window_ActiveObject, which should be set to everything but the string gadget when it goes inactive. And if you use one of the string gadget replacements then there's also the possibility of overloading MUIM_GoInactive for the string class (Textinput, Newstring or Betterstring). But keep in mind that inactivating the gadget (using tab, the mouse, opening a new window or similar) should never have a confirming effect!



MUIA_String_Accept
MUIA_String_Acknowledge
MUIA_String_AdvanceOnCR
MUIA_String_AttachedList
MUIA_String_BufferPos
MUIA_String_Contents
MUIA_String_DisplayPos
MUIA_String_EditHook
MUIA_String_Format
MUIA_String_Integer
MUIA_String_LonelyEditHook
MUIA_String_MaxLen
MUIA_String_Reject
MUIA_String_Secret



Numeric (slider, knobs, levelmeters etc)[edit]

Releasing the button when NOT over the gadget is well regarded to be a "cancel" action. Like "whoops! I accidentally pressed the button, but now I don't want to exit, so I'll move my mouse away" Sliders also keep the value you dragged them to if you release the mouse outside the gadget.

How about getting a notification on a slider when the slider is released only? I tried MUIA_Pressed but that didn't work. If you also use Area.mui/MUIA_InputMode. Also, I noted that MUIA_Slider_Level says (OBSOLETE). Use MUIA_Numeric_Value instead.


If pressing TAB key cycles options. How can I implement that ? You just set MUIA_CycleChain (area class) as TRUE in all the objects you want to be part of the cycle-chain.

Cycle object

STRPTR colors[] = { "Red", "Green", "Blue", NULL };
ULONG colorsV;
STRPTR ch;
// then in the program...
get(cy1, MUIA_Cycle_Active, &colorsV);
ch = (char *)colorsV;

Cycle_active contains the NUMBER of the selected item. To get the string you need to do something like this:

ch = colors[ colorsv ];


Since MUIA_Cycle_Entries is [I..] the only way is to create a new cycle every time you want to change the entries. To do this you do MUIM_Group_InitChange on the group containing the cycle, then OM_REMMEMBER the old one, OM_ADDMEMBER the new one and do MUIM_Group_ExitChange. You may have to use MUIM_Group_Sort to get the cycle to the right place or put it into a separate group.

Can anyone tell me if it is possible to create a dynamic cycle gadger You can "emulate" it by removing the gadget and creating a new one with MUIA_Cycle_Entries changed. Use MUIM_Group_InitChange/ExitChange for this.

MUIA_Cycle_Active
MUIA_Cycle_Entries


a slider with gradient background. You can set gradient by MUIA_Background (but attribute string is undocumented) or maybe overloading MUIM_DrawBackground...

Sliders that look like a regular button when not pressed, but when pressed, they pop up and become a slider. They save a lot of space. It's a NumericbuttonObject - it's used in the demo program Slidorama.


MUIM_Numeric_Decrease
MUIM_Numeric_Increase
MUIM_Numeric_ScaleToValue
MUIM_Numeric_SetDefault
MUIM_Numeric_Stringify
MUIM_Numeric_ValueToScale

MUIA_Numeric_CheckAllSizes
MUIA_Numeric_Default
MUIA_Numeric_Format
MUIA_Numeric_Max
MUIA_Numeric_Min
MUIA_Numeric_Reverse
MUIA_Numeric_RevLeftRight
MUIA_Numeric_RevUpDown
MUIA_Numeric_Value




Menus[edit]

Mui has its own methods for menus but the old NewMenu calls can still be used...

Best way is maybe to create&dispose the context menus you need in a subclass of window class. Menu items are set for currently active window? In the case you can setup different menu for each of window, prefereably inside window subclass, and with notification which will call method or attribute or your's window class and it'll be able to react.


 


How to notify on pressing shortcut combination with shift, control and alt keys? I tryed such code:

DoMethod(win, MUIM_Notify,MUIA_Window_InputEvent, "-control x",app, 2,MUIM_Application_ReturnID,MEN_RUN);

but I get notification each time X button is pressed (no matter whether control is pressed).

You need a normal eventhandler in your class for that. Please do not use returnid stuff? So I see the only way is to use rawkey hook. Want such shortcuts for menu items, there are too many of them so I can't use combinations with Amiga for each one. menu item "MUI Settings...". To set a notify to the menu item that calls MUIM_Application_OpenConfigWindow


The way to do this would be to implement a MenuTrigger method in one of your subclasses (MUIC_Window or the window's Root MUIC_Group are always safe picks) and have a MUIA_Window_MenuAction notification call it. There you could either have a switch with all the IDs, or simply use methodIDs instead of menu IDs and just do it like:

ULONG mMenuTrigger(struct IClass *cl, Object *obj, struct mpMenuTrigger *msg)
{
DoMethod(obj, msg->menutriggercommand);
}

Once all menu triggers are nicely done as methods (say MUIM_MyClass_Open, MUIM_MyClass_DoSomething) and you've built a menu using those IDs, you could also implement an eventhandler in the Group subclass I mentioned and simply map raw keys into methods again. This could be done nicely using something like:

struct MenuKey
{
ULONG methodForQualifierShift;
ULONG methodForQualifierControl;
ULONG methodForQualifierAlt;
ULONG methodForQualifierAltShift;
};

and then have these mapped like

struct MenuKey hotkeys[256] = 
{
{0, MUIM_MyClass_Jump}, // RAWKEY_TILDE
{MUIM_MyClass_First}, // RAWKEY_1
....
};

so you can easily map every key combo into a method.

Well... just one of the many possible approaches to this problem, but one that will leave you with a nice oo structure in the source.


Added a check for msg->imsg->Code==MENUDOWN which reatcs the RMB being pressed and stops the timer. I also did the reverse msg->imsg->Code==MENUUP which would start the timer again. BUT - once the Menu is open the mouse button presses are attached to the menu and not my window/object so the MENUUP button event is never seen so my timer remains stopped and does not start again after using a menu. You could use MUIA_Window_MenuAction to be notified when a menuitem was chosen,

DoMethod (win, MUIM_Notify, MUIA_Window_MenuAction, MUIV_EveryTime, obj, 2, MM_Main_MenuAction, MUIV_TriggerValue); 

Not sure if the attribute gets triggered when no item was selected...

Checking window activation does not help either as the window remains active when a menu is opened so nothing changes. You could add a global input handler for checking of RMB pressing and releasing and then also check if the mouse pointer is over your window...


Redundant MUIA_Application_Menu and MUIA_Window_Menu (old New Menus system)

Redundant MUIM_Application_GetMenuCheck, MUIM_Application_GetMenuState, MUIM_Application_SetMenuCheck, MUIM_Application_SetMenuState

Redundant MUIM_Window_SetMenuCheck, MUIM_Window_SetMenuState, MUIM_Window_GetMenuCheck, MUIM_Window_GetMenuState


MUIA_Menu_Enabled
MUIA_Menu_Title



MenuStrip[edit]

Menustrip class is the base class for MUI's object oriented menus. Its children are objects of Menu class, each of them describes exactly one menu.

A Menustrip object doesn't feature many options itself, but as a subclass of Family class, it simply acts as father for multiple Menu objects.

The Menustrip object is usually specified as a child of either Application class or window class with the attributes MUIA_Application_Menustrip or MUIA_Window_Menustrip.

from autodocs MUI_Menuitem/Menuitem.mui: "Since Menuitem class is a subclass of Family class, you can add other Menuitems as children of a menu item to indicate sub menus. MUI does not limit the level of sub menus, but the operation system currently allows a maximum nesting level of one. Because of this, children of menu items should not contain other menu items for now, the results are unpredictable."

With family objects (from Wanderer/Info):

DoMethod(_win(obj), MUIM_Notify, MUIA_Window_MenuAction, MUIV_EveryTime, obj, 2, MUIM_MySubClass_MenuThingy, MUIV_TriggerValue);

Read more about Menus in MUIA_Window_MenuAction section of muidevs.

See also DiskSpeed.c in DiskSpeed drawer of the contrib-source of AROS.


In one MenuItem tag there wasn`t a "MUIA_UserData" tag identifier in front of the ID of that menuitem and so on a dispose gcc somehow freed an area in memory that caused the crash where sas/c didn`t somehow.

entry = MUI_MakeObject(MUIO_Menuitem,advance_name_translation(i),NULL,0,0);
set(entry,MUIA_UserData,i);
DoMethod(entry,MUIM_Notify,MUIA_Menuitem_Trigger, MUIV_EveryTime, entry,6, MUIM_CallHook, &civstandard_hook, diplomacy_tech, pdialog, plr0->player_no,entry);
DoMethod(menu_title,MUIM_Family_AddTail, entry);



MUIA_Menustrip_Enabled




Requesters[edit]

MUI_Request(app, mainwnd, 0L, "Sorry...", "Close", "...but this feature is not yet implemented", NULL);


Either create your new objects outside this huge MUI constructions, and in there use just a pointer, or get rid of the "End" macro and exchange it with "TAG_DONE)". On MOS and AmigaOS, the NewObject variadic function is kept in the static library. It takes most of the parameters on the stack - thanks to that the implementation of NewObject calls the NewObjectA function. Everything works perfect, and the typical MUI macros may be easily used.

This, however, is not the case when you compile for AROS. Here, NewObject is a variadic macro, not a function. Thanks such approach we do not need any custom compiler in case of systems, where the arguments of variadic functions are passed partially through registers and partially through the stack (This is the case of PPC and x86_64, this is also the reason why both OS4 and MOS require specially patched compilers).

Since NewObject is a macro, the gcc's preprocessor expects the list of macros arguments enclosed within parentheses. In MUI macros it is not the case.



ASL requester[edit]

this should help:

struct FileRequester *req;
 
if ((req=MUI_AllocAslRequestTags(ASL_FileRequest,
ASLFR_Window,win ,
ASLFR_TitleText, "A requester for AmiDevCpp",
ASLFR_InitialDrawer , "T:",
ASLFR_InitialFile, "foo.txt",
ASLFR_InitialPattern , "#?.txt",
ASLFR_DoPatterns , TRUE,
ASLFR_RejectIcons , TRUE,
TAG_DONE))) {
 
if (MUI_AslRequestTags(req,TAG_DONE)) {
/* you can find the drawer in req->fr_Drawer and the file in req->fr_File */
}
}

MUI_AslRequest() functions and MUI takes care of window refresh for you.


MUIA_Popasl_Active
MUIA_Popasl_StartHook
MUIA_Popasl_StopHook
MUIA_Popasl_Type



Pop Up Pen etc[edit]

MUIA_Window or MUIA_WindowObject? MUIA_Window should refer to struct Window * but MUIA_WindowObject to Object *

Both are always NULL for a popup list, whether open or not.

An interesting point is that the window object pointer supplied to the MUIA_Popobject_WindowHook is non-NULL. checking for whether popup is open such work arround anyway? Popup object is open only a while, when user clicked popup button and popup object appeared on screen- any clicking causes popup object to disappear. So, I can't even imagine where and why are you checking this.. From 2nd parallel running task?

If popup object is made by you, you always can subclass any MUI class. And this way you will get all methods and attributes like f.e. OM_SET/OM_GET and MUIM_Setup/MUIM_Cleanup/MUIM_Show/MUIM_Hide. Probably listening on show/hide might help checking whether popup is currently open. This method probably should work also for other MUI applications, if you're not owner of app, but making monitor software- but instead subclassing get and store dispatcher address, then put your own, and listen methods/attrs like above, then call original dispatcher to return control to original application.

It looks like Popstring has special hooks for this task

  • MUIA_Popstring_OpenHook
  • MUIA_Popstring_CloseHook

Shift up/down is (normally) bound to page up/down, and if the listview hasn't been drawn once, then there are no page dimensions.


DoMethod(PP_String, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, App_p, 2, MUIM_Application_ReturnID, RID_EXECUTE);

A notify watch if a tag changes (for the given object) - MUIA_String_Acknowledge is a tag of the string class (not the Popph-string) and is set (and therefor changed) for the string object. So the notify must be on this object, rather than the container (which is Popph)

Either get the pointer to the internal string-object, and setup a notify on this (and do try to avoid ReturnID's ;-) ) Should just be to forward MUIM_Notify to the child objects, preferably only if OM_GET returns TRUE for the tag in question. I.e. if OM_GET returns TRUE for the Popph object itself (an Area class tag probably) then keep the notify, otherwise try to see which of the children supports it. Or maybe it'd be better if Popph introduced two new methods a la MUIM_Popph_NotifyString & List. Then one could use either of these to setup a notify on an internal object.


nothing wrong with ObtainBestPenA() if you don't want the user to configure the pen values (which you often do want) - though avoid placing the pen-stuff in MUIM_Show/Hide, it should be MUIM_Setup/Cleanup, otherwise it'll re-allocate pens each time you resize the window

If you want the user to be able to configure the pens then you do so by using the Poppen-object together with MUI_ObtainPen() - here's a snip from MUI_PopPen.Doc:

Anyway, you do not have to care about the internals of this class. Just create it like

obj = MUI_NewObject(MUIC_Poppen, MUIA_CycleChain , 1, MUIA_Window_Title, "Followed Links Color", TAG_DONE);

somewhere in your prefs window and everything will be fine. You can get/set the current color from a Poppen object by using the MUIA_Pendisplay_Spec attribute. The resulting struct MUI_SpenSpec may then be saved somewhere in your preferences and used as parameter for MUI_ObtainPen() and MUI_ReleasePen().

comes back in the format of: "rxxxxxxgxxxxxxbxxxxxx". There are 3 parameters in MUI_ObtainPen() *mri , *spec & flags.l

look in libraries/mui.h and you will fin a macro like this muiRenderInfo() just do this

mymri = muiRenderInfo(myobj);

and should have a valid MUI_RenderInfo but this macro should only be used between MUIM_Setup/MUIM_Cleanup


A popobject which is connected to a string gadget. The popobject is a listview. So that pressing up/down in the string gadget will scroll the listview updown (even when the listview is hidden) ?

MUIA_String_AttachedList -- (V4 ) [ISG], Object *


MUIA_Popobject_Follow
MUIA_Popobject_Light
MUIA_Popobject_Object
MUIA_Popobject_ObjStrHook
MUIA_Popobject_StrObjHook
MUIA_Popobject_Volatile
MUIA_Popobject_WindowHook


MUIA_Poplist_Array


MUIM_Popstring_Close
MUIM_Popstring_Open

MUIA_Popstring_Button
MUIA_Popstring_CloseHook
MUIA_Popstring_OpenHook
MUIA_Popstring_String
MUIA_Popstring_Toggle



Text[edit]

  • (N)Floattext already does exactly the same as you did using the list. It splits a long text into separate lines, inserts some spaces to produce an aligned text and displays them.
  • The TextEditor class in read-only does more or less the same like Floattext, but with the ability to apply different styles and coloring apart from the standard MUI text engine. Furthermore it allows to let the user edit the text in read-write mode. YAM makes heavy use of it in both modes to show and edit the mails.

Where ony Text() is done. So the size of the area depends on the size of the actual default font. Using rp(obj)->Font, but in AskMinMax it seems that when it is first called, the Font is not yet initialized. You can use _font(obj) to obtain the font. If you also need a rastport then you can setup one temporarily to do your calculations.

struct RastPort rp;
InitRastPort(&rp);
SetFont(&rp, _font(obj));
WORD size = TextLength(&rp, "The Text", 8);


The problem is that text will not stay the right size. They are either spread all over the window, or cramped into one edge, when all I want them to do is to stay on the right and just as big to display 6 chars (as long as possible). MUIA_String_Maxlen seems to have no effect, and just forcing one width doesn't seem right as you never know wether the user may prefer to use a font 50 pixels wide.

Use tag MUIA_FixWidthTxt. For example

str = StringObject, MUIA_FixWidthTxt, "0000000", End;

Note 7 characters there: one is for cursor. And use characters which are wide by nature (read MUI_Area.doc and you understand why).

It could also be solved by using MUIA_Weight, weight. Try a combination of "MUIA_Weight, weight" for the string(s) and Put RectangleObjects before/and/or/after the stringobject.

For example, to align a gadget to the right

HGroup,
Child, RectangleObject, MUIA_Weight, 75, End,
Child, StringObject,...,MUIA_Weight, 25, End,
End,


Which means that if you want to change the label and the key shortcut using one attr, you should sublcass text, and handle a specific tag for changing the label and the shortcut, parsing yourself the string. Examples of subclassing a mui class can be found in mui dev kit (especially psi contains many subclasses...) and some tools can help you to have a skeleton class using a meta definition of the class you want (mui2c or bcc do such things).


If you create a subgroup something like:

mygroup=Group ( MUIA_Group_PageMode,True
Child,SimpleButton("_Download"),
Child,SimpleButton("_Resume"),
TAG_DONE)

you get much better result. The "Download" button is now visible, but if you

set (mygroup, MUIA_Group_ActivePage, 1)

"Download" disappears and "Resume" becomes visible. This is very common trick in MUI applications.


Buttons are simply text objects, which have the following tag:

MUIA_Text_SetVMax, TRUE 

Setting this to FALSE makes a TextObjects y-size unlimited. Defaults to TRUE which means the objects height is fixed. A plain text object cannot be higher than its text, unless you set MUIA_Text_SetVMax to FALSE (default is TRUE). This will give your object unrestricted height, but the text will be displayed vertically centered, unless you set MUIA_Text_VCenter to FALSE.


MUIA_Text_Contents is ISG so:

SetAttrs(text, MUIA_Text_Contents, "NewContent", TAG_DONE);
set (object->STR_text, MUIA_Text_Contents, myText);

object->STR_Text ? Looks like MUIBuilder generated code :)

If your 'text-edit-box' is a String object, you'd rather use

set( object->STR_text, MUIA_String_Contents, myText ); 


How to calculate the x/y coords to use with the Move() function so that when using Text() to draw the label it appears centred. There is bunch of macros in mui.h:

_mleft() = starting x coordinate of your drawing area
_mtop() = starting y coord etc.
_mwidth() = width of your drawing area
_mheight() = obvious =)
text_height=GetItSomeHow();
text_len=TextLength("heipparallaa");

Move(rp, _mleft(obj) + ((_mwidth(obj) - text_len) / 2), _mtop(obj) + ((_mheight(obj) - text_height) / 2) );


(TextObject) If you really need to change the text, then you must set() it again after the modification.

If using a TextObject, then it has the FULL initial size... but if appending extra lines then these are not visible (although a bug causes it to write outside of itself!). The only way to get it to enlarge for the new text is to iconise & then uniconise the MUI window. Also, the text is not word-wrapped.

You must not modify the text as long as it is "in use" by the object. MUI will calculate the required dimensions on basis of the given text and expects it to be constant. If you really need to change the text, then you must set() it again after the modification. This will let MUI do a relayout to display all of the new text.

But this will work to a certain extend only, namely when the complete text's height becomes larger than the window's maximum height. If you need to display larger texts, then the object should be placed into a Scrollgroup object (some of which is MUI4 compatible) like this:

ScrollgroupObject,
  MUIA_Scrollgroup_AutoBars, TRUE,
  MUIA_Scrollgroup_Contents, VGroupV,
    Child, TextObject,
      MUIA_Text_Contents, "insert your text here",
      MUIA_Text_Copy, TRUE,
      End,
    End,
  End,

The attribute MUIA_Scrollgroup_AutoBars will let the scrollgroup show the scrollbars only if they are really needed, i.e. the contents' dimensions are larger than the scrollgroup's dimensions. This attribute is available in MUI4 only like MUIA_Text_Copy, MUI3 will silently ignore it and always show the scrollbars.

Second, the attribute MUIA_Text_Copy instructs Text.mui to keep a copy of your text. This makes it possible to free() the text given to MUIA_Text_Contents after object creation of after set() and it also allows you to modify the text without risking any graphical glitches due to redraw operations while the text is still modified.

However, your text will always be displayed exactly as you specify it. This means that you need to insert line breaks yourself.


If using FloattextObject or TextEditorObject, then no matter the initial contents, the initial size is always 3 lines of text (which is the minimum space needed by the scrollbar + arrows), but it is at least vertically scrollable. Also, the text is word-wrapped.

Since Floattext.mui inserts line breaks itself you cannot expect a variable height, because the text's height depends on the object's width. You can enforce a certain object height by using something like MUIA_FixHeightTxt, "\n\n\n\n". This will give you a fixed height of 4 lines while respecting the object's font.

Floattext.mui is a subclass of List.mui and as such has no restrictions on its dimensions, except you enforce them, i.e. by using MUIA_FixHeightTxt or by subclassing Floattext.mui yourself with a restricting implementation of MUIM_AskMinMax. MUI cannot just resize an object to display the entire inital content, because the content is arbitrary. It may be zero lines, but it may be a million lines. What should the initial size be in this case? Of course you can override MUIM_AskMinMax and return the dimensions which fit you best, but you must be prepared and accept that MUI will adapt the actual dimensions of the object within the limits that your (or the default) MUIM_AskMinMax method returns.

If want to the Floattext object to be resizeable then you must make the label resizeable as well by adding at least one space object above or below the label object like this:

Child, VGroup, Child, Label2("FloattextObject"), Child, VSpace(0), End,

This will give you a top aligned label. Placing the label after the VSpace(0) object will make it bottom aligned. Using VCenter() will give you a vertically centered label. Choose the solution which fits your needs best.


Have a look at MUI's demo application MUI-Demo. It uses Floattext objects in every single window.


MUIA_Text_Contents
MUIA_Text_PreParse
MUIA_Text_SetMax
MUIA_Text_SetMin
MUIA_Text_SetVMax



Fonts[edit]

try putting SetFont(_font(obj)); in your MUIM_Draw.

In OM_NEW you should set MUIA_Font to one of the predefined values (the MUIV_Font_XXXXXX values) and mui will open the font for you and make it accessible using _font(obj)

In MUIM_Draw, SetFont() to my fonts opened in MUIM_Setup. If SetFont(_font(obj)) otally lose user-settable-fonts. You are not allowed to change settings of _rp( obj ) directly! So make sure you are working on a copy of original rastport and then set fonts, colours or whatever.

struct RastPort rp1 = *_rp( obj );
struct RastPort *rp = &rp1;

SetAPen( rp, 0 );

The same applies to normal gadgetclass BOOPSI objects, where before drawing anything you have to ObtainGIRPort(), draw and then ReleaseGIRPort()...


MUICFG_Font_Fixed




Gadgets[edit]

MUIM_Import/Export and handle the loading and saving yourself. The advantages are that you don't need to create, and call MUIM_Export for the pages that the user doesn't visit, as you'll export the settings (from the created/visited pages) into an already filled dataspace object (loaded during program startup) - which should save both time, memory and complexity. Another advantage is that it makes it very easy to add options such as "Reset to defaults", "Restore" etc.


Two functions which should make it easier to save/read IFF files into dataspace objects.

VOID LoadDataspace (STRPTR file, Object *dataspace)
{
BPTR fh;
if(fh = Open(file, MODE_OLDFILE))
{
struct IFFHandle *iff;
if(iff = AllocIFF())
{
InitIFFasDOS(iff);
iff->iff_Stream = (ULONG)fh;
if(!OpenIFF(iff, IFFF_READ))
{
if(!StopChunk(iff, 'PREF', 'IPRB'))
{
if(!ParseIFF(iff, IFFPARSE_SCAN))
DoMethod(dataspace, MUIM_Dataspace_ReadIFF, iff);
}
CloseIFF(iff);
}
FreeIFF(iff);
}
Close(fh);
}
}


VOID SaveDataspace (STRPTR file, Object *dataspace, Object *app)
{
BOOL retry;
do {
 
retry = FALSE;
BPTR fh;
if(fh = Open(file, MODE_NEWFILE))
{
struct IFFHandle *iff;
if(iff = AllocIFF())
{
InitIFFasDOS(iff);
iff->iff_Stream = (ULONG)fh;
if(!OpenIFF(iff, IFFF_WRITE))
{
if(!PushChunk(iff, 'PREF', 'FORM', IFFSIZE_UNKNOWN))
DoMethod(dataspace, MUIM_Dataspace_WriteIFF, iff, 0, 'IPRB');
 
CloseIFF(iff);
}
FreeIFF(iff);
}
Close(fh);
}
else
{
UBYTE buf[84];
Fault(IoErr(), "DOS", buf, 84);
retry = MUI_Request(app, NULL, 0L, "Error", "_Retry|_Cancel", "\33cError saving:\n%s\n\n%s", file, buf);
}
 
} while(retry);
}

One object can't belong to two parents.


How does one insure free pens being allocated? I am running most applications on 16 bit screens. Not sure what is required but I would think there should be some way to have some free. What/how does on "free" pens? On True/Hi color screens You may expect to have unlimited number of free pens (or at least 65536/16777216 pens), because you can change the color of every single pixel without affecting the rest. But unfortunately all RastPort rendering functions rely on pen scheme and pen fields in RastPort are only 8-bit wide. It gives you only 256 pens.

Tried to use the MUIA_Bitmap_SourceColors and the MUIA_Bitmap_Precision but all I get is a big black rectangle in place of the discoloured image. Did you set MUIA_Bodychunk_Depth to 5 and MUIA_Bodychunk_Masking to 0 (but check if you save the brush *without* mask plane)? And remember that you should repeat R, G, B values in every byte of a longword, so for example some colour like R=$A5, G=$32, B=$0A, in palette data it looks like $A5A5A5A5,$32323232,$0A0A0A0A. So every palette entry takes 12 bytes.


You need to set the MUIA_Window_UseBottomBorderScroller (or whatever side you want) on the window. Then create a Prop Object and set the MUIA_Prop_UseWinBorder to the corresponding side(s) that you did in the window. Then just set up a notification on the prop object to listen for changes in MUIA_Prop_First. When you place the one on the right and open the window, MUI opens the window the smallest possible and doesn't draw any of the other objects that should be in the window.

dtpic Datatypes[edit]

If you would like to get a pointer to the bitmap that holds the data of the picture or a way to copy it to your own bitmap but all Zune attributes did not work.

In that case you will want to use datatypes.library directly. Dtpic AFAIK is just for easily including datatype images into MUI/Zune GUIs (as button imagery, in listviews and so on...).

If AROS supports the superbitmap window refresh method then you can. For a non-borderless window you will probably want to make it gimmezerozero (gzz) as well so that the borders and window contents have separate layers (uses slightly more memory). That being said just blitting the bitmap into the window is probably a simpler and more portable solution.

How to "convert" e.g. a png image to a bitmap structure (datatypes?). DTpic.mui is your friend. Using picture.datatype and then blitting bitmap to object rastport in MUIM_Draw.

Child, image_button = MakeButton("PROGDIR:Imagesg/test_but.png", 't',"\33uT\33nest Button"),


static APTR MakeButton(UBYTE *Image, UBYTE Key, UBYTE *Help)
{
return(MUI_NewObject("Dtpic.mui",
MUIA_Dtpic_Name,Image,
MUIA_InputMode, MUIV_InputMode_RelVerify,
MUIA_ControlChar, Key,
MUIA_Background, MUII_ButtonBack,
MUIA_ShortHelp, Help,
PropFrame,TRUE,
ImageButtonFrame,
TAG_DONE));
}


if(app)
{
   struct BitMapHeader *bmhd;
   struct BitMap *bitmap;
   Object *datatype_obj;       /* pointer to the Datatype Object          */
   Object *bitmap_obj;         /* a mui BitmapObject                      */
   Object *list_obj;           /* the one that should be used in the list */

   if(datatype_obj = NewDTObject("Images/www.iff",
      DTA_SourceType , DTST_FILE,
      DTA_GroupID    , GID_PICTURE,
      TAG_DONE))
   {
      if(DoMethod(datatype_obj, DTM_PROCLAYOUT, NULL, 1))
      {
         get(datatype_obj, PDTA_BitMapHeader, &bmhd);
         GetDTAttrs(datatype_obj, PDTA_DestBitMap, &bitmap, TAG_DONE);
         if(!bitmap)
            GetDTAttrs(datatype_obj, PDTA_BitMap, &bitmap, TAG_DONE);

         if(bitmap_obj = BitmapObject,
            MUIA_Bitmap_SourceColors  , xget(datatype_obj, PDTA_CRegs),
            MUIA_Bitmap_Width         , bmhd->bmh_Width,
            MUIA_Bitmap_Height        , bmhd->bmh_Height,
            MUIA_FixWidth             , bmhd->bmh_Width,
            MUIA_FixHeight            , bmhd->bmh_Height,
            MUIA_Bitmap_Transparent   , 0,
            MUIA_Bitmap_Bitmap        , bitmap,
            End)
         {
            list_obj = (Object *)DoMethod(LV_ButtonBank, MUIM_List_CreateImage, bitmap_obj, NULL);
         }
      }
   }
}



in MUI4.0

Child, zoneimage = (Object *) MUI_NewObject(MUIC_Dtpic,
                                MUIA_Dtpic_Name, "PROGDIR:resource/zone_local.png",
                                TAG_DONE),
                                        
DEFSMETHOD(OWBWindow_UpdateZone)
{
        GETDATA;

        char *image;

        switch(msg->zone)
        {
                default:
                case MV_OWBBrowser_Zone_Local:
                        image = "PROGDIR:resource/zone_local.png";
                        break;
                case MV_OWBBrowser_Zone_Internet:
                        image = "PROGDIR:resource/zone_internet.png";
                        break;
        }

        set(data->zoneimage, MUIA_Dtpic_Name, image);

        return 0;
}

i.e. by default we set zone_local image, and then by notification calls updatezone when the attribute is set. And it should changes (image i mean), but did't not. I see that i am in the Zone_Internet (by printfs), but image not sets. Problems with 3.9




Removal[edit]

void ExitClasses(void)
{
    if (NewList)
        {
        MUI_DeleteCustomClass(NewList);
                NewList = NULL;
        }
}   

BOOL InitClasses(void)
{
    NewList = MUI_CreateCustomClass(NULL,MUIC_NList ,NULL,sizeof(struct NewList_Data),&NewList_Dispatcher);

        if (NewList) return(TRUE);
        else        return FALSE;
}


Another thing, which your compiler should notice immediately, is that MyCustomClass is struct IClass * and assigning Object * to it is very, very wrong, and should effect crash at MUI_DeleteCustomClass()...

#define MyCustomObject NewObject(MyCustomClass->mcc_Class,NULL,

Object *mycustomobject;
mycustomobject = MyCustomObject, End;
if( mycustomobject != NULL )
{
app = ApplicationObject, [... ]
End;
if( app != NULL )
{
[...]
MUI_DisposeObject( app );
}
MUI_DisposeObject( mycustomobject );
}

Or preferably you should subclass Application.mui class and do your non-visible code there... That's the best way to handle it IMHO... This way main program code will be almost untouched and going back to using standard Application.mui within seconds, just rename MyApplicationObject to ApplicationObject (always define such macros instead of writing NewObject() directly!)


Objects are removed using OM_REMMEMBER method (must use parent object of course)




Reference[edit]

http://alfie.altervista.org/rxmui/rxmuidoc/classes.html

http://amiga.sourceforge.net/amigadevhelp/phpwebdev.php


When one of the qualifier (shift, ctrl, alt) keys is clicked then the opposite key should get the same state. I tried in the callback function:

BOOL shift = XGET(data->keybutton[96], MUIA_Pressed) |
XGET(data->keybutton[97], MUIA_Pressed);

NNSET(data->keybutton[96], MUIA_Pressed, shift);
NNSET(data->keybutton[97], MUIA_Pressed, shift);

Pressing the qualifier keys on the keyboard should act as if the button was clicked. I've therefore added an event handler method. It works so far as the visual state of the buttons is changed. Problem is that SET(keyobj, MUIA_Pressed, state) in that event method doesn't trigger the callback hook.

Try MUIA_Selected, instead of MUIA_Pressed (which is really just a thing to be used for notification, not a real attribute). You'll also have to change code logic, because once the two SHIFT buttons are selected, it won't be possible to unselected them ("shift" var would always be TRUE).



Networking with a non-blocking recv(), start with one task and make all my interfaces push-based. That is, no-one will call recv() but instead be called with the data received from a socket (when it arrives). So in your main-loop you wait for both sockets and user events and distribute these to the proper receiver (where the latter would be done by MUI). The reason for this approach is to simplify code (eliminate the need to lock resources/data with semaphores etc), and also because found out that spawning tasks had a noticeable overhead (but you could keep one network manager running all the time to get by this), not to mention that if you receive data on 8 sockets (each having its own process) then your program will be represented with 9 total tasks and thus if another program needs cpu-time it'll only get 1/10th of the time, where your program will get 9/10th, unless you fiddle with the priority of your tasks but then other problems appear. If the data you receive needs to be parsed in some way you may at first find it irritating to have to write your parser as a state-machine, but these also have advantages IMO. For example it makes it very easy to add filters to the data stream or create virtual data on the fly...

Basically you need a socket operating in the non-blocking & asynchronous mode. This prevents GUI being locked while accessing network. Another step is allocate signal bit for bsdsocket and use GetSocketEvent() to find out network actions without braindead select() or WaitSelect() functions. So that whenever network signal is set you call GetSocketEvent() in loop. This GetSocketEvent() returns socket descriptor ID and possible events occurred. Avoid GetSocketEvent() and its minion like it was the bubonic plague!!!!!!!! As it turned out, it only works under Miami. Even though AmiTCP copied the API from M$, it never worked correctly. Use WaitSelect() instead of Wait() in your MUI main loop. See STRICQ 2 src. How do you receive data from sockets? Is there a way to associate a signal to a socket? You can associate a signal with a socket by using SocketBaseTags(). Use WaitSelect() in the STRICQ v2 socket code. But in v1, used a signal.

You can encapsulate internet client in MUI application subclass, create some useful methods and attributes like MUIM_NetApplication_Connect/Disconnect/Send/Receive, and then when user press Connect/Disconnect button, call these methods in notification or callback function (so if NetApplication connected you will disable Connect, and enable Disconnect etc. gadgets). And so on. Never connect to the internet immediately in OM_NEW, that would be stupid as well as creating needed subprocess in there (which from other side might be not good idea). It can wait then as long as application sends the connect signal or message and then do it's network job, until it receives MUIM_NetApplication_Disconnect method.. How to... merge bsdsocket.library and muimaster.library in single, nicely OOP, application.




Examples[edit]

/*
    Copyright © 2002, The AROS Development Team.
    All rights reserved.
 
    $Id: aircos_server_gui.c 30794 2009-03-08 02:19:07Z neil $
*/
 
//#include    <exec/types.h>
#include    <stdlib.h>
#include    <stdio.h>
#include    <string.h>
 
#include    <proto/alib.h>
#include    <proto/exec.h>
#include    <proto/dos.h>
#include    <proto/intuition.h>
#include    <proto/muimaster.h>
#include    <proto/utility.h>
 
#include    <dos/dos.h>
#include    <intuition/gadgetclass.h>
#include    <intuition/icclass.h>
#include    <clib/alib_protos.h>
 
#include <libraries/mui.h>
#include <mui/NListtree_mcc.h>
#include <mui/NListview_mcc.h>
#include <mui/NList_mcc.h>
 
#include "aircos_global.h"
#include "locale.h"
 
extern struct AiRcOS_internal *AiRcOS_Base;
struct List                   aircos_Prefs_ServerNetworks;
struct List                   aircos_Prefs_Servers;
struct aircos_servernode      *aircos_Prefs_ServerActive;
struct Hook                   aircos_serversave_hook;
struct Hook                   aircos_networklistupdate_hook;
struct Hook                   aircos_serverlistupdate_hook;
struct Hook                   aircos_chooseserver_hook;
 
BOOL                          aircos_Prefs_ServersLoaded = FALSE;
 
Object	                     *input_server_address = NULL;
Object	                     *input_server_port =  NULL;
Object	                     *input_server_description =  NULL;
Object	                     *input_server_network =  NULL;
Object	                     *input_server_pass =  NULL;
 
Object                        *select_dropboxgrp_network = NULL;
Object                        *select_dropbox_network = NULL;
Object                        *select_dropboxgrp_server = NULL;
Object                        *select_dropbox_server = NULL;
 
STRPTR	                     network_list_empty[2] =
{
  {"<List Empty>"},
  NULL
};
STRPTR	                     server_list_empty[2] =
{
  {"<List Empty>"},
  NULL
};
Object	                     *servermodWin;
STRPTR	                     *network_list;
STRPTR	                     *server_list;
 
//network_list_empty[0] = "<List Empty>";		//MSG(MSG_LIST_EMPTY);
//network_list_empty[1] = NULL;
 
//server_list_empty[0] = "<List Empty>";		//MSG(MSG_LIST_EMPTY);
//server_list_empty[1] = NULL;
 
#define AIRCOS_DEF_SERVERSFILE "servers.dat"
 
aircosApp_LoadServers()
{
  aircos_Prefs_ServersLoaded = TRUE;
}
 
struct aircos_networknode *aircosApp_FindServerNetworkNode(char * findNetwork)
{
  struct aircos_networknode *current_Node = NULL;
  ForeachNode(&aircos_Prefs_ServerNetworks, current_Node)
  {
D(bug("[AiRcOS](FindServerNetworkNode) Checking against record for '%s'\n", current_Node->ann_Network));
      if (strcasecmp(current_Node->ann_Network, findNetwork)==0) return current_Node;
  }
  return NULL;
}
 
struct aircos_servernode  *aircosApp_FindServerNode(char * findServer)
{
struct aircos_servernode  *current_Node = NULL;
  ForeachNode(&aircos_Prefs_Servers, current_Node)
  {
D(bug("[AiRcOS](FindServerNode) Checking against record for '%s'\n", current_Node->asn_Server));
      if (strcasecmp(current_Node->asn_Server, findServer)==0) return current_Node;
  }
  return NULL;
}
 
AROS_UFH3(void, chooseserver_func,
   AROS_UFHA(struct Hook *, unused_hook, A0),
   AROS_UFHA(APTR, obj, A2),
   AROS_UFHA(struct IRC_Channel_Priv  *, hook_channel_arg, A1 ))
{
   AROS_USERFUNC_INIT
D(bug("[AiRcOS] chooseserver_func()\n"));
  ULONG                      currentPrefsServerID;
  get(select_dropbox_server, MUIA_Cycle_Active, &currentPrefsServerID);
 
  struct aircos_servernode *currentPrefsServer = NULL;
  if (!(currentPrefsServer = aircosApp_FindServerNode(server_list[currentPrefsServerID])))
  {
D(bug("[AiRcOS] chooseserver_func: Couldnt find Server Node!\n"));
    return;
  }
 
  aircos_Prefs_ServerActive = currentPrefsServer;
 
  AROS_USERFUNC_EXIT
};
 
AROS_UFH3(void, updatenetworklist_func,
   AROS_UFHA(struct Hook *, unused_hook, A0),
   AROS_UFHA(APTR, obj, A2),
   AROS_UFHA(struct IRC_Channel_Priv  *, hook_channel_arg, A1 ))
{
   AROS_USERFUNC_INIT
D(bug("[AiRcOS] updatenetworklist_func()\n"));
  struct aircos_networknode *currentPrefsNetwork = NULL;
  ULONG                      prefsNetworkCount = 0;
  ULONG                      setprefsNetworkActive = 0;
  Object                     *new_dropbox_network = NULL;
 
  ForeachNode(&aircos_Prefs_ServerNetworks, currentPrefsNetwork)
  {
      prefsNetworkCount++;
      if (aircos_Prefs_ServerActive)
        if (strcasecmp(currentPrefsNetwork->ann_Network, aircos_Prefs_ServerActive->asn_Network->ann_Network)==0)
        {
          setprefsNetworkActive = prefsNetworkCount -1;
        }
  }
 
D(bug("[AiRcOS] updatenetworklist_func: %d network nodes\n", prefsNetworkCount));
 
  if (prefsNetworkCount > 0)
  {
    if (network_list != network_list_empty) {
      FreeVec(network_list);
    }
 
    if ((network_list = AllocVec(sizeof(IPTR) * prefsNetworkCount+1, MEMF_CLEAR|MEMF_PUBLIC)))
    {
      int loop_count = 0;
 
      ForeachNode(&aircos_Prefs_ServerNetworks, currentPrefsNetwork)
      {
        network_list[loop_count] = currentPrefsNetwork->ann_Network;
        loop_count++;
      }
    }
    else
    {
D(bug("[AiRcOS] updatenetworklist_func: ERROR - couldnt allocate memory for network name pointer table\n"));
      network_list = network_list_empty;
    }
  } else if (network_list != network_list_empty) {
    FreeVec(network_list);
    network_list = network_list_empty; 
  }
 
  if (!(new_dropbox_network = MUI_MakeObject(MUIO_Cycle, NULL, network_list)))
  {
D(bug("[AiRcOS] updatenetworklist_func: Failed to create Network dropdown\n"));
    return NULL;
  }
 
  if (DoMethod(select_dropboxgrp_network, MUIM_Group_InitChange))
  {
    DoMethod(select_dropboxgrp_network, OM_REMMEMBER, select_dropbox_network);
    DoMethod(select_dropboxgrp_network, OM_ADDMEMBER, new_dropbox_network);
    DoMethod(select_dropboxgrp_network, MUIM_Group_ExitChange);
    select_dropbox_network = new_dropbox_network;
  }
 
  DoMethod(select_dropbox_network, MUIM_NoNotifySet, MUIA_Cycle_Active, setprefsNetworkActive);
  DoMethod
  (
      select_dropbox_network, MUIM_Notify, MUIA_Cycle_Active, MUIV_EveryTime,
      (IPTR) AiRcOS_Base->aircos_app, 3, MUIM_CallHook, &aircos_serverlistupdate_hook, NULL
  );
 
  AROS_USERFUNC_EXIT
};
 
AROS_UFH3(void, updateserverlist_func,
   AROS_UFHA(struct Hook *, unused_hook, A0),
   AROS_UFHA(APTR, obj, A2),
   AROS_UFHA(struct IRC_Channel_Priv  *, hook_channel_arg, A1 ))
{
   AROS_USERFUNC_INIT
D(bug("[AiRcOS] updateserverlist_func()\n"));
  struct aircos_servernode   *currentPrefsServer = NULL;
  ULONG                      prefsNetworkServerCount = 0;
  ULONG                      setprefsServerActive = 0;
  Object                     *new_dropbox_server = NULL;
 
  ULONG                      currentPrefsNetworkID;
  get(select_dropbox_network, MUIA_Cycle_Active, &currentPrefsNetworkID);
 
  struct aircos_networknode *currentPrefsNetwork = NULL;
  if (!(currentPrefsNetwork = aircosApp_FindServerNetworkNode(network_list[currentPrefsNetworkID])))
  {
D(bug("[AiRcOS] updateserverlist_func: Couldnt find Network Node!\n"));
    return;
  }
 
  ForeachNode(&aircos_Prefs_Servers, currentPrefsServer)
  {
      if (strcasecmp(currentPrefsServer->asn_Network->ann_Network, currentPrefsNetwork->ann_Network)==0)
      {
        prefsNetworkServerCount++;
        if (aircos_Prefs_ServerActive)
          if (strcasecmp(currentPrefsServer->asn_Server, aircos_Prefs_ServerActive->asn_Server)==0)
          {
            setprefsServerActive = prefsNetworkServerCount -1;
          }
      }
  }
 
D(bug("[AiRcOS] updateserverlist_func: %d server nodes for network '%s'\n", prefsNetworkServerCount, currentPrefsNetwork->ann_Network));
 
  if (prefsNetworkServerCount > 0)
  {
    if (server_list != server_list_empty) {
      FreeVec(server_list);
    }
 
    if ((server_list = AllocVec(sizeof(IPTR) * prefsNetworkServerCount+1, MEMF_CLEAR|MEMF_PUBLIC)))
    {
      int loop_count = 0;
 
      ForeachNode(&aircos_Prefs_Servers, currentPrefsServer)
      {
        if (strcasecmp(currentPrefsServer->asn_Network->ann_Network, currentPrefsNetwork->ann_Network)==0)
        {
          server_list[loop_count] = currentPrefsServer->asn_Server;
          loop_count++;
        }
      }
    }
    else
    {
D(bug("[AiRcOS] updateserverlist_func: ERROR - couldnt allocate memory for server name pointer table\n"));
      server_list = server_list_empty;
    }
  } else if (server_list != server_list_empty) {
    FreeVec(server_list);
    server_list = server_list_empty; 
  }
 
  if (!(new_dropbox_server = MUI_MakeObject(MUIO_Cycle, NULL, server_list)))
  {
D(bug("[AiRcOS] updateserverlist_func: Failed to create Server dropdown\n"));
    return NULL;
  }
 
  if (DoMethod(select_dropboxgrp_server, MUIM_Group_InitChange))
  {
    DoMethod(select_dropboxgrp_server, OM_REMMEMBER, select_dropbox_server);
    DoMethod(select_dropboxgrp_server, OM_ADDMEMBER, new_dropbox_server);
    DoMethod(select_dropboxgrp_server, MUIM_Group_ExitChange);
    select_dropbox_server = new_dropbox_server;
  }
 
  DoMethod(select_dropbox_server, MUIM_NoNotifySet, MUIA_Cycle_Active, setprefsServerActive);
 
  AROS_USERFUNC_EXIT
};
 
AROS_UFH3(void, serversave_func,
   AROS_UFHA(struct Hook *, unused_hook, A0),
   AROS_UFHA(APTR, obj, A2),
   AROS_UFHA(struct IRC_Channel_Priv  *, hook_channel_arg, A1 ))
{
   AROS_USERFUNC_INIT
D(bug("[AiRcOS] serversave_func()\n"));
 
  struct aircos_networknode *newsaveNetwork = NULL;
  char                      *newsaveNetwork_name;
  BOOL                      newsaveNetwork_update = FALSE;
 
  struct aircos_servernode  *newsaveServer = NULL;
  char                      *newsaveServer_name;
  BOOL                      newsaveServer_update = FALSE;
 
   get( input_server_network, MUIA_String_Contents, &newsaveNetwork_name);
   get( input_server_address, MUIA_String_Contents, &newsaveServer_name);
 
  if (!(newsaveNetwork = aircosApp_FindServerNetworkNode(newsaveNetwork_name)))
  {
D(bug("[AiRcOS](serversave_func) created new network node for '%s'\n", newsaveNetwork_name));
     newsaveNetwork = AllocVec(sizeof(struct aircos_networknode), MEMF_CLEAR|MEMF_PUBLIC);
     newsaveNetwork->ann_Network = AllocVec(strlen(newsaveNetwork_name)+1, MEMF_CLEAR|MEMF_PUBLIC);
     CopyMem(newsaveNetwork_name, newsaveNetwork->ann_Network, strlen(newsaveNetwork_name)+1);
 
     AddTail((struct List *)&aircos_Prefs_ServerNetworks, (struct Node *)&newsaveNetwork->ann_Node);
     newsaveNetwork_update = TRUE;
  }
  newsaveNetwork->ann_ServerCount += 1;
D(bug("[AiRcOS](serversave_func) %s Network node server count = %d\n", newsaveNetwork->ann_Network, newsaveNetwork->ann_ServerCount));
 
  if (!(newsaveServer = aircosApp_FindServerNode(newsaveServer_name)))
  {
D(bug("[AiRcOS](serversave_func) created new server node for '%s'\n", newsaveServer_name));
     newsaveServer = AllocVec(sizeof(struct aircos_servernode), MEMF_CLEAR|MEMF_PUBLIC);
     newsaveServer->asn_Server = AllocVec(strlen(newsaveServer_name)+1, MEMF_CLEAR|MEMF_PUBLIC);
     CopyMem(newsaveServer_name, newsaveServer->asn_Server, strlen(newsaveServer_name)+1);
     newsaveServer->asn_Network = newsaveNetwork;
     get( input_server_port, MUIA_String_Integer, &newsaveServer->asn_Port);
 
#warning "TODO: we need to store the password also here .."
 
     AddTail((struct List *)&aircos_Prefs_Servers, (struct Node *)&newsaveServer->asn_Node);
     newsaveServer_update = TRUE;
  }
  else
  {
D(bug("[AiRcOS](serversave_func) node already exists for server '%s'\n", newsaveServer_name));
  }
 
  aircos_Prefs_ServerActive = newsaveServer;
 
  set( servermodWin, MUIA_Window_Open, FALSE);
 
  if (newsaveNetwork_update) CallHookPkt(&aircos_networklistupdate_hook, obj, hook_channel_arg);
  if (newsaveServer_update) CallHookPkt(&aircos_serverlistupdate_hook, obj, hook_channel_arg);
 
  AROS_USERFUNC_EXIT
};
 
 
Object	*aircos_showServerConnect()
{
D(bug("[AiRcOS] showServerConnect()\n"));
  if (aircos_Prefs_ServersLoaded)
  {
D(bug("[AiRcOS](showServerConnect) Server windows already configured!\n"));
    return NULL;
  }
 
   Object   *tmp_connectWin = NULL;
 
  NewList((struct List *)&aircos_Prefs_ServerNetworks);
  NewList((struct List *)&aircos_Prefs_Servers);
 
	Object	*butt_addServer = NULL;
	Object	*butt_editServer = NULL;
	Object	*butt_delServer = NULL;
	Object	*butt_serverConnect = NULL;
	Object   *butt_serverSave = NULL;
 
   network_list = network_list_empty;
   server_list = server_list_empty;
 
	if (!(select_dropbox_network = MUI_MakeObject(MUIO_Cycle, NULL, network_list)))
	{
D(bug("[AiRcOS](showServerConnect) Failed to create Network dropdown\n"));
      return NULL;
	}
 
	if (!(select_dropbox_server = MUI_MakeObject(MUIO_Cycle, NULL, server_list)))
	{
D(bug("[AiRcOS](showServerConnect) Failed to create Server dropdown\n"));
      return NULL;
	}
 
   if (!(butt_addServer = SimpleButton("Add")))
	{
D(bug("[AiRcOS](showServerConnect) Failed to create 'ADD' button\n"));
      return NULL;
	}
 
   if (!(butt_editServer = SimpleButton("Edit")))
	{
D(bug("[AiRcOS](showServerConnect) Failed to create 'EDIT' button\n"));
      return NULL;
	}
 
   if (!(butt_delServer = SimpleButton("Del")))
	{
D(bug("[AiRcOS](showServerConnect) Failed to create 'DEL' button\n"));
      return NULL;
	}
 
   if (!(butt_serverConnect = SimpleButton("Connect!")))
	{
D(bug("[AiRcOS](showServerConnect) Failed to create 'CONNECT' button\n"));
      return NULL;
	}
 
   if (!(butt_serverSave = SimpleButton("Save!")))
	{
D(bug("[AiRcOS](showServerConnect) Failed to create 'SAVE' button\n"));
      return NULL;
	}
 
   select_dropboxgrp_network = VGroup,
                                 Child, (IPTR) select_dropbox_network,
                               End;
 
   select_dropboxgrp_server = VGroup,
                                Child, (IPTR) select_dropbox_server,
                              End;
 
	tmp_connectWin = WindowObject,
            MUIA_Window_Title, (IPTR) "Connect to Server..",
            MUIA_Window_Activate, TRUE,
            MUIA_Window_Width,200,
            MUIA_Window_Height,200,
 
            WindowContents, (IPTR) VGroup,
                Child, (IPTR) HGroup,
                  GroupFrame,
					   Child, (IPTR) VGroup,
						  Child, (IPTR) LLabel("IRC Network"),
						  Child, (IPTR) HGroup,
						    Child, (IPTR) HSpace(0),
						    Child, (IPTR) select_dropboxgrp_network,
						    Child, (IPTR) HSpace(0),
						  End,
					   End,
					   Child, (IPTR) HVSpace,
                End,
                Child, (IPTR) HGroup,
                  GroupFrame,
					   Child, (IPTR) VGroup,
						  Child, (IPTR) LLabel("IRC Server"),
						  Child, (IPTR) HGroup,
						    Child, (IPTR) HVSpace,
						    Child, (IPTR) select_dropboxgrp_server,
						    Child, (IPTR) HVSpace,
						  End,
					   End,
  					   Child, (IPTR) HVSpace,
                End,
                Child, (IPTR) HGroup,
					   Child, (IPTR) HVSpace,
					   Child, (IPTR) butt_addServer,
					   Child, (IPTR) butt_editServer,
					   Child, (IPTR) butt_delServer,
                End,
                Child, (IPTR) butt_serverConnect,
            End,
        End;
 
	servermodWin = WindowObject,
 
            MUIA_Window_Title, (IPTR) "Edit Server..",
            MUIA_Window_Activate, TRUE,
            MUIA_Window_Width,350,
            MUIA_Window_Height,400,
 
            WindowContents, (IPTR) VGroup,
               Child, (IPTR) VGroup,
                 GroupFrame,
                 Child, (IPTR) HGroup,
					    Child, (IPTR) LLabel("IRC Server Address"),
					    Child, (IPTR) (input_server_address = StringObject,
										StringFrame,
                              MUIA_CycleChain, TRUE,
										MUIA_String_Format, MUIV_String_Format_Right,
                   End ),
                 End,
                 Child, (IPTR) HGroup,
					    Child, (IPTR) LLabel("Port"),
					    Child, (IPTR) (input_server_port = StringObject,
										StringFrame,
                              MUIA_CycleChain, TRUE,
										MUIA_String_Format, MUIV_String_Format_Right,
										MUIA_String_Accept, "0123456789",
										MUIA_String_Integer, 0,
					    End),
                 End,
                 Child, (IPTR) HGroup,
					    Child, (IPTR) LLabel("Server Description"),
					    Child, (IPTR) (input_server_description = StringObject,
										StringFrame,
                              MUIA_CycleChain, TRUE,
										MUIA_String_Format, MUIV_String_Format_Right,
					    End),
                 End,
                 Child, (IPTR) HGroup,
					    Child, (IPTR) LLabel("IRC Network"),
					    Child, (IPTR) (input_server_network = StringObject,
										StringFrame,
                              MUIA_CycleChain, TRUE,
										MUIA_String_Format, MUIV_String_Format_Right,
					    End),
                 End,
               End,
               Child, (IPTR) VGroup,
                 GroupFrame,
				     Child, (IPTR) LLabel("Enter server password here if applicable."),
                 Child, (IPTR) HGroup,
					    Child, (IPTR) LLabel("Password"),
					    Child, (IPTR) (input_server_pass = StringObject,
										StringFrame,
                              MUIA_CycleChain, TRUE,
										MUIA_String_Format, MUIV_String_Format_Right,
					    End),
                 End,
               End,
               Child, (IPTR) butt_serverSave,
            End,
        End;
 
	if ((tmp_connectWin)&&(servermodWin))
	{
D(bug("[AiRcOS](showServerConnect) Created GUI objects\n"));
      DoMethod
        (
            AiRcOS_Base->aircos_app, OM_ADDMEMBER,
            (IPTR) tmp_connectWin
        );
 
      DoMethod
        (
            AiRcOS_Base->aircos_app, OM_ADDMEMBER,
            (IPTR) servermodWin
        );
 
        DoMethod
        (
            tmp_connectWin, MUIM_Notify, MUIA_Window_Open, TRUE,
            (IPTR) AiRcOS_Base->aircos_quickconnectwin, 3, MUIM_Set, MUIA_Window_Open, FALSE
        );
 
        DoMethod
        (
            AiRcOS_Base->aircos_quickconnectwin, MUIM_Notify, MUIA_Window_Open, TRUE,
            (IPTR) tmp_connectWin, 3, MUIM_Set, MUIA_Window_Open, FALSE
        );
 
        DoMethod
        (
            tmp_connectWin, MUIM_Notify, MUIA_Window_Open, FALSE,
            (IPTR) servermodWin, 3, MUIM_Set, MUIA_Window_Open, FALSE
        );
 
      DoMethod
        (
            butt_addServer, MUIM_Notify, MUIA_Selected, FALSE,
            (IPTR) servermodWin, 3, MUIM_Set, MUIA_Window_Open, TRUE
        );
 
      DoMethod
        (
            butt_editServer, MUIM_Notify, MUIA_Selected, FALSE,
            (IPTR) servermodWin, 3, MUIM_Set, MUIA_Window_Open, TRUE
        );
 
//      DoMethod
//        (
//            butt_delServer, MUIM_Notify, MUIA_Selected, FALSE,
//            (IPTR) servermodWin, 3, MUIM_Set, MUIA_ShowMe, FALSE
//        );
 
        DoMethod
        (
            tmp_connectWin, MUIM_Notify, MUIA_Window_CloseRequest, TRUE,
            (IPTR) tmp_connectWin, 3, MUIM_Set, MUIA_Window_Open, FALSE
        );
 
        DoMethod
        (
            servermodWin, MUIM_Notify, MUIA_Window_CloseRequest, TRUE,
            (IPTR) servermodWin, 3, MUIM_Set, MUIA_Window_Open, FALSE
        );
 
        DoMethod
        (
            butt_serverConnect, MUIM_Notify, MUIA_Pressed, FALSE,
                (IPTR) AiRcOS_Base->aircos_app, 3, MUIM_CallHook, &AiRcOS_Base->aircos_connect_hook, NULL
        );
 
D(bug("[AiRcOS](showServerConnect) prepare list update hooks\n"));
 
       aircos_serversave_hook.h_MinNode.mln_Succ = NULL;
       aircos_serversave_hook.h_MinNode.mln_Pred = NULL;
       aircos_serversave_hook.h_Entry = HookEntry;
       aircos_serversave_hook.h_SubEntry = (void *)serversave_func;
 
       aircos_networklistupdate_hook.h_MinNode.mln_Succ = NULL;
       aircos_networklistupdate_hook.h_MinNode.mln_Pred = NULL;
       aircos_networklistupdate_hook.h_Entry = HookEntry;
       aircos_networklistupdate_hook.h_SubEntry = (void *)updatenetworklist_func;
 
       aircos_serverlistupdate_hook.h_MinNode.mln_Succ = NULL;
       aircos_serverlistupdate_hook.h_MinNode.mln_Pred = NULL;
       aircos_serverlistupdate_hook.h_Entry = HookEntry;
       aircos_serverlistupdate_hook.h_SubEntry = (void *)updateserverlist_func;
 
       aircos_chooseserver_hook.h_MinNode.mln_Succ = NULL;
       aircos_chooseserver_hook.h_MinNode.mln_Pred = NULL;
       aircos_chooseserver_hook.h_Entry = HookEntry;
       aircos_chooseserver_hook.h_SubEntry = (void *)chooseserver_func;
 
 
        DoMethod
        (
            butt_serverSave, MUIM_Notify, MUIA_Pressed, FALSE,
                (IPTR) AiRcOS_Base->aircos_app, 3, MUIM_CallHook, &aircos_serversave_hook, NULL
        );
 
 
        DoMethod
        (
            select_dropbox_network, MUIM_Notify, MUIA_Cycle_Active, MUIV_EveryTime,
                (IPTR) AiRcOS_Base->aircos_app, 3, MUIM_CallHook, &aircos_serverlistupdate_hook, NULL
        );
 
        DoMethod
        (
            select_dropbox_server, MUIM_Notify, MUIA_Cycle_Active, MUIV_EveryTime,
                (IPTR) AiRcOS_Base->aircos_app, 3, MUIM_CallHook, &aircos_chooseserver_hook, NULL
        );
 
D(bug("[AiRcOS](showServerConnect) Configured NOTIFICATIONS\n"));
 
       set(tmp_connectWin, MUIA_Window_Open, TRUE);
       D(bug("[AiRcOS](showServerConnect) Window opened ..\n"));
     }
     else
     {
      if (!(servermodWin))
      {
D(bug("[AiRcOS](showServerConnect) Failed to create server edit window..\n"));
      }
      else MUI_DisposeObject(servermodWin);
 
      if (!(tmp_connectWin))
      {
D(bug("[AiRcOS](showServerConnect) Failed to create server selection window..\n"));      
      }
      else MUI_DisposeObject(tmp_connectWin);
     }
 
	return tmp_connectWin;
}



/*
    Copyright © 2002, The AROS Development Team.
    All rights reserved.
 
    $Id: mini2.c 30792 2009-03-07 22:40:04Z neil $
*/
 
#include <exec/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
 
#include <dos/dos.h>
#include <intuition/gadgetclass.h>
#include <intuition/icclass.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/muimaster.h>
#include <clib/alib_protos.h>
 
/* the following should go in a single include file which then only
** constits of the public constants and members. Actually this is easiey
*/
 
#include <libraries/mui.h>
 
#define DEBUG 1
#include <aros/debug.h>
 
Object *app;
 
int main(void)
{
    Object *wnd;
    static char *radio_entries2[] = {"Paris","London",NULL};
 
    app = ApplicationObject,
   	SubWindow, wnd = WindowObject,
    	    WindowContents, VGroup,
	Child, HGroup,
	    MUIA_InputMode, MUIV_InputMode_Immediate,
/*              MUIA_ShowSelState, FALSE, */
	    Child, ImageObject,
                MUIA_ShowSelState, FALSE,
	        MUIA_Image_FontMatch, TRUE,
	        MUIA_Image_Spec, MUII_RadioButton,
	        MUIA_Frame, MUIV_Frame_None,
   	        End,
	    Child, TextObject,
                MUIA_ShowSelState, FALSE,
	        MUIA_Text_Contents, "London",
	        MUIA_Frame, MUIV_Frame_None,
	        MUIA_Text_PreParse, "\33l",
	        End,
	End,
		End,
	    End,
	End;
 
    if (app)
    {
	ULONG sigs = 0;
/*  #if 0 */
	DoMethod(wnd, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, app, 2, MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
 
/*  #endif */
	set(wnd, MUIA_Window_Open, TRUE);
 
/*  #if 0 */
	while((LONG) DoMethod(app, MUIM_Application_NewInput, &sigs) != MUIV_Application_ReturnID_Quit)
	{
	    if (sigs)
	    {
		sigs = Wait(sigs | SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D);
		if (sigs & SIGBREAKF_CTRL_C) break;
		if (sigs & SIGBREAKF_CTRL_D) break;
	    }
	}
/*  #endif */
	set(wnd, MUIA_Window_Open, FALSE);
	MUI_DisposeObject(app);
    }
 
    return 0;
}


/*
    Copyright © 2002, The AROS Development Team.
    All rights reserved.
 
    $Id: dirlist.c 30792 2009-03-07 22:40:04Z neil $
*/
 
#include <exec/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
 
#include <dos/dos.h>
#include <intuition/gadgetclass.h>
#include <intuition/icclass.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/muimaster.h>
#include <clib/alib_protos.h>
 
/* the following should go in a single include file which then only
** constits of the public constants and members. Actually this is easiey
*/
 
#include <libraries/mui.h>
 
struct Library *MUIMasterBase;
 
Object *app;
 
int main(void)
{
    Object *wnd, *str, *dirlist, *page;
 
    MUIMasterBase = (struct Library*)OpenLibrary("muimaster.library",0);
 
    app = ApplicationObject,
   	SubWindow, wnd = WindowObject,
    	    MUIA_Window_Title, "dirlist",
	    MUIA_Window_Activate, TRUE,
 
    	    WindowContents, VGroup,
	    	MUIA_Background, MUII_GroupBack,
		Child, ListviewObject,
		    MUIA_Listview_List, dirlist = DirlistObject,
    	    	    	InputListFrame,
		    	End,
		    End,
		Child, HGroup,
		    Child, str = StringObject,
			StringFrame,
			MUIA_String_Contents, (IPTR)"SYS:",
			End,
		    Child, page = PageGroup,
		    	MUIA_Weight, 0,
			MUIA_FixWidthTxt, (IPTR)"AA",
		    	Child, ColorfieldObject,
			    MUIA_Colorfield_Red, 0xFFFFFFFF,
			    MUIA_Colorfield_Green, 0,
			    MUIA_Colorfield_Blue, 0,
			    End,
		    	Child, ColorfieldObject,
			    MUIA_Colorfield_Red, 0xFFFFFFFF,
			    MUIA_Colorfield_Green, 0xFFFFFFFF,
			    MUIA_Colorfield_Blue, 0,
			    End,
		    	Child, ColorfieldObject,
			    MUIA_Colorfield_Red, 0,
			    MUIA_Colorfield_Green, 0x66666666,
			    MUIA_Colorfield_Blue, 0,
			    End,
		    	End,
		    End,
		End,
	    End,
	End;
 
    if (app)
    {
	ULONG sigs = 0;
 
	DoMethod
        (
            wnd, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, (IPTR) app, 
            2, MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit
        );
 
    	DoMethod(str, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime,
	    	 (IPTR)dirlist, 3, MUIM_Set, MUIA_Dirlist_Directory, MUIV_TriggerValue);
 
    	DoMethod(dirlist, MUIM_Notify, MUIA_Dirlist_Status, MUIV_EveryTime,
	    	 (IPTR)page, 3, MUIM_Set, MUIA_Group_ActivePage, MUIV_TriggerValue);
 
    	set(dirlist, MUIA_Dirlist_Directory, "SYS:");
 
	set(wnd,MUIA_Window_Open,TRUE);
 
	while (DoMethod(app, MUIM_Application_NewInput, (IPTR) &sigs) != MUIV_Application_ReturnID_Quit)
	{
	    if (sigs)
	    {
		sigs = Wait(sigs | SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D);
		if (sigs & SIGBREAKF_CTRL_C) break;
		if (sigs & SIGBREAKF_CTRL_D) break;
	    }
	}
 
	MUI_DisposeObject(app);
    }
 
    CloseLibrary(MUIMasterBase);
 
    return 0;
}


WaitSelect() needs a pointer to your signal mask. So you need to do something like:

sigs |= SIGBREAKF_CTRL_C;
ret = WaitSelect(s+1, &rdfs, NULL, NULL, &sigs);

Then 'sigs' should contain the bits normally returned by Wait().


/*
 * Amiga Generic Set - set of libraries and includes to ease sw development for all Amiga platforms
 * Copyright (C) 2004-2008 Tomasz Wiszkowski Tomasz.Wiszkowski at gmail.com.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
 
#include "MUIPictureClass.h"
#include <Generic/LibrarySpool.h>
#include <libclass/intuition.h>
#include <libclass/muimaster.h>
#include <libraries/mui.h>
#include <libclass/utility.h>
 
using namespace GenNS;
 
#define __NOGLOBALIFACE__
#include <proto/graphics.h>
 
   static struct Library *GfxBase = 0;
#ifdef __AMIGAOS4__
   static struct GraphicsIFace *IGraphics = 0;
#endif
 
   MUICustomClassT<MUIPictureClass> *MUIPictureClass::muiclass = 0;
   static int            openCount = 0;
 
MUIPictureClass::MUIPictureClass(IClass *cls)
{
   openCount++;
   parent   = cls;
   image1   = "";
   image2   = "";
   width    = 64;
   height   = 48;
   dtimg1   = 0;
   dtimg2   = 0;
   dt       = DatatypesIFace::GetInstance(44);
   if (dt == 0)
   {
      request("Warning", "This program requires datatypes.library v44 or later\nGraphical buttons will not be displayed.", "Continue", 0);
   }
 
   if (GfxBase == 0)
   {
      GfxBase = Exec->OpenLibrary("graphics.library", 39);
   }
#ifdef __AMIGAOS4__
   if ((GfxBase != 0) && (IGraphics == 0))
   {
      IGraphics = (GraphicsIFace*)Exec->GetInterface(GfxBase, "main", 1, (TagItem*)NULL);
   }
#endif
}
 
MUIPictureClass::~MUIPictureClass()
{
   closeImages();
   dt->FreeInstance();
   openCount--;
   if (0 == openCount)
   {
#ifdef __AMIGAOS4__
      Exec->DropInterface((Interface*)IGraphics);
      IGraphics = 0;
#endif
      Exec->CloseLibrary(GfxBase);
      GfxBase = 0;
   }
}
 
iptr MUIPictureClass::DoMtd(iptr *obj, iptr *msg)
{
   uint16  *minmax;
   iptr      k;
 
   switch (msg[0]) 
   {
      case OM_NEW:
         {
            if (!(obj = (Object*)DoSuperMtd(parent, obj, msg))) 
               return 0;
 
            k = (iptr)Utility->GetTagData(MUIA_Picture_NormalImage, 0, (TagItem*)msg[1]);
            if (k != 0)
               image1 = (char*)k;
            k = (iptr)Utility->GetTagData(MUIA_Picture_SelectedImage, 0, (TagItem*)msg[1]);
            if (k != 0)
               image2 = (char*)k;
 
            isDisabled = Utility->GetTagData(MUIA_Disabled, 0, (struct TagItem*)msg[1]) ? true : false;
            isSelected = Utility->GetTagData(MUIA_Selected, 0, (struct TagItem*)msg[1]) ? true : false;
 
            openImages();
 
            return (ULONG)obj;
         }
         break;
 
      case OM_DISPOSE:
      case MUIM_Hide:
         break;
 
      case MUIM_Show:
         {
            if (dt != 0)
            {
               if (dtimg1 != 0)
               {
                  dt->SetDTAttrsA(dtimg1, 0, 0, (TagItem*)ARRAY(
                     PDTA_Screen,         (iptr)_screen(obj), 
                     PDTA_DestMode,       PMODE_V43, 
                     PDTA_UseFriendBitMap,true,
                     TAG_DONE,            0));
 
                  dt->DoDTMethodA(dtimg1, 0, 0, ARRAY(DTM_PROCLAYOUT, 0, 1));
               }
               if (dtimg2 != 0)
               {
                  dt->SetDTAttrsA(dtimg2, 0, 0, (TagItem*)ARRAY(
                     PDTA_Screen,         (iptr)_screen(obj), 
                     PDTA_DestMode,       PMODE_V43, 
                     PDTA_UseFriendBitMap,true,
                     TAG_DONE,            0));
 
                  dt->DoDTMethodA(dtimg2, 0, 0, ARRAY(DTM_PROCLAYOUT, 0, 1));
               }
            }
         }
         break;
 
      case MUIM_AskMinMax:
         {
            DoSuperMtd(parent, obj, msg);
 
            minmax = (uint16*)msg[1];
            minmax[0] = width;
            minmax[2] = width;
            minmax[4] = width;
            minmax[1] = height;
            minmax[3] = height;
            minmax[5] = height;
         }
         return 0;
 
      case OM_SET:
         {
            bool flg;
            bool refresh = false;
//            image1 = (char*)GetTagData(MUIA_Picture_NormalImage,   (int32)image1.Data(), (TagItem*)msg[1]);
//            image2 = (char*)GetTagData(MUIA_Picture_SelectedImage, (int32)image2.Data(), (TagItem*)msg[1]);
            flg = Utility->GetTagData(MUIA_Disabled, isDisabled, (struct TagItem*)msg[1]) ? true : false;
            if (flg != isDisabled)
            {
               isDisabled = flg;
               refresh = true;
            }
 
            flg = Utility->GetTagData(MUIA_Selected, isSelected, (struct TagItem*)msg[1]) ? true : false;
            if (isSelected != flg)
            {
               isSelected = flg;
               refresh = true;
            }
 
            if (refresh)
               MUIMaster->MUI_Redraw(obj, 0);
         }
         break;
 
      case MUIM_NoNotifySet:
      case MUIM_Set:
         {
            bool refresh = false;
            if (msg[1] == MUIA_Picture_NormalImage)
               image1 = (char*)msg[2];
 
            if (msg[1] == MUIA_Picture_SelectedImage)
               image2 = (char*)msg[2];
 
            if (msg[1] == MUIA_Disabled)
            {
               bool flg = msg[2] ? true : false;
               if (flg != isDisabled)
               {
                  isDisabled = flg;
                  refresh = true;
               }
            }
 
            if (msg[1] == MUIA_Selected)
            {
               bool flg = msg[2] ? true : false;
               if (flg != isSelected)
               {
                  refresh = true;
                  isSelected = flg;
               }
            }
            if (refresh)
               MUIMaster->MUI_Redraw(obj, 0);
         }
         break;
 
      case MUIM_Draw:
         {
            Object* o = 0;
            BitMap* bitmap = 0;
            void*   drawinfo = 0;
 
            if (0 == _rp(obj)) 
               break;
 
            if ((isSelected) && (dtimg2 != 0))
               o = dtimg2;
            else if (dtimg1 != 0)
               o = dtimg1;
            else
               break;
 
 
            drawinfo = dt->ObtainDTDrawInfoA(o, (TagItem*)ARRAY(
               PDTA_Screen,   (iptr)_screen(obj),
               TAG_DONE,      0));
 
            if (drawinfo != 0)
            {
               dt->DrawDTObjectA(
                  _rp(obj),
                  o,
                  _mleft(obj),
                  _mtop(obj),
                  width,
                  height,
                  0,
                  0,
                  0);
               dt->ReleaseDTDrawInfo(o, drawinfo);
            }
            else
            {
               dt->GetDTAttrsA(o, (TagItem*)ARRAY(
                        PDTA_DestBitMap,     (iptr)&bitmap,
                        TAG_DONE,            0));
               if ((0 != bitmap) && (0 != GfxBase))
               {
#ifndef __amigaos4
                  BltBitMapRastPort(bitmap, 0, 0, _rp(obj), _mleft(obj), _mtop(obj), width, height, 0xc0);
#else
                  IGraphics->BltBitMapRastPort(bitmap, 0, 0, _rp(obj), _mleft(obj), _mtop(obj), width, height, 0xc0);
#endif
               }
            }
         }
         return 0;
 
   }
   return DoSuperMtd(parent, obj, msg);
}
 
void MUIPictureClass::openImages()
{
   if (dt == 0)
      return;
 
   closeImages();
 
 
   dtimg1 = dt->NewDTObjectA(image1.Data(), (TagItem*)ARRAY(
            DTA_GroupID,      (iptr)GID_PICTURE,
            PDTA_Remap,       true,
            OBP_Precision,    (iptr)PRECISION_EXACT,
            TAG_DONE,         0));
 
   dtimg2 = dt->NewDTObjectA(image2.Data(), (TagItem*)ARRAY(
            DTA_GroupID,      (iptr)GID_PICTURE,
            PDTA_Remap,       true,
            OBP_Precision,    (iptr)PRECISION_EXACT,
            TAG_DONE,         0));
 
   width  = 0x7fff;
   height = 0x7fff;
 
   if (dtimg1 != 0)
   {
      BitMapHeader *bmhd;
 
      dt->GetDTAttrsA(dtimg1, (TagItem*)ARRAY(
         PDTA_BitMapHeader,   (iptr)&bmhd,
         TAG_DONE,            0));
 
      width  = width  <? bmhd->bmh_Width;
      height = height <? bmhd->bmh_Height;
   }
 
   if (dtimg2 != 0)
   {
      BitMapHeader *bmhd;
 
      dt->GetDTAttrsA(dtimg1, (TagItem*)ARRAY(
         PDTA_BitMapHeader,   (iptr)&bmhd,
         TAG_DONE,            0));
 
      width  = width  <? bmhd->bmh_Width;
      height = height <? bmhd->bmh_Height;
   }
 
   if ((width == 0x7fff) && (height == 0x7fff))
   {
      width = 64;
      height = 48;
   }
}
 
void MUIPictureClass::closeImages()
{
   if (dt != 0)
   {
      if (dtimg1 != 0)
      {
         dt->DisposeDTObject(dtimg1);
      }
      if (dtimg2 != 0)
      {
         dt->DisposeDTObject(dtimg2);
      }
   }
   width    = 64;
   height   = 48;
   dtimg1   = 0;
   dtimg2   = 0;
}
 
iptr* MUIPictureClass::CreateClass()
{
    if (MUIPictureClass::muiclass == 0)
    {
	MUIPictureClass::muiclass = new MUICustomClassT<MUIPictureClass>(MUIC_Area);
    }
    return (iptr*)MUIPictureClass::muiclass;
}
 
void MUIPictureClass::DestroyClass()
{
    MUICustomClassT<MUIPictureClass> *p = MUIPictureClass::muiclass;
    MUIPictureClass::muiclass = 0;
    delete p;
}


Check out #mui on irc.freenode.net and report bugs/problems there and MUI Yahoo Group