OPT LARGE
OPT PREPROCESS,OSVERSION=39

#define PlaneFuncObject NewObjectA(cl_planefunc.mcc_class,NIL,[TAG_IGNORE,0
#define ProjectListObject NewObjectA(cl_projectlist.mcc_class,NIL,[TAG_IGNORE,0
#define uSliderObject NewObjectA(cl_uslider.mcc_class,NIL,[TAG_IGNORE,0
#define SizeTextObject NewObjectA(cl_sizetxt.mcc_class,NIL,[TAG_IGNORE,0
#define ProjectholdObject NewObjectA(cl_projecthold.mcc_class,NIL,[TAG_IGNORE,0
#define MyApplicationObject NewObjectA(cl_myapp.mcc_class,NIL,[TAG_IGNORE,0
/*****/
#define VarListObject NewObjectA(varlist_mcc,NIL,[MUIA_List_Format,'MIW=50 BAR,',\
                                                  MUIA_List_ConstructHook,vl_constructhook,\
                                                  MUIA_List_DestructHook,vl_destructhook,\
                                                  MUIA_List_DisplayHook,vl_displayhook

#define label(t) TextObject,MUIA_Text_Contents,t,MUIA_Text_SetMax,MUI_TRUE,End

#define modt(val) val AND $FF
#define pixel(raw,w,x,y) raw[x+(y*w)]

#define PICVERSION 'PicFX 1.5'
#define FULLVERSION '$VER: PicFX 1.5 (02/06/00)'

->MODULE 'afc/super_picture','afc/trueBitmap'
MODULE       'amigalib/boopsi','amigalib/lists',
       'asl',
       'datatypes','datatypes/datatypes','datatypes/pictureclass','datatypes/pictureclassext','datatypes/datatypesclass',
       'devices/timer',
       'dos/dos','dos/dosextens','dos/dostags',
       'exec/io','exec/lists','exec/memory','exec/nodes','exec/ports','exec/semaphores','exec/tasks',
       'fork',
       'graphics/gfx','graphics/rastport','graphics/view','guigfx','guigfx/guigfx',
       'iffparse','libraries/iffparse',
       'intuition/classes','intuition/classusr','intuition/intuition','intuition/screens',
       'libraries/asl','libraries/cybergraphics','libraries/gadtools','libraries/iffparse','libraries/mui',
           'libraries/muip',
       'mui/lamp_mcc','mui/muicustomclass',
       'muimaster',
       '*varlist',
       'utility','utility/tagitem','utility/hooks','tools/installhook',
       '*parser','tools/longreal',
       'tools/ilbmdefs',
       '*picFXlocale',
       'render/render',
       '*classes'

->MODULE 'tools/debug' ->kputfmt(formatstring,[values])

CONST TICKS_PER_MINUTE = TICKS_PER_SECOND*60

DEF app,
    messages_win,messages_lst,
    about_win,abouttext,
    prefs_win, /*Preference button*/
        mainpri_sld,renderpri_sld,renderupdate_sld,mui_btn,currpri_sld,
        recompile_chk,
        close_cyc,showmess_chk,dither_cyc,dither_sld,savep_btn,
    settings_win,colour_txt,
    menus, -> First a newmenu object and later an mui object :-)
    opened_lsv,opened_lst,
    new_btn,prefs_btn,var_btn,
    varlist_win,varlist_lsv,varlist_lst,varname_str,varval_str,varadd_btn,vardel_btn,
    sgroup,     /*settings gadgets*/ -> sgroup is the group with the gadgets,
    egroup,                          -> and egroup, when non NIL, is a stupid text object replacing that group
    stat_grp,                        -> this the group that goes below ?group...
    name_str,type_reg,
        path_str,size_rad,size_txt, -> for the DTPic type
        r_lbl,g_lbl,b_lbl,
        red_str,green_str,blue_str,outr_cyc,outg_cyc,outb_cyc, -> for the Function type
        red_fail,green_fail,blue_fail,
    width_str,height_str,
            /*action buttons*/
    draw_btn,savepic_btn,savefunc_btn,abort_btn,showme_cyc,close_btn,
    loadfunc_btn,clear_btn,
            /*state area*/
    lamp_lmp,state_txt,state_gau,

    ftype_registertitles,size_radiolabels,showme_cyclelabels,
            /*animation stuff*/
    animate_win,parameter_str,frames_str,
        render_lst,render_lsv,
        insertP_btn,delP_btn,
        saveP,savename_str,
        doAnim_btn,
        animation_helptext,
        animstatus_txt,
    sigs=0,
    lastid=0,/* lastid is a global variable incremented everytime a project is opened.
                absolutely no risk to have several projects with same ID*/
    screentitle,
    title[15]:STRING,counter=1, -> number of messages
    messages[15]:ARRAY OF LONG,
    picdir:PTR TO CHAR,funcdir:PTR TO CHAR,

    cat:PTR TO catalog_picFX,

    rp=-2,cp=-1,        -> Priorities
    rupd=10,wclose,dithm,ditha,     -> Some preference variables..

->    psema:ss,           -> Semaphore for preferences  (!!)

    currid=-1,quiet=FALSE     -> currid contains the id of the currently selected project. Quiet controls attributes refreshing.

DEF nodraw,friend=NIL

ENUM MENU_G_About=1,MENU_G_AboutMui, -> Not very oo-style but compact :-) These are the IDs of the menus.
     MENU_G_Prefs,MENU_G_PrefsMui,
     MENU_G_New,MENU_G_Animation,
     MENU_G_Varlist,
     MENU_G_Hide,

     MENU_Project,
     MENU_P_Render,MENU_P_Abort,
     MENU_P_Visible,MENU_P_Close,
     MENU_P_SaveP,MENU_P_Clear,
     MENU_P_LoadF,MENU_P_SaveF

DEF pl_displayer:PTR TO hook

DEF cl_planefunc:PTR TO mui_customclass
DEF cl_projectlist:PTR TO mui_customclass
DEF cl_uslider:PTR TO mui_customclass
DEF cl_sizetxt:PTR TO mui_customclass
DEF cl_projecthold:PTR TO mui_customclass
DEF cl_myapp:PTR TO mui_customclass

ENUM STC_STARTUP=-2,
    STC_SHUTDOWN,      /*messages to the subtask*/
    STC_START,
    STC_PAUSE,
    STC_RESTART,
    STC_CHANGEDONE

/***************************************************************
**            Custom PlaneFunc private Class                  **
****************************************************************
This class is responsible for rendering the functions. It uses a
subtask to avoid the main program to be freezed during rendering
***************************************************************/

OBJECT empty_data -> class(es) that doesn't use any data
    nothing
ENDOBJECT

OBJECT projectList_data
/* Stuff for timer (title bar) */
/* Everything is copied from mui demo "inputhandler" Thanks for its authors ;-) */
    port:PTR TO mp
    req:PTR TO timerequest
    ihnode:mui_inputhandlernode_timer
ENDOBJECT

OBJECT uslider_data
    buffer:PTR TO CHAR
    stringify
ENDOBJECT

OBJECT args_compare -> MUIM_uSlider_Bigger/Smaller
    id
    than
ENDOBJECT

OBJECT args_abort -> Used by MUIM_PlaneFunc_Abort
    id
    reason
ENDOBJECT

OBJECT args_stp -> MUIM_PlaneFunc_SetTaskPri
    id
    value
ENDOBJECT

OBJECT args_savepic
    id
    filegiven
    filename
ENDOBJECT

OBJECT args_notify -> Used by MUIM_ProjectList_Notify
    id
    gadget -> MUIV_ProjectList_Notify_<something>
    value
ENDOBJECT

OBJECT args_showme -> Used by MUIM_ProjectList_ShowMe
    id
    value
ENDOBJECT

OBJECT args_forward -> Used by MUIM_ProjectList_Forward
    id
    cnt
    method
    args[10]:ARRAY OF LONG -> We've got to give a number, so I put 10 :-)
ENDOBJECT

OBJECT args_prefs -> MUIM_ProjectList_Prefs
    id
    var:PTR TO LONG
    value
ENDOBJECT

OBJECT args_refresh -> Used by MUIM_ProjectList_Refresh
    id
    ostate -> TRUE if only the 'state' field must be refreshed.
ENDOBJECT

OBJECT args_setpri -> MUIM_ProjectList_SetPri
    id
    task -> MUIV_ProjectList_SetPri_*
    value
ENDOBJECT

OBJECT args_iil
    id
    list
ENDOBJECT

OBJECT projecthold_data
    current:PTR TO planeFunc_data
    animating -> this is TRUE when animation is being rendered
/*other animation data. Available only during render!*/
    line:LONG             -> This is the line of the list that is being worked on
    varname:PTR TO CHAR   -> time parameter name
    frameCnt:LONG         -> maximum value (float!)
    filenamebase:PTR TO CHAR -> for the saved pictures
ENDOBJECT

/*--------------*/
/* A little tool to help reading IFF chunks from any version */
OBJECT chunkSeeker
    data:PTR TO LONG
    size
    shift -> this how far we are in data...
ENDOBJECT

PROC create(sp:PTR TO storedproperty) OF chunkSeeker
    self.data:=sp.data
    self.size:=sp.size
ENDPROC

PROC read(object,attribute) OF chunkSeeker
DEF dt:PTR TO LONG
    IF self.shift > self.size THEN RETURN

    dt:=self.data+self.shift
    set(object,attribute,dt[])
    self.shift:=self.shift+4
ENDPROC

PROC write(object,attribute) OF chunkSeeker
DEF dt:PTR TO LONG
    IF self.shift > self.size THEN RETURN
    dt:=self.data
    get(object,attribute,dt+self.shift)
    self.shift:=self.shift+4
ENDPROC
/*--------------*/
PROC planeFunc_dispatcher(cl:PTR TO iclass,obj,msg:PTR TO msg)
DEF methodid
    methodid:=msg.methodid
    SELECT methodid
    CASE OM_NEW;        RETURN planeFunc_new(cl,obj,msg)
    CASE OM_DISPOSE;    RETURN planeFunc_dispose(cl,obj,msg)
    CASE OM_GET;        RETURN planeFunc_get(cl,obj,msg)   /*gets an attribute*/
    CASE OM_SET;        RETURN planeFunc_set(cl,obj,msg)   /*sets an attribute*/
    CASE MUIM_AskMinMax;RETURN planeFunc_askminmax(cl,obj,msg)
    CASE MUIM_Cleanup;  RETURN planeFunc_cleanup(cl,obj,msg)
    CASE MUIM_Draw;     RETURN planeFunc_draw(cl,obj,msg)  /*mui (or the subtask) asked to redraw*/
    CASE MUIM_HandleInput;RETURN planeFunc_handleinput(cl,obj,msg)
    CASE MUIM_Setup;    RETURN planeFunc_setup(cl,obj,msg)

    CASE MUIM_PlaneFunc_Abort;      RETURN planeFunc_abort(cl,obj,msg)      /*public methods...*/
    CASE MUIM_PlaneFunc_Clear;      RETURN planeFunc_clear(cl,obj/*,msg*/)
    CASE MUIM_PlaneFunc_Close;      RETURN planeFunc_close(cl,obj/*,msg*/)
    CASE MUIM_PlaneFunc_DrawHandle; RETURN planeFunc_drawhandle(cl,obj,msg)
    CASE MUIM_PlaneFunc_Lamps;      RETURN planeFunc_lamps(cl,obj/*,msg*/)
    CASE MUIM_PlaneFunc_LoadFunc;   RETURN planeFunc_loadfunc(cl,obj/*,msg*/)
    CASE MUIM_PlaneFunc_Render;     RETURN planeFunc_render(cl,obj/*,msg*/)
    CASE MUIM_PlaneFunc_SaveFunc;   RETURN planeFunc_savefunc(cl,obj/*,msg*/)
    CASE MUIM_PlaneFunc_SavePic;    RETURN planeFunc_savepic(cl,obj,msg)
    CASE MUIM_PlaneFunc_SetSize;    RETURN planeFunc_setsize(cl,obj/*,msg*/)
    CASE MUIM_PlaneFunc_SetTaskPri; RETURN planeFunc_settaskpri(cl,obj,msg)
    CASE MUIM_PlaneFunc_Update;     RETURN planeFunc_update(cl,obj/*,msg*/)
    ENDSELECT
ENDPROC doSuperMethodA(cl,obj,msg)

PROC planeFunc_new(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg)
DEF data:PTR TO planeFunc_data

    IF (obj:=doSuperMethodA(cl,obj,msg)) = NIL THEN RETURN NIL

    data:=INST_DATA(cl,obj)
    data.self:=obj

    data.app:=TRUE
    InitSemaphore(data.sema)

    data.projectid:=lastid++
    data.failure:=$000
    data.rstr:=String(256)
    data.gstr:=String(256)
    data.bstr:=String(256)
    StrCopy(data.rstr,'x')
    StrCopy(data.gstr,'y')
    StrCopy(data.bstr,'(x+y)/2')
    data.outputr:=OUT_Integer
    data.outputg:=OUT_Integer
    data.outputb:=OUT_Integer

    NEW data.rfunc.create(data.outputr,data.rstr)
    NEW data.gfunc.create(data.outputg,data.gstr)
    NEW data.bfunc.create(data.outputb,data.bstr)
    data.imagefile:=String(512)
    data.imagefilename:=String(512)

    data.height:=256
    data.loadm:=MUIV_PlaneFunc_LoadM_CutTile
    data.lock:=0
    StrCopy(data.imagefile,cat.msg_Unnamed.getstr())
    data.modified:=0
    data.newleft:=-1
    data.name:=String(32)
    StrCopy(data.name,cat.msg_Unnamed.getstr())
    data.paused:=FALSE
    data.ratio:=100000
    data.newtop:=-1
    data.type:=MUIV_PlaneFunc_Type_Graph
    data.width:=256

    data.xmin:=10
    data.xmax:=240
    data.ymin:=30
    data.ymax:=80

    data.imagedata:=AllocMem(data.width*data.height*4,MEMF_CLEAR OR MEMF_PUBLIC);data.vec:=TRUE
    data.picture:=MakePictureA(data.imagedata,data.width,data.height,[GGFX_PixelFormat,PIXFMT_0RGB_32,TAG_DONE])

ENDPROC obj
PROC planeFunc_askminmax(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO muip_askminmax)
DEF minmax:PTR TO mui_minmax,data:PTR TO planeFunc_data
    data:=INST_DATA(cl,obj)
    doSuperMethodA(cl,obj,msg)
    minmax:=msg.minmaxinfo
    minmax.maxwidth:=minmax.maxwidth+MUI_MAXMAX
    minmax.maxheight:=minmax.maxheight+MUI_MAXMAX
    minmax.defwidth:=minmax.defwidth+(data.width)
    minmax.defheight:=minmax.defheight+(data.height)
ENDPROC 0

PROC planeFunc_setup(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg)

    IF doSuperMethodA(cl,obj,msg)=NIL THEN RETURN FALSE
    Mui_RequestIDCMP(obj,IDCMP_MOUSEBUTTONS)

    ->Mui_RequestIDCMP(obj,IDCMP_RAWKEY)
ENDPROC MUI_TRUE

PROC planeFunc_cleanup(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg)
DEF data:PTR TO planeFunc_data
    Mui_RejectIDCMP(obj,IDCMP_MOUSEBUTTONS)
    ->Mui_RejectIDCMP(obj,IDCMP_RAWKEY)

    data:=INST_DATA(cl,obj)
    IF data.drawhandle
        ReleaseDrawHandle(data.drawhandle);data.drawhandle:=NIL
    ENDIF

ENDPROC doSuperMethodA(cl,obj,msg)

PROC planeFunc_handleinput(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO muip_handleinput)
#define _between(a,x,b) (((x)>=(a)) AND ((x)<=(b)))
DEF data:PTR TO planeFunc_data,col,colourstr[30]:STRING,x,y

    data:=INST_DATA(cl,obj)
    IF msg.imsg
        IF (msg.imsg.class=IDCMP_MOUSEBUTTONS) AND (msg.imsg.code=SELECTDOWN)
            _mwidth(obj)*(data.width-data.left)/Div(Mul(_mwidth(obj),100000),data.ratio)
            IF (_between(_mleft(obj),msg.imsg.mousex,_mright(obj)) AND _between(_mtop(obj),msg.imsg.mousey,_bottom(obj)))
                x:=Div(Mul(msg.imsg.mousex-_mleft(obj),100000),data.ratio)+data.left
                y:=Div(Mul(msg.imsg.mousey-_mtop(obj),100000),data.ratio)+data.top
                IF (x>=data.width) OR (y>=data.height) THEN RETURN doSuperMethodA(cl,obj,msg)
                /*get colour*/
                ObtainSemaphore(data.sema)
                /*IF cybergfxbase
                    col:=ReadRGBPixel(data.rp,x,y)
                ELSE
                    col:=Pi96ReadPixel(data.rp,x,y)
                ENDIF*/
                IF data.imagedata THEN
                    col:=pixel(data.imagedata,data.width,x,y)
                ReleaseSemaphore(data.sema)
                StringF(colourstr,'c(\d,\d,\d)=[\d,\d,\d]',data.projectid,x,y,Shr(col,16),Shr(col AND $FF00,8),col AND $FF)
                set(colour_txt,MUIA_Text_Contents,colourstr)
            ENDIF
/*        ELSEIF (msg.imsg.class=IDCMP_RAWKEY)
            IF (msg.imsg.code=96) OR (msg.imsg.code=97)
                data.shift:=TRUE
            ELSEIF (msg.imsg.code=224) OR (msg.imsg.code=225)
                data.shift:=FALSE
            ENDIF*/
        ENDIF
    ENDIF
ENDPROC doSuperMethodA(cl,obj,msg)

PROC planeFunc_clear(cl:PTR TO iclass,obj:PTR TO object/*,msg:PTR TO msg*/)
DEF data:PTR TO planeFunc_data
    data:=INST_DATA(cl,obj)
    ObtainSemaphore(data.sema)
    IF data.lock=0
        DeletePicture(data.picture)
        data.imagedata:=AllocMem(data.width*data.height*4,MEMF_CLEAR OR MEMF_PUBLIC);data.vec:=TRUE
        data.picture:=MakePictureA(data.imagedata,data.width,data.height,[GGFX_PixelFormat,PIXFMT_0RGB_32,TAG_DONE])

        ReleaseSemaphore(data.sema)
        Mui_Redraw(obj,MADF_DRAWUPDATE)
    ELSE
        ReleaseSemaphore(data.sema)
        message(data.projectid,cat.msgm_WriteVsLock.getstr())
    ENDIF
ENDPROC

PROC planeFunc_drawhandle(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg)
DEF data:PTR TO planeFunc_data,window
    data:=INST_DATA(cl,obj)

    IF data.drawhandle
        ReleaseDrawHandle(data.drawhandle);data.drawhandle:=NIL
    ENDIF
    get(obj,MUIA_Window,{window})
    IF window
        data.drawhandle:=odhandle(obj)
        Mui_Redraw(obj,MADF_DRAWUPDATE)
    ENDIF
ENDPROC

PROC odhandle(obj:PTR TO object)
DEF dm,id
    SELECT dithm
        CASE 0; dm:=DITHERMODE_NONE
        CASE 1; dm:=DITHERMODE_EDD
        CASE 2; dm:=DITHERMODE_FS
        CASE 3; dm:=DITHERMODE_RANDOM
        ENDSELECT

        id:=GetVPModeID(_screen(obj).viewport)

        RETURN ObtainDrawHandleA(NIL,_window(obj).rport,_window(obj).wscreen.viewport.colormap,[
        GGFX_DitherMode,dm,
        GGFX_DitherAmount,ditha,
        GGFX_ModeID,id,

        TAG_DONE])
ENDPROC

PROC planeFunc_close(cl:PTR TO iclass,obj:PTR TO object/*,msg:PTR TO msg*/)
DEF data:PTR TO planeFunc_data,kdata:PTR TO projectList_data
DEF state,count=0,i,d:PTR TO planeFunc_data,win
    data:=INST_DATA(cl,obj)
    get(obj,MUIA_P_WindowObject,{win})
    IF wclose=0
        set(win,MUIA_Window_Open,FALSE)
        IF data.projectid=currid
            nnset(showme_cyc,MUIA_Cycle_Active,0) -> update cycle display.
            i:=doMethodA(menus,[MUIM_FindUData,MENU_P_Visible])
            nnset(i,MUIA_Menuitem_Checked,FALSE)
        ENDIF
    ELSEIF wclose=1
quiet:=TRUE
        IF data.modified <> 0
            i:=String(StrLen(cat.msgr_Remove.getstr())+StrLen(cat.msgr_Cancel.getstr())+1)
            StringF(i,'\s|\s',cat.msgr_Remove.getstr(),cat.msgr_Cancel.getstr())
            IF Mui_RequestA(app,data.win,0,cat.msgr_RemProject.getstr(),i,cat.msgr_RemoveLong.getstr(),[data.projectid,data.name])<>1
                DisposeLink(i)
                quiet:=FALSE
                RETURN
            ENDIF
            DisposeLink(i)
        ENDIF
        set(win,MUIA_Window_Open,FALSE)
        kdata:=INST_DATA(cl_projectlist.mcc_class,opened_lst)
        doMethodA(obj,[MUIM_PlaneFunc_Abort,MUIV_PlaneFunc_Abort_Abort])
        get(obj,MUIA_PlaneFunc_State,{state})
        IF state = MUIV_PlaneFunc_State_Frozen
            message(data.projectid,cat.msgm_RemVsFreeze.getstr())
            quiet:=FALSE
            RETURN
        ENDIF
        IF currid=data.projectid THEN currid:=-1
/*remove the list item*/
        get(opened_lst,MUIA_List_Entries,{count})
        FOR i:=0 TO (count-1)
            doMethodA(opened_lst,[MUIM_List_GetEntry,i,{d}])
            EXIT d=data
        ENDFOR
        doMethodA(opened_lst,[MUIM_List_Remove,i]) -> was the object really found??
/*free the window*/
        doMethodA(app,[OM_REMMEMBER,win])
        Mui_DisposeObject(win)
quiet:=FALSE
        doMethodA(opened_lst,[MUIM_ProjectList_Refresh,0])
->Delay(50)
->WriteF('\n»»»Done with refreshing.\n')
    ENDIF
ENDPROC

PROC planeFunc_dispose(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg)
DEF data:PTR TO planeFunc_data
    data:=INST_DATA(cl,obj)
    ObtainSemaphore(data.sema)
    data.app:=FALSE
    ReleaseSemaphore(data.sema)
    IF data.subtask
        killsubtask(data)
        set(obj,MUIA_PlaneFunc_Lock,MUIV_PlaneFunc_Lock_NoWrite)
    ENDIF
    DisposeLink(data.name)
    DisposeLink(data.rstr)
    DisposeLink(data.gstr)
    DisposeLink(data.bstr)
    END data.rfunc
    END data.gfunc
    END data.bfunc
    DisposeLink(data.imagefile)
    DisposeLink(data.imagefilename)

    IF data.picture THEN DeletePicture(data.picture)
    IF data.imagedata AND data.vec THEN FreeMem(data.imagedata,data.width*data.height*4)
ENDPROC doSuperMethodA(cl,obj,msg)
PROC planeFunc_lamps(cl:PTR TO iclass,obj:PTR TO object/*,msg:PTR TO msg*/)
DEF data:PTR TO planeFunc_data
    data:=INST_DATA(cl,obj)
    IF data.failure AND $1
        set(blue_fail,MUIA_Lamp_Color,MUIV_Lamp_Color_Error)
    ELSE
        set(blue_fail,MUIA_Lamp_Color,MUIV_Lamp_Color_Ok)
    ENDIF
    data:=INST_DATA(cl,obj)
    IF data.failure AND $10
        set(green_fail,MUIA_Lamp_Color,MUIV_Lamp_Color_Error)
    ELSE
        set(green_fail,MUIA_Lamp_Color,MUIV_Lamp_Color_Ok)
    ENDIF
    IF data.failure AND $100
        set(red_fail,MUIA_Lamp_Color,MUIV_Lamp_Color_Error)
    ELSE
        set(red_fail,MUIA_Lamp_Color,MUIV_Lamp_Color_Ok)
    ENDIF
ENDPROC

PROC planeFunc_draw(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO muip_draw)
DEF data:PTR TO planeFunc_data,delta,d=FALSE,window:PTR TO window,mw,mh,w,h
    doSuperMethodA(cl,obj,msg)
    data:=INST_DATA(cl,obj)

    mw:=_mwidth(obj);mh:=_mheight(obj)

    IF data.drawhandle = NIL
        get(obj,MUIA_Window,{window})
        IF window
            data.drawhandle:=odhandle(obj)
            IF data.drawhandle = 0
                WriteF('\s\n\n',cat.msgm_NoDrawHandle.getstr());RETURN
            ENDIF
        ELSE ; WriteF('\s\n',cat.msgm_NoWindow.getstr());RETURN
        ENDIF
    ENDIF

    ObtainSemaphore(data.sema)
    IF (msg.flags AND MADF_DRAWUPDATE)
        IF data.newtop <> -1/*scroll vertically*/
            data.top:=data.newtop;data.newtop:=-1
            d:=TRUE
        ENDIF
        IF data.newleft <> -1/*scroll horizontally*/
            data.left:=data.newleft;data.newleft:=-1
            d:=TRUE
        ENDIF

    ELSEIF (msg.flags AND MADF_DRAWOBJECT)
        /*the window may have been resized..*/
        set(data.hscroll,MUIA_Prop_Visible,Div(Mul(mw,100000),data.ratio))
        set(data.vscroll,MUIA_Prop_Visible,Div(Mul(mh,100000),data.ratio))
        get(data.vscroll,MUIA_Prop_First,{delta})
        data.top:=delta
        get(data.hscroll,MUIA_Prop_First,{delta})
        data.left:=delta
        d:=TRUE
    ENDIF

    IF mw*mh=0 THEN
        RETURN ReleaseSemaphore(data.sema) -> Would be silly to draw when size is zero :)

    w:=Div(Mul(mw,100000),data.ratio) -> dimensions of source to display.
    h:=Div(Mul(mh,100000),data.ratio)
    DrawPictureA(data.drawhandle,data.picture,_mleft(obj),_mtop(obj),[
                                            GGFX_SourceX,data.left,
                                            GGFX_SourceY,data.top,
                                            GGFX_SourceWidth,Min(w,data.width-data.left),
                                            GGFX_DestWidth,Min(mw,mw*(data.width-data.left)/w),
                                            GGFX_SourceHeight,Min(h,data.height-data.top),
                                            GGFX_DestHeight,Min(mh,mh*(data.height-data.top)/h),
                                            TAG_DONE])
    ReleaseSemaphore(data.sema)
ENDPROC

/*not yet finished: ability to select a destination area*/

/*PROC drawhoriz(x,y,xx,obj:PTR TO object,data:PTR TO planeFunc_data)
    IF data.ymin > data.top              ->top border
        Move(_rp(obj),_mleft(obj)+Max(0,data.xmin-data.left),         _mtop(obj)+data.ymin-data.top)
        Draw(_rp(obj),_mleft(obj)+Min(_mwidth(obj),data.xmax-data.left),_mtop(obj)+data.ymin-data.top)
    ENDIF

    Move(_rp(obj),x,y)
    Draw(_rp(obj),xx,y)
ENDPROC*/

PROC planeFunc_get(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO opget)
DEF data:PTR TO planeFunc_data,storage,attrid

    data:=INST_DATA(cl,obj)
    attrid:=msg.attrid
    storage:=msg.storage
    ObtainSemaphore(data.sema)
    SELECT attrid
    CASE MUIA_PlaneFunc_Bstr
        ^storage:=data.bstr
/*    CASE MUIA_CompileR
        ^storage:=data.compiler
    CASE MUIA_CompileG
        ^storage:=data.compileg
    CASE MUIA_CompileB
        ^storage:=data.compileb
*/    CASE MUIA_PlaneFunc_Gstr
        ^storage:=data.gstr
    CASE MUIA_PlaneFunc_Height
        ^storage:=data.height
    CASE MUIA_PlaneFunc_ImageFile
        ^storage:=data.imagefile
    CASE MUIA_PlaneFunc_Loading
        ^storage:=data.loading
    CASE MUIA_PlaneFunc_LoadM
        ^storage:=data.loadm
    CASE MUIA_PlaneFunc_Lock
        ^storage:=data.lock
    CASE MUIA_PlaneFunc_Modified
        ^storage:=data.modified
    CASE MUIA_PlaneFunc_Name
        ^storage:=data.name
    CASE MUIA_PlaneFunc_Percent
        IF data.lock=MUIV_PlaneFunc_Lock_Write
            ^storage:=data.calculated*100/data.height
        ELSE
            ^storage:=0
        ENDIF
    CASE MUIA_PlaneFunc_OutputR
        ^storage:=data.outputr
    CASE MUIA_PlaneFunc_OutputG
        ^storage:=data.outputg
    CASE MUIA_PlaneFunc_OutputB
        ^storage:=data.outputb
    CASE MUIA_PlaneFunc_ProjectID
        ^storage:=data.projectid
    CASE MUIA_PlaneFunc_Quiet
        ^storage:=data.quiet
    CASE MUIA_PlaneFunc_Rstr
        ^storage:=data.rstr
    CASE MUIA_PlaneFunc_State
        IF data.lock = MUIV_PlaneFunc_Lock_Write
            IF data.paused
                ^storage:=MUIV_PlaneFunc_State_Paused
            ELSEIF data.loading
                ^storage:=MUIV_PlaneFunc_State_Loading
            ELSE
                ^storage:=MUIV_PlaneFunc_State_Drawing
            ENDIF
        ELSE
            IF data.lock = 0
                ^storage:=MUIV_PlaneFunc_State_Idle
            ELSE
                ^storage:=MUIV_PlaneFunc_State_Frozen
            ENDIF
        ENDIF
    CASE MUIA_PlaneFunc_Type
        ^storage:=data.type
    CASE MUIA_PlaneFunc_Width
        ^storage:=data.width
    CASE MUIA_P_WindowObject -> MUIA_WindowObject does not work >:-(
        ^storage:=data.win
    DEFAULT
        ReleaseSemaphore(data.sema)
        RETURN doSuperMethodA(cl,obj,msg)
    ENDSELECT
    ReleaseSemaphore(data.sema)
ENDPROC MUI_TRUE

PROC planeFunc_set(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO opset)
DEF data:PTR TO planeFunc_data,
    tags:PTR TO tagitem,
    tag:PTR TO tagitem,
    attrid
DEF window,group
    data:=INST_DATA(cl,obj)
    tags:=msg.attrlist
    ObtainSemaphore(data.sema)
    WHILE tag:=NextTagItem({tags})
        attrid:=tag.tag
        SELECT attrid
        CASE MUIA_PlaneFunc_ImageFile
            StrCopy(data.imagefile,tag.data)
        CASE MUIA_PlaneFunc_Loading
            data.loading:=tag.data
            IF data.projectid=currid THEN
                setlamp(obj)
        CASE MUIA_PlaneFunc_Left
            data.newleft:=tag.data
            Mui_Redraw(obj,MADF_DRAWUPDATE)
        CASE MUIA_PlaneFunc_Top
            data.newtop:=tag.data
            Mui_Redraw(obj,MADF_DRAWUPDATE)
        CASE MUIA_PlaneFunc_Lock
            IF data.lock=MUIV_PlaneFunc_Lock_Write-> If the object is being written, the only tag allowed is NoWrite
                IF tag.data=MUIV_PlaneFunc_Lock_NoWrite
                    data.lock:=0
                ENDIF
            ELSEIF data.lock=0            -> If the object is free, it can either be read or write - locked
                data.lock:=tag.data
            ELSE                         -> If the object is read locked, it can be read unlocked or read locked again.
                IF tag.data=MUIV_PlaneFunc_Lock_NoRead
                    data.lock:=data.lock-1
                ELSEIF tag.data=MUIV_PlaneFunc_Lock_Read
                    data.lock:=data.lock+1
                ENDIF
            ENDIF
            doMethodA(opened_lst,[MUIM_ProjectList_Refresh,data.projectid+2]) -> Refresh the "state" lamp.
        CASE MUIA_PlaneFunc_Name
            StrCopy(data.name,tag.data)
            get(obj,MUIA_P_WindowObject,{window})
            set(window,MUIA_Window_Title,data.name)
        CASE MUIA_PlaneFunc_Quiet
            data.quiet:=tag.data
            IF data.quiet=FALSE
                get(obj,MUIA_Parent,{group})
                doMethodA(group,[MUIM_Group_InitChange])
                doMethodA(group,[MUIM_Group_ExitChange])
->                IF data.imagedata AND data.vec THEN FreeMem(data.imagedata,data.width*data.height*4);data.imagedata:=NIL

              /*  data.imagedata:=AllocMem(data.width*data.height*4,MEMF_CLEAR OR MEMF_PUBLIC);data.vec:=TRUE
                data.picture:=MakePictureA(data.imagedata,data.width,data.height,[GGFX_PixelFormat,PIXFMT_0RGB_32,TAG_DONE])
              */
                IF data.projectid=currid
                    nnset(width_str,MUIA_String_Integer,data.width)
                    nnset(height_str,MUIA_String_Integer,data.height)
                ENDIF
                /*IF data.subtask THEN
                    sendsubtaskmsg(data.subtask,STC_CHANGEDONE,NIL) */
            ENDIF
        CASE MUIA_P_WindowObject
            data.win:=tag.data
        DEFAULT
            IF (data.lock <> MUIV_PlaneFunc_Lock_Write) OR (data.quiet=-2)
                SELECT attrid
                CASE MUIA_PlaneFunc_Bstr
                    StrCopy(data.bstr,tag.data)
                    LowerStr(data.bstr)
                    idecode(data.bfunc,tag.data,$1,data)
                    data.modified:=data.modified OR 1
                CASE MUIA_PlaneFunc_Gstr
                    StrCopy(data.gstr,tag.data)
                    idecode(data.gfunc,tag.data,$10,data)
                    data.modified:=data.modified OR 1
                CASE MUIA_PlaneFunc_LoadM
                    data.loadm:=tag.data
                CASE MUIA_PlaneFunc_OutputR
                    data.outputr:=tag.data
                    idecode(data.rfunc,data.rstr,$100,data,data.outputr)
                    data.modified:=data.modified OR 1
                CASE MUIA_PlaneFunc_OutputG
                    data.outputg:=tag.data
                    idecode(data.gfunc,data.gstr,$10,data,data.outputg)
                    data.modified:=data.modified OR 1
                CASE MUIA_PlaneFunc_OutputB
                    data.outputb:=tag.data
                    idecode(data.bfunc,data.bstr,$1,data,data.outputb)
                    data.modified:=data.modified OR 1
                CASE MUIA_PlaneFunc_Rstr
                    StrCopy(data.rstr,tag.data)
                    LowerStr(data.rstr)
                    idecode(data.rfunc,tag.data,$100,data)
                    data.modified:=data.modified OR 1
                CASE MUIA_PlaneFunc_Type
                    data.type:=tag.data
                DEFAULT
                    IF (data.lock <> MUIV_PlaneFunc_Lock_NoRead) AND (data.quiet<>-2)
                        message(data.projectid,cat.msgm_AttrVsLock.getstr())
                    ELSEIF attrid = MUIA_PlaneFunc_Width
                        data.width:=tag.data
                        set(data.hscroll,MUIA_Prop_Entries,tag.data)
                        IF data.quiet=FALSE
                            get(obj,MUIA_Parent,{group})
                            doMethodA(group,[MUIM_Group_InitChange])
                            doMethodA(group,[MUIM_Group_ExitChange])
->                            IF data.imagedata AND data.vec THEN FreeMem(data.imagedata,data.width*data.height*4);data.imagedata:=NIL
                            IF data.picture THEN DeletePicture(data.picture);data.picture:=NIL
                            data.imagedata:=AllocMem(data.width*data.height*4,MEMF_CLEAR OR MEMF_PUBLIC);data.vec:=TRUE
                            data.picture:=MakePictureA(data.imagedata,data.width,data.height,[GGFX_PixelFormat,PIXFMT_0RGB_32,TAG_DONE])
                        ENDIF
                        data.modified:=data.modified OR 3 -> (both definition and picture)
                    ELSEIF attrid = MUIA_PlaneFunc_Height
                        data.height:=tag.data
                        set(data.vscroll,MUIA_Prop_Entries,tag.data)
                        IF data.quiet=FALSE
                            get(obj,MUIA_Parent,{group})
                            doMethodA(group,[MUIM_Group_InitChange])
                            doMethodA(group,[MUIM_Group_ExitChange])
->                            IF data.imagedata AND data.vec THEN FreeMem(data.imagedata,data.width*data.height*4);data.imagedata:=NIL
                            IF data.picture THEN DeletePicture(data.picture);data.picture:=NIL
                            data.imagedata:=AllocMem(data.width*data.height*4,MEMF_CLEAR OR MEMF_PUBLIC);data.vec:=TRUE
                            data.picture:=MakePictureA(data.imagedata,data.width,data.height,[GGFX_PixelFormat,PIXFMT_0RGB_32,TAG_DONE])
                        ENDIF
                        data.modified:=data.modified OR 3
                    ENDIF
                ENDSELECT
            ELSE
                message(data.projectid,cat.msgm_AttrVsLock.getstr())
            ENDIF
        ENDSELECT
    ENDWHILE
    ReleaseSemaphore(data.sema)
ENDPROC doSuperMethodA(cl,obj,msg)

/*idecode, extends f.create(). Ok, I know I should maybe have written a method, but it's easier like this :-¦*/
PROC idecode(f:PTR TO expression,s:PTR TO CHAR,l,data:PTR TO planeFunc_data,in=-1)
DEF out/*:PTR TO outinfo,err*/,i:PTR TO CHAR

    i:=String(StrLen(s)) -> New string so that lowerstr doesn't change text in field.
    StrCopy(i,s)
    LowerStr(i)

    IF in = -1 THEN
        in:=f.precision

    IF in = OUT_Float64
        message(data.projectid,cat.msgm_Nof64.getstr())
        DisposeLink(i)
        RETURN
    ENDIF

    END f

    NEW f.create(in,i)
    out:=f.message
    IF out AND FAILURE
        data.failure:=data.failure OR l
        /*err:=out.error
        SELECT err
        CASE ERR_NoParse
            messagefmt(data.projectid,cat.msgm_NoParse.getstr(),out.quote)
        CASE ERR_NoFunc
            messagefmt(data.projectid,cat.msgm_NoFunc.getstr(),out.quote)
        CASE ERR_NoChar
            messagefmt(data.projectid,cat.msgm_NoChar.getstr(),out.quote)
        DEFAULT
            message(data.projectid,cat.msgm_Failure.getstr())
        ENDSELECT
        END out*/
    ELSE -> Can't currently have both failure and warning. It will come :-)
        data.failure:=data.failure AND Not(l)
        IF out AND WARN_NoSupport THEN
            message(data.projectid,cat.msgm_WarnNoSupport.getstr())
        IF out AND WARN_EPi THEN
            message(data.projectid,cat.msgm_WarnEPi.getstr())
    ENDIF
    doMethodA(data.self,[MUIM_PlaneFunc_Lamps])
    DisposeLink(i)
ENDPROC

PROC planeFunc_abort(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO args_abort)
DEF data:PTR TO planeFunc_data,ds1:PTR TO datestamp,ds2:datestamp,qe[30]:STRING,u
    data:=INST_DATA(cl,obj)
    IF data.subtask
        killsubtask(data)
        set(obj,MUIA_PlaneFunc_Lock,MUIV_PlaneFunc_Lock_NoWrite)

        u:=msg.reason
        SELECT u
        CASE MUIV_PlaneFunc_Abort_Finish
            ds1:=data.ds
            DateStamp(ds2)
            u:=(ds2.minute-ds1.minute)*TICKS_PER_MINUTE+ds2.tick-ds1.tick -> number of ticks
            StringF(qe,'\s  \z\d[2]:\z\d[2].\z\d[2].',cat.msgm_Done.getstr(),u/TICKS_PER_MINUTE,Mod(u,TICKS_PER_MINUTE)/TICKS_PER_SECOND,Mod(u,TICKS_PER_SECOND)*100/TICKS_PER_SECOND)
            message(data.projectid,qe)
        CASE MUIV_PlaneFunc_Abort_Abort
            message(data.projectid,cat.msgm_Abort.getstr())
            IF data.type = MUIV_PlaneFunc_Type_Graph AND data.imagedata -> the rendering was aborted. we'll have to tidy up
                IF data.picture THEN DeletePicture(data.picture)
                data.picture:=MakePictureA(data.imagedata,data.width,data.height,[GGFX_PixelFormat,PIXFMT_0RGB_32,TAG_DONE])
                data.calculated:=0
            ENDIF
        CASE MUIV_PlaneFunc_Abort_NoLoad
            message(data.projectid,cat.msgm_NoLoad.getstr())
        ENDSELECT

        data.drawn:=0
        data.calculated:=0
        data.paused:=FALSE
        data.loading:=FALSE

        Mui_Redraw(obj,MADF_DRAWUPDATE)

        doMethodA(saveP,[MUIM_Projecthold_StepAnimate]) -> If an animation is going then this jumps to next step. Otherwise, this does nothing.
    ENDIF
ENDPROC

PROC planeFunc_loadfunc(cl:PTR TO iclass,obj:PTR TO object/*,msg:PTR TO msg*/)
DEF u,filereq:PTR TO filerequester,s[200]:STRING,handler,data:PTR TO planeFunc_data,
    eq,var[10]:STRING,value:PTR TO CHAR

    data:=INST_DATA(cl,obj)
    get(settings_win,MUIA_Window_Window,{u})
    filereq:=AllocAslRequest(ASL_FILEREQUEST,
                            [ASL_HAIL,      cat.msgr_Loadf_T.getstr(),
                             ASL_OKTEXT,    cat.msgr_Loadf_Ok.getstr(),
                             ASL_DIR,       funcdir,
                             ASL_WINDOW,    u,
                             NIL])
    IF filereq=NIL
        Mui_RequestA(app,settings_win,0,cat.msgr_Loadf_T.getstr(),NIL,cat.msgr_NoAslReq.getstr(),NIL)
    ELSE
        IF AslRequest(filereq,NIL)
            StrCopy(s,filereq.drawer)
            DisposeLink(funcdir);funcdir:=String(StrLen(s));StrCopy(funcdir,s)
            AddPart(s,filereq.file,StrMax(s))
            SetStr(s,StrLen(s))
            evaluatePath(s)
        ENDIF
        FreeAslRequest(filereq)
    ENDIF
    IF s[] = NIL THEN RETURN
    handler:=Open(s,OLDFILE)
    Fgets(handler,s,StrMax(s)-1)
    IF s[]=NIL
        Close(handler)
        message(data.projectid,cat.msgm_EmptyFile.getstr())
        RETURN
    ENDIF
    SetStr(s,StrLen(s)-1)
    IF Not(StrCmp(s,'picFX function-file; type1'))
        Close(handler)
        message(data.projectid,cat.msgm_BadFile.getstr())
        RETURN
    ENDIF
    set(obj,MUIA_PlaneFunc_Quiet,MUI_TRUE)
    WHILE Fgets(handler,s,StrMax(s)-1)/* semaphore will be obtained in the SET method */
        SetStr(s,StrLen(s)-1)
        eq:=InStr(s,'=')
        StrCopy(var,s,eq)
        value:=s+eq+1
        IF StrCmp(var,'bstr')
            set(obj,MUIA_PlaneFunc_Bstr,value)
        ELSEIF StrCmp(var,'gstr')
            set(obj,MUIA_PlaneFunc_Gstr,value)
        ELSEIF StrCmp(var,'height')
            set(obj,MUIA_PlaneFunc_Height,Val(value))
        ELSEIF StrCmp(var,'rstr')
            set(obj,MUIA_PlaneFunc_Rstr,value)
        ELSEIF StrCmp(var,'width')
            set(obj,MUIA_PlaneFunc_Width,Val(value))
        ELSEIF StrCmp(var,'precision') -> Obsolete tag!
            set(obj,MUIA_PlaneFunc_OutputR,Val(value))
            set(obj,MUIA_PlaneFunc_OutputG,Val(value))
            set(obj,MUIA_PlaneFunc_OutputB,Val(value))
            message(data.projectid,cat.msgm_Obsolete.getstr())
        ELSEIF StrCmp(var,'outputr')
            set(obj,MUIA_PlaneFunc_OutputR,Val(value))
        ELSEIF StrCmp(var,'outputg')
            set(obj,MUIA_PlaneFunc_OutputG,Val(value))
        ELSEIF StrCmp(var,'outputb')
            set(obj,MUIA_PlaneFunc_OutputB,Val(value))
        ELSE
            StringF(s,cat.msgm_UnknownVar.getstr(),var)
            message(data.projectid,s)
        ENDIF
    ENDWHILE
    Close(handler)
    set(obj,MUIA_PlaneFunc_Quiet,FALSE)
    doMethodA(opened_lst,[MUIM_ProjectList_Refresh,0])
ENDPROC

/*evaluatePath will take an estring of a filename and replace all "[len:]expr" by their values*/
PROC evaluatePath(string)
DEF result,temp,c=0,l=-1,r=-1 -> cursor, left, right
    result:=String(StrMax(string))
    WHILE (l:=InStr(string,'"',c)) <> -1
        r:=InStr(string,'"',l+1)
        IF r = -1
            StrAdd(string,'"') -> Just hope there's enough room :-)
            r:= EstrLen(string)-1
        ENDIF
        StrAdd(result,string+c,l-c)
        string[r]:=0 -> To make evalstr() think the string is finishing there :-)
        temp:=evalstr(string+l+1) -> now temp points to the evaluated string
        string[r]:='"' -> restore the quote :-)
        StrAdd(result,temp)
        DisposeLink(temp)
        c:=r+1
    ENDWHILE
    StrAdd(result,string+r+1)
    StrCopy(string,result) -> Replace...
    DisposeLink(result)
ENDPROC

PROC planeFunc_render(cl:PTR TO iclass,obj:PTR TO object/*,msg*/)
DEF data:PTR TO planeFunc_data,state,r,msg
    data:=INST_DATA(cl,obj)
    get(obj,MUIA_PlaneFunc_State,{state})
    IF state = MUIV_PlaneFunc_State_Idle
        get(recompile_chk,MUIA_Selected,{r}) ; IF r -> do recompile

/*Here comes a little problem: If the user changes the string of a function without pressing enter and then
changes the current project, then the string will be lost. However if he doesn't, then the string will be taken into
account next time the function is needed. This is not very coherent :-)
*/
            IF data.projectid=currid -> If the strings are for the current project, then we read them (as they might not have been saved yet)
                get(red_str,MUIA_String_Contents,{r})
                doMethodA(opened_lst,[MUIM_ProjectList_Notify,MUIV_ProjectList_Notify_Red,r])
                get(green_str,MUIA_String_Contents,{r})
                doMethodA(opened_lst,[MUIM_ProjectList_Notify,MUIV_ProjectList_Notify_Green,r])
                get(blue_str,MUIA_String_Contents,{r})
                doMethodA(opened_lst,[MUIM_ProjectList_Notify,MUIV_ProjectList_Notify_Blue,r])
            ELSE -> otherwise we directely look the strings in the project... (The following don't do any lamp stuff, just end and recompile the function)
                idecode(data.rfunc,data.rstr,$000,data)
                idecode(data.gfunc,data.gstr,$000,data)
                idecode(data.bfunc,data.bstr,$000,data)
            ENDIF

        ENDIF

        set(obj,MUIA_PlaneFunc_Lock,MUIV_PlaneFunc_Lock_Write)

        data.subtask:=AllocVec(SIZEOF subtask,MEMF_PUBLIC OR MEMF_CLEAR)

        IF data.type = MUIV_PlaneFunc_Type_DTPic
            set(obj,MUIA_PlaneFunc_Loading,TRUE)
        ELSE
            IF freeze(data.rfunc) = 0 -> This will freeze used projects; if zero, it failed.
                message(data.projectid,cat.msgm_NoAlloc.getstr())
                FreeVec(data.subtask);data.subtask:=NIL
                set(obj,MUIA_PlaneFunc_Lock,MUIV_PlaneFunc_Lock_NoWrite)
                RETURN
            ENDIF
            IF freeze(data.gfunc) = 0
                message(data.projectid,cat.msgm_NoAlloc.getstr())
                FreeVec(data.subtask);data.subtask:=NIL
                set(obj,MUIA_PlaneFunc_Lock,MUIV_PlaneFunc_Lock_NoWrite)
                RETURN
            ENDIF
            IF freeze(data.bfunc) = 0
                message(data.projectid,cat.msgm_NoAlloc.getstr())
                FreeVec(data.subtask);data.subtask:=NIL
                set(obj,MUIA_PlaneFunc_Lock,MUIV_PlaneFunc_Lock_NoWrite)
                RETURN
            ENDIF
            /*will we have to save previous pixel information?*/
            data.savepixel:=(data.outputr>=OUT_OldR) AND (data.outputr<=OUT_OldB)
            data.savepixel:=data.savepixel OR ((data.outputg>=OUT_OldR) AND (data.outputg<=OUT_OldB))
            data.savepixel:=data.savepixel OR ((data.outputb>=OUT_OldR) AND (data.outputb<=OUT_OldB))

            data.rupd:=rupd
            data.subtask.st_Reply:=CreateMsgPort()
        ENDIF

        IF data.type = MUIV_PlaneFunc_Type_DTPic
            StrCopy(data.imagefilename,data.imagefile)
            evaluatePath(data.imagefilename) -> We have to do this separately as we can't
                            -> compile a function from the subtask (eg, access to varlist)
        ENDIF

        r,msg:=fork()

        SELECT r
        CASE 0 -> if we are here then we are the subtask
            IF data.type = MUIV_PlaneFunc_Type_DTPic
                loadpicture(data)
            ELSE
                data.subtask.st_Port:=CreateMsgPort()
                renderfunc(data)                
            ENDIF
        CASE -1
            SELECT msg -> if we're here then forking the process failed
            CASE FORKERROR_SIGNAL
                message(data.projectid,cat.msgm_FEsignal.getstr())
            CASE FORKERROR_MEMORY
                message(data.projectid,cat.msgm_FEmemory.getstr())
            CASE FORKERROR_CHILD
                message(data.projectid,cat.msgm_FEchild.getstr())
            DEFAULT
                messagefmt(data.projectid,'Unexpected error $\h',msg) -> ditto
            ENDSELECT
        DEFAULT -> if we're here then we're still the main task
            data.subtask.st_Task:=r
            SetTaskPri(data.subtask.st_Task,cp)
        ENDSELECT
    ELSEIF data.type = MUIV_PlaneFunc_Type_Graph
        SELECT state
        CASE MUIV_PlaneFunc_State_Drawing
            sendsubtaskmsg(data.subtask,STC_PAUSE,NIL)
            data.paused:=TRUE
            doMethodA(opened_lst,[MUIM_ProjectList_Refresh,data.projectid+2])
        CASE MUIV_PlaneFunc_State_Paused
            data.paused:=FALSE
            sendsubtaskmsg(data.subtask,STC_RESTART,NIL)
            doMethodA(opened_lst,[MUIM_ProjectList_Refresh,data.projectid+2])
        CASE MUIV_PlaneFunc_State_Frozen
            message(data.projectid,cat.msgm_WriteVsFreeze.getstr())
        ENDSELECT
    ENDIF
ENDPROC

PROC planeFunc_savefunc(cl:PTR TO iclass,obj:PTR TO object/*,msg:PTR TO msg*/)
DEF u,filereq:PTR TO filerequester,s[200]:STRING,newhandler,oldhandler,data:PTR TO planeFunc_data
    data:=INST_DATA(cl,obj)
    get(settings_win,MUIA_Window_Window,{u})
    filereq:=AllocAslRequest(ASL_FILEREQUEST,
                            [ASL_HAIL,      cat.msgr_Savef_T.getstr(),
                             ASL_OKTEXT,    cat.msgr_Savef_Ok.getstr(),
                             ASL_DIR,       funcdir,
                             ASL_WINDOW,    u,
                             ASL_FUNCFLAGS, FILF_SAVE,
                             NIL])
    IF filereq=NIL
        Mui_RequestA(app,settings_win,0,cat.msgr_Savef_T.getstr(),NIL,cat.msgr_NoAslReq.getstr(),NIL)
    ELSE
        IF AslRequest(filereq,NIL)
            StrCopy(s,filereq.drawer)
            DisposeLink(funcdir);funcdir:=String(StrLen(s));StrCopy(funcdir,s)
            AddPart(s,filereq.file,StrMax(s))
            SetStr(s,StrLen(s))
            evaluatePath(s)
        ENDIF
        FreeAslRequest(filereq)
    ENDIF
    IF s[] = NIL THEN RETURN
    newhandler:=Open(s,NEWFILE)
    oldhandler:=SetStdOut(newhandler)
    WriteF('picFX function-file; type1\n')
    ObtainSemaphore(data.sema)
    WriteF('bstr=\s\n',data.bstr)
    WriteF('gstr=\s\n',data.gstr)
    WriteF('height=\d\n',data.height)
    WriteF('rstr=\s\n',data.rstr)
    WriteF('outputr=\d\n',data.outputr)
    WriteF('outputg=\d\n',data.outputg)
    WriteF('outputb=\d\n',data.outputb)
    WriteF('width=\d\n',data.width)
    ReleaseSemaphore(data.sema)
    Close(newhandler)
    SetStdOut(oldhandler)
    data.modified:=data.modified AND 2 -> we did not save the picture
ENDPROC

PROC planeFunc_savepic(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO args_savepic) -> Warning, this method is synchrone and the animation assumes it!!
DEF iff:PTR TO iffhandle,bmhd:PTR TO bmhd,x,y,w,bp,bit=0,b:PTR TO CHAR,c,k
DEF filereq:PTR TO filerequester,s[200]:STRING,data:PTR TO planeFunc_data
    data:=INST_DATA(cl,obj)
    IF msg.filegiven=FALSE -> Then we ask the user
        get(settings_win,MUIA_Window_Window,{w})
        filereq:=AllocAslRequest(ASL_FILEREQUEST,
                                [ASL_HAIL,      cat.msgr_SavePicture.getstr(),
                                 ASL_OKTEXT,    cat.msgr_Savef_Ok.getstr(),
                                 ASL_DIR,       picdir,
                                 ASL_WINDOW,    w,
                                 ASL_FUNCFLAGS, FILF_SAVE,
                                 NIL])
        IF filereq=NIL
            Mui_RequestA(app,settings_win,0,cat.msgr_SavePicture.getstr(),NIL,cat.msgr_NoAslReq.getstr(),NIL)
        ELSE
            IF AslRequest(filereq,NIL)
                StrCopy(s,filereq.drawer)
                DisposeLink(picdir);picdir:=String(StrLen(s));StrCopy(picdir,s)
                AddPart(s,filereq.file,StrMax(s))
                SetStr(s,StrLen(s))
                evaluatePath(s)
            ENDIF
            FreeAslRequest(filereq)
        ENDIF
    ELSE
        s:=msg.filename
    ENDIF

    IF s[] = NIL THEN RETURN

    IF currid=data.projectid
        set(state_txt,MUIA_Text_Contents,cat.msgs_Saving.getstr())
        set(lamp_lmp,MUIA_Lamp_Color,MUIV_Lamp_Color_SavingData)
        set(state_gau,MUIA_Gauge_Current,0)
    ENDIF

    iff:=AllocIFF()
    iff.stream:=Open(s,NEWFILE)
    IF iff.stream = NIL THEN RETURN
    InitIFFasDOS(iff)
    OpenIFF(iff,IFFF_WRITE)

    PushChunk(iff,ID_ILBM,ID_FORM,IFFSIZE_UNKNOWN)

    NEW bmhd
    bmhd.w:=data.width;bmhd.h:=data.height
    bmhd.planes:=24
    bmhd.compression:=0 -> currently (?) saving uncompressed iffs.
    bmhd.xaspect:=1;bmhd.yaspect:=bmhd.xaspect -> what should be correct values?
    bmhd.pagew:=bmhd.w;bmhd.pageh:=bmhd.h

    PushChunk(iff,ID_ILBM,ID_BMHD,SIZEOF bmhd)
    WriteChunkBytes(iff,bmhd,SIZEOF bmhd)
    PopChunk(iff)
    END bmhd

    PushChunk(iff,ID_ILBM,ID_BODY,IFFSIZE_UNKNOWN)
    w:=data.width
    w:=(w-1) AND Not(%1111)+15 -> Rounded to the nearest multiple of 16, and substract 1 because we start from zero...
    NEW b[(w+1)*3+1]
    c:=b
    bit:=0
    FOR y:=0 TO data.height
        set(state_gau,MUIA_Gauge_Current,y*100/data.height)
        FOR k:=0 TO 2
            FOR bp:=16-(k*8) TO (23-(k*8))
                FOR x:=0 TO w
                    b[]:=Shl(b[],1)+(Shr(pixel(data.imagedata,data.width,x,y),bp) AND $1) -> Yurk, that's awfully sloow!!!
                    bit++
                    IF bit=8
                        b++;bit:=0
                        b[]:=0
                    ENDIF
                ENDFOR
            ENDFOR
        ENDFOR
        b:=c
        WriteChunkBytes(iff,b,(w+1)*3)
    ENDFOR
    PopChunk(iff)
    END b[(w+1)*3+1]
    PopChunk(iff)

    CloseIFF(iff)
    Close(iff.stream)
    FreeIFF(iff)
    data.modified:=data.modified AND 1 -> we did not save the definition
    setlamp(obj)
ENDPROC

PROC planeFunc_setsize(cl:PTR TO iclass,obj:PTR TO object/*,msg:PTR TO msg*/)
DEF data:PTR TO planeFunc_data,k,nr
    data:=INST_DATA(cl,obj)
/*We want the sum of the apparent dimensions of the picture to be the same as the one for the drawing area.*/

    nr:=Div(Mul(100000,_mwidth(obj)+_mheight(obj)+1),data.width+data.height+1)
    IF data.ratio=nr
        data.ratio:=100000 -> Press twice space bar to set ratio to 1:1
    ELSE
        data.ratio:=nr
    ENDIF

    set(data.hscroll,MUIA_Prop_Visible,Div(Mul(_mwidth(obj),100000),data.ratio))
    set(data.vscroll,MUIA_Prop_Visible,Div(Mul(_mheight(obj),100000),data.ratio))
    get(data.vscroll,MUIA_Prop_First,{k})
    data.newtop:=k
    get(data.hscroll,MUIA_Prop_First,{k})
    data.newleft:=k

    Mui_Redraw(obj,MADF_DRAWOBJECT)
ENDPROC

PROC planeFunc_settaskpri(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO args_stp)
DEF data:PTR TO planeFunc_data
    data:=INST_DATA(cl,obj)
    ObtainSemaphore(data.sema)
    IF data.subtask THEN
        SetTaskPri(data.subtask.st_Task,msg.value)
    ReleaseSemaphore(data.sema)
ENDPROC
PROC planeFunc_update(cl:PTR TO iclass,obj:PTR TO object/*,msg:PTR TO msg*/)
DEF data:PTR TO planeFunc_data
    data:=INST_DATA(cl,obj)
    ObtainSemaphore(data.sema)
    data.app:=FALSE
    ReleaseSemaphore(data.sema)
    IF data.calculated=0 THEN Mui_Redraw(obj,MADF_DRAWUPDATE)
    IF data.projectid = currid
->        ObtainSemaphore(data.sema)
        set(state_gau,MUIA_Gauge_Current,data.calculated*100/data.height)
->        ReleaseSemaphore(data.sema)
    ENDIF
    ObtainSemaphore(data.sema)
    data.app:=TRUE
    ReleaseSemaphore(data.sema)
ENDPROC

/***************************************************************
** custom ProjectList private subclass of List class          **
****************************************************************
**                                                            **
** Interaction between the application and the opened projects**
***************************************************************/

CONST PROJECTNODE_ID=100

PROC projectList_dispatcher(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg)
DEF methodid
    methodid:=msg.methodid
    SELECT methodid
    CASE OM_NEW                   ; RETURN projectList_new(cl,obj,msg)
    CASE OM_DISPOSE               ; RETURN projectList_dispose(cl,obj,msg)
    CASE MUIM_Setup               ; RETURN projectList_setup(cl,obj,msg)
    CASE MUIM_Cleanup             ; RETURN projectList_cleanup(cl,obj,msg)
    CASE MUIM_ProjectList_DelItem ; RETURN projectList_delitem(cl,obj/*,msg*/)
    CASE MUIM_ProjectList_DisableProgram ; RETURN projectList_disableProgram(cl,obj,msg)
    CASE MUIM_ProjectList_EnableProgram  ; RETURN projectList_enableProgram(cl,obj,msg)
    CASE MUIM_ProjectList_Exit    ; RETURN projectList_exit(cl,obj/*,msg*/)
    CASE MUIM_ProjectList_Forward ; RETURN projectList_forward(cl,obj,msg)
    CASE MUIM_ProjectList_Memory  ; RETURN projectList_memory(cl,obj/*,msg*/)
    CASE MUIM_ProjectList_NewItem ; RETURN projectList_newitem(cl,obj/*,msg*/)
    CASE MUIM_ProjectList_Notify  ; RETURN projectList_notify(cl,obj,msg)
    CASE MUIM_ProjectList_Prefs   ; RETURN projectList_prefs(cl,obj,msg)
    CASE MUIM_ProjectList_Refresh ; RETURN projectList_refresh(cl,obj,msg)
    CASE MUIM_ProjectList_SavePrefs;RETURN projectList_saveprefs(/*cl,obj,msg*/)
    CASE MUIM_ProjectList_SetPri  ; RETURN projectList_setpri(cl,obj,msg)
    CASE MUIM_ProjectList_ShowMe  ; RETURN projectList_showme(cl,obj,msg)
    CASE MUIM_ProjectList_InsertInList; RETURN projectList_insertInList(cl,obj,msg)
    ENDSELECT
ENDPROC doSuperMethodA(cl,obj,msg)

PROC getcurrent(data:PTR TO projectList_data,obj:PTR TO object)
DEF k:PTR TO planeFunc_data,win=NIL
    doMethodA(obj,[MUIM_List_GetEntry,MUIV_List_GetEntry_Active,{k}])
->WriteF('[Got $\h for active entry]\n',k)
    IF k
        k:=k.self
        get(k,MUIA_P_WindowObject,{win})
    ENDIF
ENDPROC k,win

/*Care! getp() asks the ProjectID and not line number!!!*/

PROC getp(data:PTR TO projectList_data,pid)
DEF count,i,dt:PTR TO planeFunc_data

    get(opened_lst,MUIA_List_Entries,{count})
    FOR i:=0 TO (count-1)
        doMethodA(opened_lst,[MUIM_List_GetEntry,i,{dt}])
        IF dt.projectid=pid
            get(dt.self,MUIA_P_WindowObject,{i})
            RETURN dt.self,i  -> PlaneFuncObject,WindowObject
        ENDIF
    ENDFOR
ENDPROC NIL,NIL

PROC getdata(data:PTR TO projectList_data,pid) -> This code is a bit redundant with getp()! :-)
DEF count,i,dt:PTR TO planeFunc_data

    get(opened_lst,MUIA_List_Entries,{count})
    FOR i:=0 TO (count-1)
        doMethodA(opened_lst,[MUIM_List_GetEntry,i,{dt}])
        IF dt.projectid=pid
            RETURN dt
        ENDIF
    ENDFOR

/*If we did not find the requested id..*/
    messagefmt(-1,cat.msgm_NoID.getstr(),pid)

ENDPROC NIL

PROC projectList_new(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg)
DEF data:PTR TO projectList_data
DEF port,req

    IF (obj:=doSuperMethodA(cl,obj,msg)) = FALSE THEN RETURN FALSE
    data:=INST_DATA(cl,obj)

    screentitle:=String(60)

    port := CreateMsgPort()
    IF port=NIL
        coerceMethodA(cl,obj,OM_DISPOSE)
        RETURN NIL
    ENDIF
    data.port:=port
    req := CreateIORequest(data.port,SIZEOF timerequest)
    IF req=NIL
        coerceMethodA(cl,obj,OM_DISPOSE)
        RETURN NIL
    ENDIF
    data.req:=req
    IF OpenDevice(TIMERNAME,UNIT_VBLANK,data.req,0) -> NIL if it went all right...
        coerceMethodA(cl,obj,OM_DISPOSE)
        RETURN NIL
    ENDIF
    data.ihnode.ihn_millis  := 5000 -> Update memory display every five seconds
    data.ihnode.ihn_object  := obj
    data.ihnode.ihn_method  := MUIM_ProjectList_Memory
    data.ihnode.ihn_flags   := MUIIHNF_TIMER
ENDPROC obj

PROC projectList_setup(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO muip_setup)
DEF id,data:PTR TO projectList_data
    IF doSuperMethodA(cl,obj,msg) = FALSE THEN RETURN FALSE
    data:=INST_DATA(cl,obj)

    data.req.io.command := TR_ADDREQUEST
    data.req.time.secs    := 5 -> Update every five seconds
    data.req.time.micro   := 0
    SendIO(data.req)
    doMethodA(_app(obj),[MUIM_Application_AddInputHandler,data.ihnode])

    initfuncs({getdata},data,varlist_lst) -> initialization of parser.m (Warn: done every time the window is opened!!)
ENDPROC MUI_TRUE

PROC projectList_cleanup(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg)
DEF data:PTR TO projectList_data
    data := INST_DATA(cl,obj)
    doMethodA(_app(obj),[MUIM_Application_RemInputHandler,data.ihnode])
    IF (CheckIO(data.req)=NIL) THEN AbortIO(data.req)
    WaitIO(data.req)
    cleanfuncs()
ENDPROC (doSuperMethodA(cl,obj,msg))

PROC projectList_dispose(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg)
DEF data:PTR TO projectList_data
    data:=INST_DATA(cl,obj)

    DisposeLink(screentitle)

    IF (data.req)
        IF (data.req.io.device) THEN CloseDevice(data.req)
        DeleteIORequest(data.req)
    ENDIF
    IF (data.port) THEN DeleteMsgPort(data.port)
ENDPROC doSuperMethodA(cl,obj,msg)

PROC projectList_delitem(cl:PTR TO iclass,obj:PTR TO object/*,msg:PTR TO msg*/)
DEF data:PTR TO projectList_data,gad,window,modified,state,pid,name,btns

quiet:=TRUE /*prevents the opened projects - list from refreshing the display*/
    data:=INST_DATA(cl,obj)
    gad,window:=getcurrent(data,obj)
    IF gad=NIL THEN RETURN -> This should never happen
    get(gad,MUIA_PlaneFunc_ProjectID,{pid})
    get(gad,MUIA_PlaneFunc_Name,{name})
    get(gad,MUIA_PlaneFunc_Modified,{modified})

    IF modified <> 0
        IF window=0 THEN window:=settings_win
        btns:=String(StrLen(cat.msgr_Remove.getstr())+StrLen(cat.msgr_Cancel.getstr())+1)
        StringF(btns,'\s|\s',cat.msgr_Remove.getstr(),cat.msgr_Cancel.getstr())
        IF Mui_RequestA(app,window,0,cat.msgr_RemProject.getstr(),btns,cat.msgr_RemoveLong.getstr(),[pid,name])<>1
            DisposeLink(btns)
            quiet:=FALSE
            RETURN
        ENDIF
        DisposeLink(btns)
    ENDIF

    doMethodA(gad,[MUIM_PlaneFunc_Abort,MUIV_PlaneFunc_Abort_Abort])/*aborts the task, if necessary*/
    get(gad,MUIA_PlaneFunc_State,{state})
    IF state = MUIV_PlaneFunc_State_Frozen
        message(pid,cat.msgm_RemVsFreeze.getstr())
        quiet:=FALSE
        RETURN
    ENDIF
    doMethodA(obj,[MUIM_List_Remove,MUIV_List_Remove_Active])/*removes the name from the list*/
    set(window,MUIA_Window_Open,FALSE)/*Closes the window*/
    doMethodA(app,[OM_REMMEMBER,window])
    Mui_DisposeObject(window) /*Removes the window and the PlaneFuncObject*/
    currid:=-1 -> This is to prevent projectList to change that unexistant project's priority
quiet:=FALSE
    doMethodA(obj,[MUIM_ProjectList_Refresh,0])
ENDPROC

PROC projectList_disableProgram(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg)
DEF root
    IF egroup<>NIL THEN RETURN -> (The program was already disabled)
    get(sgroup,MUIA_Parent,{root})
    egroup:=TextObject,
        TextFrame,MUIA_Background,MUII_TextBack,
        MUIA_Text_SetMax,FALSE,MUIA_Text_SetVMax,FALSE,
        MUIA_Text_Contents,cat.msgm_PleaseWait.getstr(),
    End
    doMethodA(root,[MUIM_Group_InitChange])
      doMethodA(root,[OM_REMMEMBER,sgroup])
      doMethodA(root,[OM_ADDMEMBER,egroup])
      doMethodA(root,[MUIM_Group_Sort,egroup,stat_grp,NIL])
    doMethodA(root,[MUIM_Group_ExitChange])
ENDPROC

PROC projectList_enableProgram(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg)
DEF root
    IF egroup=NIL THEN RETURN -> (The program was already enabled)
    get(egroup,MUIA_Parent,{root})
    doMethodA(root,[MUIM_Group_InitChange])
      doMethodA(root,[OM_REMMEMBER,egroup])
      doMethodA(root,[OM_ADDMEMBER,sgroup])
      doMethodA(root,[MUIM_Group_Sort,sgroup,stat_grp,NIL])
    doMethodA(root,[MUIM_Group_ExitChange])
    Mui_DisposeObject(egroup)
    egroup:=NIL
ENDPROC

PROC projectList_exit(cl:PTR TO iclass,obj:PTR TO object/*,msg:PTR TO msg*/)
DEF count,gad,data:PTR TO projectList_data,pid
DEF z,cs:PTR TO object,d:PTR TO planeFunc_data,i
    data:=INST_DATA(cl,obj)
    get(obj,MUIA_List_Entries,{count})
    FOR i:=0 TO (count-1)
        doMethodA(opened_lst,[MUIM_List_GetEntry,i,{d}])
        get(d.self,MUIA_PlaneFunc_ProjectID,{pid})
        messagefmt(-1,cat.msgm_Removing.getstr(),pid)
        doMethodA(d.self,[MUIM_PlaneFunc_Abort,MUIV_PlaneFunc_Abort_Abort])
    ENDFOR

ENDPROC

PROC projectList_forward(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO args_forward)
DEF gad,data:PTR TO projectList_data
    data:=INST_DATA(cl,obj)
    gad:=getcurrent(data,obj)
    IF gad = NIL THEN RETURN -> This should never happen
    IF msg.cnt=2
        doMethodA(gad,[msg.method,msg.args[]]) -> (Not sure it works)
    ELSEIF msg.cnt=1
        doMethodA(gad,[msg.method])
    ELSE
        message(-1,'IE:Wrong argcount given to Forward') -> Not very nice but I don't need more currently ;-)
    ENDIF
ENDPROC

PROC projectList_memory(cl:PTR TO iclass,obj:PTR TO object)
DEF win:PTR TO object,data:PTR TO projectList_data
    data:=INST_DATA(cl,obj)

    StringF(screentitle,'\s - \d Chip - \d Fast - \d Total',PICVERSION,AvailMem(MEMF_CHIP),AvailMem(MEMF_FAST),AvailMem(MEMF_ANY))
    get(obj,MUIA_WindowObject,{win})
    set(win,MUIA_Window_ScreenTitle,screentitle)
    set(messages_win,MUIA_Window_ScreenTitle,screentitle)
ENDPROC

PROC projectList_newitem(cl:PTR TO iclass,obj:PTR TO object/*,msg:PTR TO msg*/)
DEF window, pfunc,vscroll,hscroll,pid,
    name:PTR TO CHAR,
    data:PTR TO projectList_data,
    pdata:PTR TO planeFunc_data

    data:=INST_DATA(cl,obj)
    window:=WindowObject,
        MUIA_Window_ScreenTitle, PICVERSION,
        ->MUIA_Window_UseRightBorderScroller,MUI_TRUE,
        MUIA_Window_UseBottomBorderScroller,MUI_TRUE,
        WindowContents,
            HGroup,
                MUIA_Group_Spacing,0,
                Child, pfunc:=PlaneFuncObject,End,
                Child, vscroll:=ScrollbarObject,
                    MUIA_FixWidth,20,
/*This is an MUI bug, the refreshing is not done correctely when having a scroller in the right border..*/
                    ->MUIA_Prop_UseWinBorder,MUIV_Prop_UseWinBorder_Right,

                    MUIA_Prop_Entries,255,MUIA_Prop_Visible,255,
                End,
                Child, hscroll:=PropObject,
                    MUIA_Prop_UseWinBorder,MUIV_Prop_UseWinBorder_Bottom,
                    MUIA_Prop_Entries,255,MUIA_Prop_Visible,255,
                End,
            End,
        End

    set(vscroll,MUIA_Prop_Entries,255) -> entries not set correctely..
    set(pfunc,MUIA_P_WindowObject,window)

    doMethodA(app,[OM_ADDMEMBER,window])  /* Now the project window is ready;*/
    get(pfunc,MUIA_PlaneFunc_Name,{name}) /*  the PlaneFunc class has also initialised.*/
    set(window,MUIA_Window_Title,name) -> Read the default PlaneFunc name
    pdata:=INST_DATA(cl_planefunc.mcc_class,pfunc)
    pdata.hscroll:=hscroll
    pdata.vscroll:=vscroll
    doMethodA(hscroll,[MUIM_Notify,MUIA_Prop_First,MUIV_EveryTime,pfunc,3,MUIM_Set,MUIA_PlaneFunc_Left,MUIV_TriggerValue])
    doMethodA(vscroll,[MUIM_Notify,MUIA_Prop_First,MUIV_EveryTime,pfunc,3,MUIM_Set,MUIA_PlaneFunc_Top,MUIV_TriggerValue])
    doMethodA(window,[MUIM_Notify,MUIA_Window_CloseRequest,MUI_TRUE,pfunc,1,MUIM_PlaneFunc_Close])
    doMethodA(window,[MUIM_Notify,MUIA_Window_InputEvent,'space',pfunc,1,MUIM_PlaneFunc_SetSize])
    get(pfunc,MUIA_PlaneFunc_ProjectID,{pid})

    set(window,MUIA_Window_ID,$12345678+pid) -> stupid but unique id for a project :-)

    doMethodA(obj,[MUIM_List_InsertSingle,pdata,MUIV_List_Insert_Bottom])
    set(window,MUIA_Window_Open,MUI_TRUE) -> Open the window when everything is finished
    set(obj,MUIA_List_Active,MUIV_List_Active_Bottom)
    doMethodA(obj,[MUIM_ProjectList_Memory]) -> Update memory display when opening a project

ENDPROC

PROC projectList_notify(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO args_notify)
DEF data:PTR TO projectList_data,gadid,z,gad,value,pid
    data:=INST_DATA(cl,obj)
    gad:=getcurrent(data,obj)
    IF gad = NIL THEN RETURN -> This should never happen (maybe put some error message?)
    value:=msg.value
    gadid:=msg.gadget
    SELECT gadid
    CASE MUIV_ProjectList_Notify_Name
        set(gad,MUIA_PlaneFunc_Name,value)
        doMethodA(obj,[MUIM_List_Redraw,MUIV_List_Redraw_Active])
    CASE MUIV_ProjectList_Notify_Type
        SELECT value
        CASE 0
            set(gad,MUIA_PlaneFunc_Type,MUIV_PlaneFunc_Type_DTPic)
        CASE 1
            set(gad,MUIA_PlaneFunc_Type,MUIV_PlaneFunc_Type_Graph)
        ENDSELECT
    CASE MUIV_ProjectList_Notify_ImageFile
        set(gad,MUIA_PlaneFunc_ImageFile,value)
    CASE MUIV_ProjectList_Notify_LoadM
        SELECT value
        CASE 0
            set(gad,MUIA_PlaneFunc_LoadM,MUIV_PlaneFunc_LoadM_Scale)
        CASE 1
            set(gad,MUIA_PlaneFunc_LoadM,MUIV_PlaneFunc_LoadM_CutTile)
        CASE 2
            set(gad,MUIA_PlaneFunc_LoadM,MUIV_PlaneFunc_LoadM_ChangeSize)
        ENDSELECT
    CASE MUIV_ProjectList_Notify_Red
        set(gad,MUIA_PlaneFunc_Rstr,value)
    CASE MUIV_ProjectList_Notify_Green
        set(gad,MUIA_PlaneFunc_Gstr,value)
    CASE MUIV_ProjectList_Notify_Blue
        set(gad,MUIA_PlaneFunc_Bstr,value)
/*    CASE MUIV_ProjectList_Notify_CompileR
        set(gad,MUIA_PlaneFunc_CompileR,value)
    CASE MUIV_ProjectList_Notify_CompileG
        set(gad,MUIA_PlaneFunc_CompileG,value)
    CASE MUIV_ProjectList_Notify_CompileB
        set(gad,MUIA_PlaneFunc_CompileB,value)
*/    CASE MUIV_ProjectList_Notify_OutputR
        set(gad,MUIA_PlaneFunc_OutputR,value)
    CASE MUIV_ProjectList_Notify_OutputG
        set(gad,MUIA_PlaneFunc_OutputG,value)
    CASE MUIV_ProjectList_Notify_OutputB
        set(gad,MUIA_PlaneFunc_OutputB,value)
    CASE MUIV_ProjectList_Notify_Width
        set(gad,MUIA_PlaneFunc_Width,Val(value))
    CASE MUIV_ProjectList_Notify_Height
        set(gad,MUIA_PlaneFunc_Height,Val(value))
    ENDSELECT
ENDPROC

PROC projectList_prefs(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO args_prefs) -> a future version may require semaphore, but not for now.
DEF data:PTR TO projectList_data,d:PTR TO planeFunc_data,count,i
    msg.var[]:=msg.value

    IF (msg.var={dithm}) -> In these cases we've got to update the drawhandle for all windows

        data:=INST_DATA(cl,obj)
        IF currid > -1
            set(state_txt,MUIA_Text_Contents,cat.msgs_DrawHandle.getstr())
            set(lamp_lmp,MUIA_Lamp_Color,MUIV_Lamp_Color_Processing)
            set(state_gau,MUIA_Gauge_Current,0)
        ENDIF
        get(obj,MUIA_List_Entries,{count})
        FOR i:=0 TO (count-1)
            doMethodA(opened_lst,[MUIM_List_GetEntry,i,{d}])
            doMethodA(d.self,[MUIM_PlaneFunc_DrawHandle])
        ENDFOR
        obj:=getcurrent(data,obj); IF obj THEN setlamp(obj)
    ENDIF
ENDPROC

/*This method reads all attributes of the current class and writes the state of the gadgets
in the settings window*/
PROC projectList_refresh(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO args_refresh)
DEF data:PTR TO projectList_data,nm:PTR TO CHAR,gad,value,q[11]:STRING,pid,win
DEF i,count,ogad,d:PTR TO planeFunc_data

    IF quiet=TRUE THEN RETURN
    data:=INST_DATA(cl,obj)
    gad,win:=getcurrent(data,obj)
    IF gad=NIL
        set(sgroup,MUIA_Disabled,MUI_TRUE)
        doMethodA(menus,[MUIM_SetUDataOnce,MENU_Project,MUIA_Menu_Enabled,FALSE])
        currid:=-1
        RETURN
    ENDIF
    set(sgroup,MUIA_Disabled,FALSE)
    doMethodA(menus,[MUIM_SetUDataOnce,MENU_Project,MUIA_Menu_Enabled,MUI_TRUE])
/*Unimplemented features:*/
    set(size_rad,MUIA_Disabled,MUI_TRUE)
    get(gad,MUIA_PlaneFunc_ProjectID,{pid})
    IF pid <> currid /*was currid and will be pid*/
       ogad:=getp(data,currid)
        IF ogad <> NIL
            doMethodA(ogad,[MUIM_PlaneFunc_SetTaskPri,rp])
        ENDIF
        doMethodA(gad,[MUIM_PlaneFunc_SetTaskPri,cp])
    ENDIF

    currid:=pid
    IF msg.ostate=FALSE
        doMethodA(gad,[MUIM_PlaneFunc_Lamps])
        StringF(q,'r(\d,x,y)=',pid)
        set(r_lbl,MUIA_Text_Contents,q)
        StringF(q,'g(\d,x,y)=',pid)
        set(g_lbl,MUIA_Text_Contents,q)
        StringF(q,'b(\d,x,y)=',pid)
        set(b_lbl,MUIA_Text_Contents,q)
        get(gad,MUIA_PlaneFunc_Name,{value})
        nnset(name_str,MUIA_String_Contents,value)   /*nnset to avoid triggering useless notification*/
        get(gad,MUIA_PlaneFunc_Type,{value})
        SELECT value
        CASE MUIV_PlaneFunc_Type_DTPic
            nnset(type_reg,MUIA_Group_ActivePage,0)
        CASE MUIV_PlaneFunc_Type_Graph
            nnset(type_reg,MUIA_Group_ActivePage,1)
        ENDSELECT

        get(gad,MUIA_PlaneFunc_LoadM,{value})  /* these are for image-projects*/
        SELECT value                           
        CASE MUIV_PlaneFunc_LoadM_Scale        /* but all settings are updated, so that */   
            nnset(size_rad,MUIA_Radio_Active,0)/* the user may change the selected page and*/
        CASE MUIV_PlaneFunc_LoadM_CutTile      /*see the current settings..*/                
            nnset(size_rad,MUIA_Radio_Active,1)
        CASE MUIV_PlaneFunc_LoadM_ChangeSize
            nnset(size_rad,MUIA_Radio_Active,2)
        ENDSELECT
        get(gad,MUIA_PlaneFunc_ImageFile,{value})
        nnset(path_str,MUIA_String_Contents,value)

        get(gad,MUIA_PlaneFunc_Rstr,{value})   /*these are for function-projects*/
        nnset(red_str,MUIA_String_Contents,value)
        get(gad,MUIA_PlaneFunc_Gstr,{value})
        nnset(green_str,MUIA_String_Contents,value)
        get(gad,MUIA_PlaneFunc_Bstr,{value})
        nnset(blue_str,MUIA_String_Contents,value)
        get(gad,MUIA_PlaneFunc_OutputR,{value})
        nnset(outr_cyc,MUIA_Cycle_Active,value)
        get(gad,MUIA_PlaneFunc_OutputG,{value})
        nnset(outg_cyc,MUIA_Cycle_Active,value)
        get(gad,MUIA_PlaneFunc_OutputB,{value})
        nnset(outb_cyc,MUIA_Cycle_Active,value)
        get(gad,MUIA_PlaneFunc_Width,{value})
        nnset(width_str,MUIA_String_Integer,value)
        get(gad,MUIA_PlaneFunc_Height,{value})
        nnset(height_str,MUIA_String_Integer,value)
        get(win,MUIA_Window_Open,{value})
        nnset(showme_cyc,MUIA_Cycle_Active,value)
        nm:=doMethodA(menus,[MUIM_FindUData,MENU_P_Visible])
        nnset(nm,MUIA_Menuitem_Checked,value)
    ENDIF
    IF (msg.ostate = FALSE) OR (msg.ostate-2=pid)
        setlamp(gad)
    ENDIF
    IF msg.ostate > 0
        pid:=msg.ostate-2

        get(obj,MUIA_List_Entries,{count})
        FOR i:=0 TO (count-1)
            doMethodA(obj,[MUIM_List_GetEntry,i,{d}])
            EXIT d.projectid=pid
        ENDFOR
        doMethodA(obj,[MUIM_List_Redraw,i]) -> Maybe should I call here similar method for the other project-list object?
    ENDIF
ENDPROC

PROC setlamp(gad:PTR TO object)
DEF value
    get(gad,MUIA_PlaneFunc_State,{value})
    SELECT value
    CASE MUIV_PlaneFunc_State_Idle
        set(state_txt,MUIA_Text_Contents,cat.msgs_Idle.getstr())
        set(lamp_lmp,MUIA_Lamp_Color,MUIV_Lamp_Color_Off)
        set(state_gau,MUIA_Gauge_Current,0)
        set(draw_btn,MUIA_Text_Contents,cat.msgl_Render.getstr())
        doMethodA(menus,[MUIM_SetUDataOnce,MENU_P_Render,MUIA_Menuitem_Title,cat.msgl_Render.getstr()])
    CASE MUIV_PlaneFunc_State_Drawing
        set(state_txt,MUIA_Text_Contents,cat.msgs_Drawing.getstr())
        set(lamp_lmp,MUIA_Lamp_Color,MUIV_Lamp_Color_Processing)
        get(gad,MUIA_PlaneFunc_Percent,{value})
        set(state_gau,MUIA_Gauge_Current,value)
        set(draw_btn,MUIA_Text_Contents,cat.msgl_Pause.getstr())
        doMethodA(menus,[MUIM_SetUDataOnce,MENU_P_Render,MUIA_Menuitem_Title,cat.msgl_Pause.getstr()])
    CASE MUIV_PlaneFunc_State_Loading
        set(state_txt,MUIA_Text_Contents,cat.msgs_Loading.getstr())
        set(lamp_lmp,MUIA_Lamp_Color,MUIV_Lamp_Color_LoadingData)
        get(gad,MUIA_PlaneFunc_Percent,{value})
        set(state_gau,MUIA_Gauge_Current,value)
        set(draw_btn,MUIA_Text_Contents,cat.msgl_Pause.getstr())
        doMethodA(menus,[MUIM_SetUDataOnce,MENU_P_Render,MUIA_Menuitem_Title,cat.msgl_Pause.getstr()])
    CASE MUIV_PlaneFunc_State_Paused
        set(state_txt,MUIA_Text_Contents,cat.msgs_Paused.getstr())
        set(lamp_lmp,MUIA_Lamp_Color,MUIV_Lamp_Color_Ok)
        get(gad,MUIA_PlaneFunc_Percent,{value})
        set(state_gau,MUIA_Gauge_Current,value)
        set(draw_btn,MUIA_Text_Contents,cat.msgl_Restart.getstr())
        doMethodA(menus,[MUIM_SetUDataOnce,MENU_P_Render,MUIA_Menuitem_Title,cat.msgl_Restart.getstr()])
    CASE MUIV_PlaneFunc_State_Frozen
        set(state_txt,MUIA_Text_Contents,cat.msgs_Frozen.getstr())
        set(lamp_lmp,MUIA_Lamp_Color,MUIV_Lamp_Color_SendingData) -> That project is sending some data to others ;-)
        set(state_gau,MUIA_Gauge_Current,0)
        set(draw_btn,MUIA_Text_Contents,cat.msgl_Render.getstr()) -> maybe pointless? [user can't use this button!]
        doMethodA(menus,[MUIM_SetUDataOnce,MENU_P_Render,MUIA_Menuitem_Title,cat.msgl_Render.getstr()])
    ENDSELECT
ENDPROC

PROC useprefs()
/*Don't forget to update this once adding prefs :-)*/
#define prCount 8

DEF iff:PTR TO iffhandle,sp:storedproperty,prefs:PTR TO chunkSeeker,dt:PTR TO CHAR

    iff:=AllocIFF()
    iff.stream:=Open('ENV:picFX.prefs',NEWFILE)
    IF iff.stream = NIL THEN RETURN
    InitIFFasDOS(iff)
    OpenIFF(iff,IFFF_WRITE)

    PushChunk(iff,"PPRF",ID_FORM,prCount*4+12)

    PushChunk(iff,"PPRF","BODY",prCount*4)

    NEW dt[prCount*4]
    sp.data:=dt
    sp.size:=prCount*4
    NEW prefs.create(sp)

    prefs.write(mainpri_sld,MUIA_Slider_Level)
    prefs.write(renderpri_sld,MUIA_Slider_Level)
    prefs.write(currpri_sld,MUIA_Slider_Level)
    prefs.write(renderupdate_sld,MUIA_Slider_Level)
    prefs.write(close_cyc,MUIA_Cycle_Active)
    prefs.write(showmess_chk,MUIA_Selected)
    prefs.write(dither_cyc,MUIA_Cycle_Active)
    prefs.write(recompile_chk,MUIA_Selected)

    WriteChunkBytes(iff,prefs.data,prCount*4)
    PopChunk(iff)
    PopChunk(iff)

    CloseIFF(iff)
    Close(iff.stream)
    FreeIFF(iff)

    END dt
ENDPROC

PROC loadprefs() HANDLE
DEF iff:PTR TO iffhandle,sp:storedproperty,b:PTR TO LONG,prefs:PTR TO chunkSeeker,out
DEF context:PTR TO contextnode,k[5]:STRING
DEF p:PTR TO LONG

    iff:=AllocIFF()
    iff.stream:=Open('env:picFX.prefs',OLDFILE)
    IF iff.stream = NIL THEN Raise("OPEN")
    InitIFFasDOS(iff)
    OpenIFF(iff,IFFF_READ)

    LOOP
        out:=ParseIFF(iff,IFFPARSE_RAWSTEP)
        IF out * (out - IFFERR_EOC) THEN Raise("PARS") -> An eof is an error, too
        context:=CurrentChunk(iff)
        IF context
            IF (context.id="BODY") AND (context.type="PPRF") THEN
                JUMP offloop
        ENDIF
    ENDLOOP
offloop:
    sp.size:=context.size
    sp.data:=NewR(context.size)
    p:=sp.data
    IF (out:=ReadChunkBytes(iff,sp.data,sp.size)) <> sp.size THEN Raise("PARS")

    /*IF sp = NIL THEN Raise("PARS")*/

    NEW prefs.create(sp)

    prefs.read(mainpri_sld,MUIA_Slider_Level)
    prefs.read(renderpri_sld,MUIA_Slider_Level)
    prefs.read(currpri_sld,MUIA_Slider_Level)
    prefs.read(renderupdate_sld,MUIA_Slider_Level)
    prefs.read(close_cyc,MUIA_Cycle_Active)
    prefs.read(showmess_chk,MUIA_Selected)
    prefs.read(dither_cyc,MUIA_Cycle_Active)
    prefs.read(recompile_chk,MUIA_Selected)
EXCEPT DO
    IF exception THEN WriteF('exception \s (Sorry for being unfriendly ;-)\n',IdtoStr(exception,k))
    IF iff
        IF iff.stream
            CloseIFF(iff)
            Close(iff.stream)
        ENDIF
        FreeIFF(iff)
    ENDIF
    Dispose(sp.data) -> I know it is not required, but it is maybe cleaner so? :-)
ENDPROC

PROC projectList_saveprefs(/*cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg*/)
    useprefs()
    Execute('Copy env:picFX.prefs TO envarc:',NIL,NIL)
ENDPROC

PROC projectList_setpri(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO args_setpri)
DEF data:PTR TO projectList_data,task,d:PTR TO planeFunc_data
DEF cs:PTR TO object,i,count
    data:=INST_DATA(cl,obj)
    task:=msg.task
    SELECT task
    CASE MUIV_ProjectList_SetPri_Main
        SetTaskPri(FindTask(0),msg.value)
    CASE MUIV_ProjectList_SetPri_Render
        rp:=msg.value
        get(obj,MUIA_List_Entries,{count})
        FOR i:=0 TO (count-1)
            doMethodA(obj,[MUIM_List_GetEntry,i,{d}])
            IF d.projectid<>currid
                doMethodA(d.self,[MUIM_PlaneFunc_SetTaskPri,rp])
            ENDIF
        ENDFOR
    CASE MUIV_ProjectList_SetPri_Curr
        cp:=msg.value
        i:=getcurrent(data,obj)
        IF i
            doMethodA(i,[MUIM_PlaneFunc_SetTaskPri,cp])
        ENDIF
    ENDSELECT
ENDPROC

PROC projectList_showme(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO args_showme)
DEF d:PTR TO planeFunc_data,win

    doMethodA(obj,[MUIM_List_GetEntry,MUIV_List_GetEntry_Active,{d}])
    IF d=NIL THEN RETURN
    get(d.self,MUIA_P_WindowObject,{win})
    set(win,MUIA_Window_Open,msg.value)
ENDPROC

PROC projectList_insertInList(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO args_iil)
DEF data:PTR TO planeFunc_data
    data:=INST_DATA(cl,obj)

    doMethodA(obj,[MUIM_List_GetEntry,MUIV_List_GetEntry_Active,{data}])
    IF data = NIL THEN RETURN -> Either no project is active or there is no project at all
    doMethodA(msg.list,[MUIM_List_InsertSingle,data,MUIV_List_Insert_Bottom])
ENDPROC

/*Should I check here that the project actually exists?*/
PROC projectList_displayer(dest:PTR TO LONG,entry:PTR TO planeFunc_data)
    dest[0]:=dataFormat(entry)
ENDPROC NIL

PROC dataFormat(data:PTR TO planeFunc_data)
DEF r:PTR TO CHAR
DEF z=32,pid

    r:=String(StrLen(data.name)+4)
    get(data.self,MUIA_PlaneFunc_State,{z})
    IF (z=MUIV_PlaneFunc_State_Drawing) OR (z=MUIV_PlaneFunc_State_Loading)
        z:=33
    ELSE
        z:=32
    ENDIF
    get(data.self,MUIA_PlaneFunc_ProjectID,{pid})
    StringF(r,'\d\c:\s',pid,z,data.name)
ENDPROC r

/***************************************************************
** custom uSlider private subclass of Slider class            **
****************************************************************
*                                                              *
*Simple sub-class of Slider, overriding MUIM_Numeric_Stringify *
*                                                              *
***************************************************************/
PROC uslider_dispatcher(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO muip_numeric_stringify)
DEF data:PTR TO uslider_data,methodid
    methodid:=msg.methodid
    SELECT methodid
    CASE OM_NEW;RETURN uslider_new(cl,obj,msg)
    CASE MUIM_Numeric_Stringify
        data:=INST_DATA(cl,obj)
        IF data.stringify=MUI_TRUE
            data.buffer:=String(10) -> Mysterious bug fixed like that :-( => leaving unused allocated memory on *each* call to this method (freed at program quit)
            IF msg.value = 0
                StrCopy(data.buffer,cat.msgp_guNone.getstr())
            ELSEIF msg.value=1
                StringF(data.buffer,'\d \s',msg.value,cat.msgp_Pixel.getstr())
            ELSE
                StringF(data.buffer,'\d \s',msg.value,cat.msgp_Pixels.getstr())
            ENDIF
            RETURN data.buffer
        ENDIF
    CASE MUIM_uSlider_Bigger;RETURN uslider_bigger(obj,msg)
    CASE MUIM_uSlider_Smaller;RETURN uslider_smaller(obj,msg)
    CASE MUIM_Cleanup
        data:=INST_DATA(cl,obj)
        IF data.stringify THEN
            DisposeLink(data.buffer)
    ENDSELECT
ENDPROC doSuperMethodA(cl,obj,msg)

PROC uslider_new(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO opset)
DEF data:PTR TO uslider_data,
    tags:PTR TO tagitem,
    tag:PTR TO tagitem

    IF (obj:=doSuperMethodA(cl,obj,msg))=NIL THEN RETURN 0

    data:=INST_DATA(cl,obj)
    tags:=msg.attrlist
    WHILE tag:=NextTagItem({tags})
        IF (tag.tag=MUIA_uSlider_Stringify) AND (tag.data=MUI_TRUE)
            data.stringify:=tag.data
            data.buffer:=String(10)
        ENDIF
    ENDWHILE
ENDPROC obj

PROC uslider_bigger(obj:PTR TO object,msg:PTR TO args_compare)
DEF level
    get(obj,MUIA_Slider_Level,{level})
    IF level < msg.than THEN
        nnset(obj,MUIA_Slider_Level,msg.than)
ENDPROC

PROC uslider_smaller(obj:PTR TO object,msg:PTR TO args_compare)
DEF level
    get(obj,MUIA_Slider_Level,{level})
    IF level > msg.than THEN
        nnset(obj,MUIA_Slider_Level,msg.than)
ENDPROC

/***************************************************************
** sizetxt class                                              **
****************************************************************
*  textobject containing the size of the last loaded picture.  *
***************************************************************/

OBJECT sizetxt_data
    buffer:PTR TO CHAR
ENDOBJECT

CONST MUIM_SizeTxt_Update=$FCB93800

OBJECT muip_stupd
    methodid
    width,height,depth
    projectid
ENDOBJECT

PROC sizetxt_dispatcher(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg)
DEF methodid
    methodid:=msg.methodid
    SELECT methodid
    CASE OM_NEW;RETURN sizetxt_new(cl,obj,msg)
    CASE OM_DISPOSE;RETURN sizetxt_dispose(cl,obj,msg)
    CASE MUIM_SizeTxt_Update;RETURN sizetxt_update(cl,obj,msg)
    ENDSELECT
ENDPROC doSuperMethodA(cl,obj,msg)

PROC sizetxt_new(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg)
DEF data:PTR TO sizetxt_data
    obj:=doSuperMethodA(cl,obj,msg)
    data:=INST_DATA(cl,obj)
    data.buffer:=String(50)
ENDPROC obj

PROC sizetxt_dispose(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg)
DEF data:PTR TO sizetxt_data
    data:=INST_DATA(cl,obj)
    DisposeLink(data.buffer)
ENDPROC doSuperMethodA(cl,obj,msg)

PROC sizetxt_update(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO muip_stupd)
DEF data:PTR TO sizetxt_data
    data:=INST_DATA(cl,obj)
    IF msg.projectid = currid
        StringF(data.buffer,'\s\d×\d×\d',cat.msgl_PicSize.getstr(),msg.width,msg.height,msg.depth)
        set(obj,MUIA_Text_Contents,data.buffer)
    ENDIF
ENDPROC

/********************...            ...*********...           ...***...
projecthold class 'contains' a project. Current can be changed using drag'n'drop
.                                                   ****/


PROC projecthold_dispatcher(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg)
DEF methodid
    methodid:=msg.methodid
    SELECT methodid
    CASE OM_NEW       ; RETURN projecthold_new(cl,obj,msg)
    CASE OM_GET       ; RETURN projecthold_get(cl,obj,msg)
    CASE MUIM_DragQuery ; RETURN projecthold_dragquery(cl,obj,msg)
    CASE MUIM_DragDrop ; RETURN projecthold_dragdrop(cl,obj,msg)
    CASE MUIM_Projecthold_Animate ; RETURN projecthold_animate(cl,obj) -> This has not much to do with projecthold but I did not feel like putting a hook... hehe...
    CASE MUIM_Projecthold_StepAnimate ; RETURN projecthold_stepAnimate(cl,obj)
    ENDSELECT
ENDPROC doSuperMethodA(cl,obj,msg)

PROC projecthold_new(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO msg)
DEF data:PTR TO projecthold_data
    obj:=doSuperMethodA(cl,obj,msg)
    IF obj=NIL THEN RETURN NIL
    data:=INST_DATA(cl,obj)

    data.animating:=FALSE;data.current:=NIL
ENDPROC obj

PROC projecthold_get(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO opget)
DEF data:PTR TO projecthold_data,storage,attrid
    data:=INST_DATA(cl,obj)
    attrid:=msg.attrid
    storage:=msg.storage
    SELECT attrid
    CASE MUIA_Projecthold_Current
        ^storage:=data.current
    DEFAULT
        RETURN doSuperMethodA(cl,obj,msg)
    ENDSELECT
ENDPROC MUI_TRUE

PROC projecthold_dragquery(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO muip_dragquery)
    IF (msg.obj=opened_lst) OR (msg.obj=render_lst) THEN RETURN MUIV_DragQuery_Accept
ENDPROC MUIV_DragQuery_Refuse

PROC projecthold_dragdrop(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO muip_dragdrop)
DEF data:PTR TO projecthold_data,d

    data:=INST_DATA(cl,obj)
    doMethodA(msg.obj,[MUIM_List_GetEntry,MUIV_List_GetEntry_Active,{d}])
    IF d = NIL THEN RETURN -> Either no project is active or there is no project at all

    set(obj,MUIA_Text_Contents,dataFormat(d))

    data.current:=d
ENDPROC 0

PROC projecthold_animate(cl:PTR TO iclass,obj:PTR TO object)
DEF data:PTR TO projecthold_data,infotext[60]:STRING
DEF k
    data:=INST_DATA(cl,obj)

    IF data.animating -> Then we abort the animation
        data.animating:=FALSE
        doMethodA(opened_lst,[MUIM_ProjectList_EnableProgram])
        set(animstatus_txt,MUIA_Text_Contents,cat.msgs_IdleAborted.getstr())
        set(doAnim_btn,MUIA_Text_Contents,cat.msga_StartAnimation.getstr())
        RETURN
    ENDIF

    IF data.current=NIL THEN RETURN -> there still may be a problem if the user deletes the selected project before running the animation..

    data.animating:=TRUE
    doMethodA(opened_lst,[MUIM_ProjectList_DisableProgram])

    /*Now we read the values found in the three fields*/
    get(savename_str,MUIA_String_Contents,{k})
    data.filenamebase:=k
    get(frames_str,MUIA_String_Integer,{k})
    data.frameCnt:=k! -> we save the count in float mode (like the variables)!!!

    set(doAnim_btn,MUIA_Text_Contents,cat.msga_AbortAnimation.getstr())
    StringF(infotext,cat.msgs_AnimatingFirst.getstr(),k)
    set(animstatus_txt,MUIA_Text_Contents,infotext)

    get(parameter_str,MUIA_String_Contents,{k})
    data.varname:=k
    setvariable(k,StrLen(k),0.,varlist_lst) -> Initialize the time to zero

/* The state of List_Active reflects the project that was last rendered.
Before starting, I put it to off (to avoid a project to be saved for nothing)*/
    set(render_lst,MUIA_List_Active,MUIV_List_Active_Off)

    doMethodA(obj,[MUIM_Projecthold_StepAnimate]) -> Launch the thing :-)

-> Remember that this method returns once the rendering of the first project of first frame has *started*
-> (and not once the animation is finished)
ENDPROC

PROC projecthold_stepAnimate(cl:PTR TO iclass,obj:PTR TO object)
DEF data:PTR TO projecthold_data,line,count,filename:PTR TO CHAR,time,
    pdata:PTR TO planeFunc_data,infotext[30]:STRING

    data:=INST_DATA(cl,obj)

    IF Not(data.animating) THEN RETURN

    get(render_lst,MUIA_List_Active,{line})
    get(render_lst,MUIA_List_Entries,{count})
    IF line = MUIV_List_Active_Off THEN line:=-1

    line++ -> ie we go to the next project (but not yet move the cursor)

    IF line = count -> ie we are now over with rendering (line # line does not exist)
        time:=getvariable(data.varname,StrLen(data.varname),varlist_lst)
        /*Now save the picture*/
        filename:=String(StrLen(data.filenamebase)+4)
        StringF(filename,'\s.\z\d[3]',data.filenamebase,!time!) -> Quotes are not yet handled => risk of... "ram disk:anim".001 ... :-)
        StringF(infotext,cat.msgs_SavingFrame.getstr(),!time!,!data.frameCnt!)
        set(animstatus_txt,MUIA_Text_Contents,infotext)
        doMethodA(data.current::planeFunc_data.self,[MUIM_PlaneFunc_SavePic,MUI_TRUE,filename]) -> This method is synchrone...
-> Maybe display a message here
        DisposeLink(filename)
        line:=0 -> set back to line zero

        time:=!time+1. -> Let the time fly :-)

        IF !time >= data.frameCnt -> The work is over
            data.animating:=FALSE
            doMethodA(opened_lst,[MUIM_ProjectList_EnableProgram])
            set(animstatus_txt,MUIA_Text_Contents,cat.msgs_Idle.getstr())
            set(doAnim_btn,MUIA_Text_Contents,cat.msga_StartAnimation.getstr())
            RETURN
        ENDIF
        setvariable(data.varname,StrLen(data.varname),time,varlist_lst)
        StringF(infotext,cat.msgs_AnimatingFrame.getstr(),!time!,!data.frameCnt!)
        set(animstatus_txt,MUIA_Text_Contents,infotext)
    ENDIF

    set(render_lst,MUIA_List_Active,line) -> This is actually just to show the user what is going on

    doMethodA(render_lst,[MUIM_List_GetEntry,line,{pdata}])
    -> Now find out which project this is, start its rendering and exit that method.
    doMethodA(pdata.self,[MUIM_PlaneFunc_Render])

ENDPROC -> This method will be called again once the rendering is over

PROC myapp_dispatcher(cl:PTR TO iclass,obj:PTR TO object,msg:PTR TO muip_application_returnid)
DEF btns
    IF msg.methodid=MUIM_Application_ReturnID
        IF msg.retid=MUIV_Application_ReturnID_Quit  /*separate IFs to avoid illegal memory access, if the message hasn't any argument*/
            btns:=String(StrLen(cat.msgr_Quit.getstr())+StrLen(cat.msgr_Cancel.getstr())+1)
            StringF(btns,'\s|\s',cat.msgr_Quit.getstr(),cat.msgr_Cancel.getstr())
            IF Mui_RequestA(app,settings_win,0,cat.msgr_QuitProgram.getstr(),btns,
                    cat.msgr_QuitProgramLong.getstr(),[]) <> 1
                DisposeLink(btns)
                RETURN 0
            ENDIF
            DisposeLink(btns)
            doMethodA(opened_lst,[MUIM_ProjectList_Exit]) -> This will cause the Exit method to be executed right before
        ENDIF
    ENDIF                                           -> ReturnId (when called with Exit returnid)
ENDPROC doSuperMethodA(cl,obj,msg)

PROC sendsubtaskmsg(st:PTR TO subtask,command,params)
    IF st.st_Port=NIL THEN RETURN
    st.st_Message.stm_Message.replyport:= st.st_Reply
    st.st_Message.stm_Message.length   := SIZEOF subtaskmsg
    st.st_Message.stm_Command          := command
    st.st_Message.stm_Parameter        := params
    st.st_Message.stm_Result           := 0
    PutMsg(IF command=STC_STARTUP THEN st.st_Task::process.msgport ELSE st.st_Port,st.st_Message)
    WaitPort(st.st_Reply)          /*gets the reply*/
    GetMsg(st.st_Reply)
ENDPROC (st.st_Message.stm_Result)

PROC killsubtask(data:PTR TO planeFunc_data)
    IF data.type = MUIV_PlaneFunc_Type_Graph THEN
        sendsubtaskmsg(data.subtask,STC_SHUTDOWN,data.subtask)
    wait_one_fork()
    IF data.type = MUIV_PlaneFunc_Type_Graph
        free(data.rfunc)
        free(data.gfunc)
        free(data.bfunc)                    
        IF data.subtask.st_Reply THEN
            DeleteMsgPort(data.subtask.st_Reply)
    ENDIF
    FreeVec(data.subtask);data.subtask:=NIL
ENDPROC

/***************************************************************
**    Subtask which does all the time-consuming rendering     **
***************************************************************/

PROC renderfunc(data:PTR TO planeFunc_data)
DEF st=NIL:PTR TO subtask,
    worktodo=TRUE,x,y,
    stm=NIL:PTR TO subtaskmsg,
    command,i,
    red,green,blue,lr:PTR TO longreal,cnt,oldpixel,oldid:PTR TO LONG,
    rf,gf,bf,rc,gc,bc

    st:=data.subtask

    ObtainSemaphore(data.sema)
/*                    data.drawn:=-1
                    data.calculated:=-1
*/
    DateStamp(data.ds)
    oldid:=data.imagedata
    data.imagedata:=AllocMem(data.width*data.height*4,MEMF_PUBLIC OR MEMF_CLEAR);data.vec:=TRUE

    ReleaseSemaphore(data.sema)

    y:=0->;cnt:=data.rupd
    i:=0
    NEW lr
    rf:=data.rfunc.calculate
    gf:=data.gfunc.calculate
    bf:=data.bfunc.calculate
    rc:=data.rfunc.precalc
    gc:=data.gfunc.precalc
    bc:=data.bfunc.precalc

    LOOP
        WHILE (stm:= GetMsg(st.st_Port))
            command:=stm.stm_Command
            SELECT command
            CASE STC_SHUTDOWN
                JUMP exit
            CASE STC_PAUSE
                worktodo:= FALSE
            CASE STC_RESTART -> Deactivate pause
                worktodo:=TRUE
            ENDSELECT
            ReplyMsg(stm.stm_Message)
        ENDWHILE

        IF (worktodo)
            /* if there is work to do, i.e. if the rendering is not
               finished yet, we calculate the next line and draw
               it to the offscreen rastport.*/

            IF data.outputr=OUT_Integer      /*red*/
                rc(x,y)
            ELSEIF data.outputr=OUT_Float32
                rc(x!,y!)
            ENDIF
            IF data.outputg=OUT_Integer      /*green*/
                gc(x,y)
            ELSEIF data.outputg=OUT_Float32
                gc(x!,y!)
            ENDIF
            IF data.outputb=OUT_Integer      /*blue*/
                bc(x,y)
            ELSEIF data.outputb=OUT_Float32
                bc(x!,y!)
            ENDIF

            ObtainSemaphore(data.sema)

            IF data.savepixel THEN
                oldpixel:=oldid[i] ->pixel(oldid,data.width,0,y)

            FOR x:=0 TO data.width-1
                IF data.outputr=OUT_Integer      /*red*/

                    red:=rf(x,y)
                    red:=modt(red)
                    red:=Shl(red,16)
                ELSEIF data.outputr=OUT_Float32
                    red:=!rf(x!,y!)!
                    red:=modt(red)
                    red:=Shl(red,16)
                /*ELSEIF data.outputr=OUT_Float64
                    rproc(x,y,lr,data.rfunc)
                    red:=dFix(lr)
                    red:=modt(red)
                    red:=Shl(red,16)*/
                ELSEIF data.outputr=OUT_OldR
                    red:=oldpixel AND $FF0000
                ELSEIF data.outputr=OUT_OldG
                    red:=Shl(oldpixel AND $FF00,8)
                ELSE /*IF data.outputr=OUT_OldB*/
                    red:=Shl(oldpixel AND $FF,16)
                ENDIF
                IF data.outputg=OUT_Integer      /*green*/
                    green:=gf(x,y)
                    green:=modt(green)
                    green:=Shl(green,8)
                ELSEIF data.outputg=OUT_Float32
                    green:=!gf(x!,y!)!
                    green:=modt(green)
                    green:=Shl(green,8)
                /*ELSEIF data.outputg=OUT_Float64
                    gproc(x,y,lr,data.gfunc)
                    green:=dFix(lr)
                    green:=modt(green)
                    green:=Shl(green,8)*/
                ELSEIF data.outputg=OUT_OldR
                    green:=oldpixel AND $FF0000
                    green:=Shr(green,8)
                ELSEIF data.outputg=OUT_OldG
                    green:=oldpixel AND $FF00
                ELSEIF data.outputg=OUT_OldB
                    green:=oldpixel AND $FF
                    green:=Shl(green,8)
                ELSE /*IF data.outputr=OUT_CopyR*/
                    green:=Shr(red,8)
                ENDIF
                IF data.outputb=OUT_Integer      /*blue*/
                    blue:=bf(x,y)
                    blue:=modt(blue)
                ELSEIF data.outputb=OUT_Float32
                    blue:=!bf(x!,y!)!
                    blue:=modt(blue)
                /*ELSEIF data.outputb=OUT_Float64
                    bproc(x,y,lr,data.bfunc)
                    blue:=dFix(lr)
                    blue:=modt(blue)*/
                ELSEIF data.outputb=OUT_OldR
                    blue:=Shr(oldpixel,16)
                ELSEIF data.outputb=OUT_OldG
                    blue:=Shr(oldpixel,8) AND $FF
                    blue:=blue AND $FF
                ELSEIF data.outputb=OUT_OldB
                    blue:=oldpixel AND $FF
                ELSEIF data.outputb=OUT_CopyR
                    blue:=Shr(red,16)
                ELSE /*IF data.outputb=OUT_CopyG*/
                    blue:=Shr(green,8)
                ENDIF
                data.imagedata[i]:=red+green+blue
                i++
                IF data.savepixel THEN
                    oldpixel:=oldid[i] ->pixel(data.imagedata,data.width,x+1,y)
            ENDFOR
            ReleaseSemaphore(data.sema)
            y++

            IF (y AND $F=0)
                IF data.app
                    data.calculated:=y
                    doMethodA(app,[MUIM_Application_PushMethod,data.self,1,MUIM_PlaneFunc_Update])
                ENDIF
            ENDIF

            IF (y=data.height)
                /* check if we are finished to draw our picture */
                 worktodo:= FALSE
                /*if we're finished, we ask the main task to exit ourselves..*/
                IF data.picture THEN DeletePicture(data.picture)
                data.picture:=MakePictureA(data.imagedata,data.width,data.height,[GGFX_PixelFormat,PIXFMT_0RGB_32,TAG_DONE])
                doMethodA(app,[MUIM_Application_PushMethod,data.self,2,MUIM_PlaneFunc_Abort,MUIV_PlaneFunc_Abort_Finish])
            ENDIF
            /* Since we are very busy working, we do not Wait() for signals. */
        ELSE
            /* We have nothing to do, just sit quietly and wait for something to happen */
            WaitPort(st.st_Port)
        ENDIF
    ENDLOOP
exit:
    END lr
    IF data.subtask.st_Port
        DeleteMsgPort(data.subtask.st_Port);data.subtask.st_Port:=NIL
        data.subtask.st_Port:=NIL
    ENDIF
    ReplyMsg(stm.stm_Message)

ENDPROC

PROC loadpicture(data:PTR TO planeFunc_data)
DEF w,h
DEF st=NIL:PTR TO subtask,
    x,y,
    stm=NIL:PTR TO subtaskmsg,
    command,i,
    cnt=0,init=2

    /*IF AttemptSemaphore(data.sema)
        WriteF('Went all right..\n')
    ELSE
        WriteF('The semaphore is locked!!!\n')
    ENDIF
    */
    ObtainSemaphore(data.sema)
    DateStamp(data.ds)
    IF data.picture
        DeletePicture(data.picture)
    ENDIF
    data.picture:=LoadPictureA(data.imagefilename,[TAG_DONE])
    IF data.picture = NIL  ->And if data.app=NIL ? :-)
        data.imagedata:=AllocMem(data.width*data.height*4,MEMF_CLEAR OR MEMF_PUBLIC);data.vec:=TRUE
        data.picture:=MakePictureA(data.imagedata,data.width,data.height,[GGFX_PixelFormat,PIXFMT_0RGB_32,TAG_DONE])
        ReleaseSemaphore(data.sema)
        doMethodA(app,[MUIM_Application_PushMethod,data.self,2,MUIM_PlaneFunc_Abort,MUIV_PlaneFunc_Abort_NoLoad])
    ELSE
        GetPictureAttrsA(data.picture,[PICATTR_PixelFormat,{x},TAG_DONE])
        IF x <> PIXFMT_0RGB_32 THEN
            DoPictureMethodA(data.picture,PICMTHD_RENDER,[PIXFMT_0RGB_32,TAG_DONE])

        IF TRUE ->data.loadm=MUIV_PlaneFunc_LoadM_ChangeSize
            GetPictureAttrsA(data.picture,[PICATTR_Width,{w},PICATTR_Height,{h},PICATTR_RawData,{x},TAG_DONE])
            data.imagedata:=x;data.vec:=FALSE
            doMethodA(app,[MUIM_Application_PushMethod,data.self,3,
                MUIM_Set,MUIA_PlaneFunc_Quiet,-2])
            doMethodA(app,[MUIM_Application_PushMethod,data.self,3,
                MUIM_Set,MUIA_PlaneFunc_Width,w])
            doMethodA(app,[MUIM_Application_PushMethod,data.self,3,
                MUIM_Set,MUIA_PlaneFunc_Height,h])
            doMethodA(app,[MUIM_Application_PushMethod,data.self,3,
                MUIM_Set,MUIA_PlaneFunc_Quiet,FALSE])
        ELSE -> MUIV_PlaneFunc_LoadM_Scale
            DoPictureMethodA(data.picture,PICMTHD_SCALE,[data.width,data.height,TAG_DONE])
        ENDIF
        ReleaseSemaphore(data.sema)
    -> No need to send an update message, as the quiet attribute-change did it for us.
        doMethodA(app,[MUIM_Application_PushMethod,data.self,2,MUIM_PlaneFunc_Abort,MUIV_PlaneFunc_Abort_Finish])
    ENDIF
    Delay(10)
ENDPROC
/********************************
*   Inter-referencing...        *
********************************/

PROC dataExists(data) -> returns TRUE if a project owning the given data exists.
DEF i,count,d:PTR TO planeFunc_data

    get(opened_lst,MUIA_List_Entries,{count})

    FOR i:=0 TO (count-1)
        doMethodA(opened_lst,[MUIM_List_GetEntry,i,{d}])
        IF d=data THEN RETURN TRUE
    ENDFOR

ENDPROC FALSE

/*freeze(expression): this will read-lock all used functions.
returns true if success, or false if failure*/

PROC freeze(f:PTR TO expression)
DEF a:PTR TO planeFunc_data,b,c,data:PTR TO projectList_data,obj,lock
    IF f.precision > OUT_Float64 THEN RETURN TRUE
    data:=INST_DATA(cl_projectlist.mcc_class,opened_lst)

    a,b,c:=f.reference()
    IF a > 3 -> direct reference to project "a" (a is the data of the object)
            /* + sub-functions "b" and "c"*/
        /*checks that a exists*/
        IF dataExists(a) = FALSE THEN RETURN FALSE
        obj:=a.self
        get(obj,MUIA_PlaneFunc_Lock,{lock})
        IF lock = MUIV_PlaneFunc_Lock_Write THEN RETURN FALSE
        set(obj,MUIA_PlaneFunc_Lock,MUIV_PlaneFunc_Lock_Read)
        IF freeze(b) = 0
            set(obj,MUIA_PlaneFunc_Lock,MUIV_PlaneFunc_Lock_NoRead)
            RETURN FALSE
        ENDIF
        IF freeze(c) = 0
            free(b) -> IF "c" can't be frozen, let's free "b" before returning
            set(obj,MUIA_PlaneFunc_Lock,MUIV_PlaneFunc_Lock_NoRead)
            RETURN FALSE
        ENDIF
        RETURN TRUE
    ENDIF

    SELECT a
    CASE 2 -> One sub-function : "b"
        IF freeze(b) = 0 THEN RETURN FALSE
    CASE 3 -> Two sub functions : "b" and "c"
        IF freeze(b) = 0 THEN RETURN FALSE
        IF freeze(c) = 0
            free(b) -> IF "c" can't be frozen, let's free "b" before returning
            RETURN FALSE
        ENDIF
    ENDSELECT
ENDPROC TRUE

/*free(expression): This un read-locks all used functions.
It assumes that freeze(function) has been executed before and that
function did not change since*/

PROC free(f:PTR TO expression)
DEF a:PTR TO planeFunc_data,b,c,data:PTR TO projectList_data,obj

    data:=INST_DATA(cl_projectlist.mcc_class,opened_lst)

    IF f.precision > OUT_Float64 THEN RETURN TRUE

    a,b,c:=f.reference()
    IF a > 3
        obj:=a.self
        set(obj,MUIA_PlaneFunc_Lock,MUIV_PlaneFunc_Lock_NoRead)
        a:=3 -> This will cause a problem if a function f(c,x), where c is a project, ever comes.
    ENDIF
    SELECT a
    CASE 2 -> One sub-expression : "b"
        free(b)
    CASE 3 -> Two sub-expressions : "b" and "c"
        free(b)
        free(c)
    ENDSELECT
ENDPROC

/***************************************************************
**                       main program                         **
****************************************************************
 Opens the libraries, creates the user interface structure and
notifications. The input loop just waits for MUI to say to quit.
***************************************************************/

PROC main() HANDLE
DEF l
    IF (localebase:=OpenLibrary('locale.library',0)) = NIL THEN
        Raise('locale.library')
    NEW cat.create()
    cat.open()

    IF (aslbase:=OpenLibrary('asl.library',37))=NIL THEN
        Raise('asl.library')
    IF (datatypesbase:=OpenLibrary('datatypes.library',0))=NIL THEN
        Raise('datatypes.library')
    IF (guigfxbase:=OpenLibrary('guigfx.library',10))=NIL THEN
        Raise('guiGFX.library')
    IF (iffparsebase:=OpenLibrary('iffparse.library',0))=NIL THEN
        Raise('iffparse.library')
    IF (muimasterbase:=OpenLibrary(MUIMASTER_NAME,MUIMASTER_VMIN)) = NIL THEN
        Raise('muimaster.library')
    IF (utilitybase:=OpenLibrary('utility.library',36))=NIL THEN
        Raise('utility.library') /*for tag-list parsing*/

    funcdir:=String(19)
    StrCopy(funcdir,'progdir:')
    picdir:=String(18)
    StrCopy(picdir,'progdir:')

    init_fork([FORK_NAME,'picFX-Render Task',TAG_DONE])

    dInit(TRUE)

    NEW pl_displayer
    installhook(pl_displayer,{projectList_displayer})

    cl_planefunc:=eMui_CreateCustomClass(NIL,MUIC_Area ,NIL,SIZEOF planeFunc_data,{planeFunc_dispatcher})
    cl_projectlist:=eMui_CreateCustomClass(NIL,MUIC_List,NIL,SIZEOF projectList_data,{projectList_dispatcher})
    cl_uslider:=eMui_CreateCustomClass(NIL,MUIC_Slider,NIL,SIZEOF uslider_data,{uslider_dispatcher})
    cl_sizetxt:=eMui_CreateCustomClass(NIL,MUIC_Text,NIL,SIZEOF sizetxt_data,{sizetxt_dispatcher})
    cl_projecthold:=eMui_CreateCustomClass(NIL,MUIC_Text,NIL,SIZEOF projecthold_data,{projecthold_dispatcher})
    cl_myapp:=eMui_CreateCustomClass(NIL,MUIC_Application,NIL,SIZEOF empty_data,{myapp_dispatcher})

    ftype_registertitles:=[cat.msgl_type_Image.getstr(),cat.msgl_type_Function.getstr(),NIL]
    size_radiolabels:=[cat.msgl_lm_Scale.getstr(),cat.msgl_lm_CutTile.getstr(),cat.msgl_lm_Change.getstr(),NIL]
    showme_cyclelabels:=[cat.msgl_Hidden.getstr(),cat.msgl_Shown.getstr(),NIL]
    ccc() -> VarList

    FOR l:=0 TO 14
        messages[l]:=String(100)
    ENDFOR

    abouttext:=String(StrLen(cat.msg_AboutText.getstr())+StrLen(PICVERSION)+6)
    StringF(abouttext,'\ec\s\n\n\s',PICVERSION,cat.msg_AboutText.getstr())

    ->  NM_ITEM, Ø,'Text',               'shortcut',opt,(xcl),id
    menus:= [
    NM_TITLE,0,'picFX',0,0,0,0,
        NM_ITEM, 0,cat.msgn_AboutPicFX.getstr(),'?', 0,     0, MENU_G_About,
        NM_ITEM, 0, cat.msgn_AboutMui.getstr(), '_', 0,     0, MENU_G_AboutMui,
        NM_ITEM, 0, cat.msgl_Prefs.getstr(),    'F', 0,     0, MENU_G_Prefs,
        NM_ITEM, 0, cat.msgp_MuiPrefs.getstr(), 'D', 0,     0, MENU_G_PrefsMui,
        NM_ITEM, 0, NM_BARLABEL,                 0,  0,     0, 0,
        NM_ITEM, 0, cat.msgn_Animation.getstr(),'A', 0,     0, MENU_G_Animation,
        NM_ITEM, 0, cat.msgl_VarListWin.getstr(),'V',0,     0, MENU_G_Varlist,
        NM_ITEM, 0, cat.msgn_NewProject.getstr(),'N',0,     0, MENU_G_New,
        NM_ITEM, 0, NM_BARLABEL,                 0,  0,     0, 0,
        NM_ITEM, 0, cat.msgn_Hide.getstr(),     'H', 0,     0, MENU_G_Hide,
        NM_ITEM, 0, cat.msgn_Quit.getstr(),     'Q', 0,     0, MUIV_Application_ReturnID_Quit,
    NM_TITLE,0,'project',0,NM_MENUDISABLED,0,MENU_Project, -> I put an Id here to alow dis/enabling it.
        NM_ITEM, 0, cat.msgl_Render.getstr(),   'R', 0,     0, MENU_P_Render,
        NM_ITEM, 0, cat.msgl_Abort.getstr(),    'Z', 0,     0, MENU_P_Abort,
        NM_ITEM, 0, NM_BARLABEL,                 0,  0,     0, 0,
        NM_ITEM, 0, cat.msgn_Visible.getstr(),   0,CHECKIT, 0, MENU_P_Visible, -> Does not seem to accept shortcut for a checkit...
        NM_ITEM, 0, cat.msgl_Close.getstr(),    'K', 0,     0, MENU_P_Close,
        NM_ITEM, 0, cat.msgl_SavePic.getstr(),  'P', 0,     0, MENU_P_SaveP,
        NM_ITEM, 0, cat.msgn_ClearPic.getstr(),  0,  0,     0, MENU_P_Clear,
        NM_ITEM, 0, NM_BARLABEL,                 0,  0,     0, 0,
        NM_ITEM, 0, cat.msgl_LoadFunc.getstr(), 'L', 0,     0, MENU_P_LoadF,
        NM_ITEM, 0, cat.msgl_SaveFunc.getstr(), 'S', 0,     0, MENU_P_SaveF,
    NM_END,0,NIL,0,0,0,0]:newmenu

    /*Application structure. It might be nicer to create the window upon need instead of everything at start!*/

    app:=MyApplicationObject,
        MUIA_Application_Title, 'picFX',
        MUIA_Application_Version,FULLVERSION,
        MUIA_Application_Author, 'Maxime Gamboni',
        MUIA_Application_Description, cat.msg_Description.getstr(),
        MUIA_Application_Base,'picFX',
        MUIA_Application_Menustrip,menus:=Mui_MakeObjectA(MUIO_MenustripNM,[menus,0]),

        SubWindow,varlist_win:=WindowObject, -> This window must come before the project_list creation for initfuncs()
            MUIA_Window_Title,cat.msgl_VarListWin.getstr(),
            MUIA_Window_ID,"VARS",
            WindowContents,VGroup,
                Child, varlist_lsv:=ListviewObject,
                    InputListFrame,
                    MUIA_Listview_List,varlist_lst:=VarListObject,End,
                End,
                Child, HGroup,
                    Child, varname_str:=StringObject,
                        StringFrame,
                        MUIA_Disabled,MUI_TRUE,
                        MUIA_String_AttachedList,varlist_lsv,
                        MUIA_CycleChain,MUI_TRUE,
                    End,
                    Child, TextObject,
                        MUIA_Text_Contents,'\ec=',
                    End,
                    Child, varval_str:=StringObject,
                        StringFrame,
                        MUIA_Disabled,MUI_TRUE,
                        MUIA_String_AttachedList,varlist_lsv,
                        MUIA_CycleChain,MUI_TRUE,
                    End,
                End,
                Child, HGroup,
                    Child, varadd_btn:=SimpleButton(cat.msgl_Add.getstr()),
                    Child, vardel_btn:=SimpleButton(cat.msgl_Del.getstr()),
                End,
            End,
        End,
        SubWindow, settings_win:=WindowObject,
            MUIA_Window_Title, cat.msgl_Title.getstr(),
            MUIA_Window_ScreenTitle, PICVERSION,
            MUIA_Window_ID,"SETT",
            WindowContents,HGroup,
                Child, VGroup,
                    MUIA_Weight,30,
                    Child, TextObject,
                        NoFrame,
                        MUIA_Text_PreParse,'\ec',
                        MUIA_Text_Contents,cat.msgl_OpenedProjects.getstr(),
                    End,
                    Child, opened_lsv:=ListviewObject,
                        InputListFrame,
                        MUIA_Listview_DragType,MUIV_Listview_DragType_Immediate,
                        MUIA_Listview_List,opened_lst:=ProjectListObject,MUIA_List_DisplayHook,pl_displayer,End,
                    End,
                    Child, new_btn:=SimpleButton(cat.msgl_New.getstr()),
                    Child, var_btn:=TextObject,
                        ButtonFrame,
                        MUIA_Background,MUII_ButtonBack,
                        MUIA_Text_PreParse,'\ec',
                        MUIA_Text_Contents,cat.msgl_VarList.getstr(),
                        MUIA_InputMode,MUIV_InputMode_Toggle,
                    End,
                    Child, RectangleObject,MUIA_Rectangle_HBar,MUI_TRUE,MUIA_Weight,0,End,
                    Child, prefs_btn:=TextObject,
                        ButtonFrame,
                        MUIA_Background,MUII_ButtonBack,
                        MUIA_Text_PreParse,'\ec',
                        MUIA_Text_Contents,cat.msgl_Prefs.getstr(),
                        MUIA_InputMode,MUIV_InputMode_RelVerify,
                    End,
                End,
                Child, RectangleObject,MUIA_Rectangle_VBar,MUI_TRUE,MUIA_Weight,0,End,
                Child, VGroup,
                    Child, sgroup:=VGroup,
                        MUIA_Disabled,MUI_TRUE, -> No projects are opened at beginning
                        Child,name_str:= StringObject,MUIA_CycleChain,1,MUIA_String_Contents,cat.msg_Unnamed.getstr(),StringFrame,End,
                        Child, type_reg:=RegisterGroup(ftype_registertitles),
                            Child, VGroup,
                                Child, HGroup,
                                    Child, FreeLabel(cat.msgl_Path.getstr()),
                                    Child, PopaslObject,
                                        MUIA_Popstring_Button,PopButton(MUII_PopFile),
                                        MUIA_Popasl_Type,ASL_FILEREQUEST,
                                        MUIA_Popstring_String,path_str:=StringObject,
                                            StringFrame,
                                            MUIA_String_Contents,cat.msg_Unnamed.getstr(),
                                        End,
                                    End,
                                End,
                                Child, HGroup,
                                    Child, size_rad:=RadioObject,
                                        GroupFrameT(cat.msgl_Size.getstr()),
                                        MUIA_Radio_Entries,size_radiolabels,
                                        MUIA_Radio_Active,1,
                                    End,
                                    Child, VGroup,
                                        Child, RectangleObject,End,
                                        Child, size_txt:=SizeTextObject,
                                            TextFrame,
                                            MUIA_Text_PreParse,'\ec',
                                            MUIA_Text_Contents,cat.msgl_PicSize.getstr(),
                                        End,
                                        Child, RectangleObject,End,
                                    End,
                                End,
                            End,
                            Child, VGroup,
                                Child, HGroup,
                                    Child,red_fail:=LampObject,
                                        MUIA_Lamp_Type,MUIV_Lamp_Type_Big,
                                        MUIA_Lamp_ColorType,MUIV_Lamp_ColorType_UserDefined,
                                        MUIA_Lamp_Color,MUIV_Lamp_Color_Ok,
                                    End,
                                    Child, r_lbl:=label('r(#,x,y)='),
                                    Child, red_str:=StringObject,
                                        StringFrame,
                                        MUIA_CycleChain,1,
                                    End,
                                End,
                                Child, HGroup,
                                    Child, Label(cat.msgl_Output.getstr()),
                                    Child, outr_cyc:=CycleObject,
                                        MUIA_Cycle_Entries,[cat.msgl_Int32.getstr(),cat.msgl_Float32.getstr(),cat.msgl_Float64.getstr(),
                                                            cat.msgl_OldR.getstr(),cat.msgl_OldG.getstr(),cat.msgl_OldB.getstr(),NIL],
                                        MUIA_Cycle_Active,3,
                                        MUIA_CycleChain,1,
                                    End,
                                    Child, RectangleObject,End,
                                End,

                                Child, HGroup,
                                    Child,green_fail:=LampObject,
                                        MUIA_Lamp_Type,MUIV_Lamp_Type_Big,
                                        MUIA_Lamp_ColorType,MUIV_Lamp_ColorType_UserDefined,
                                        MUIA_Lamp_Color,MUIV_Lamp_Color_Ok,
                                    End,
                                    Child, g_lbl:=label('g(#,x,y)='),
                                    Child, green_str:=StringObject,
                                        StringFrame,
                                        MUIA_CycleChain,1,
                                    End,
                                End,
                                Child, HGroup,
                                    Child, Label(cat.msgl_Output.getstr()),
                                    Child, outg_cyc:=CycleObject,
                                        MUIA_Cycle_Entries,[cat.msgl_Int32.getstr(),cat.msgl_Float32.getstr(),cat.msgl_Float64.getstr(),
                                                            cat.msgl_OldR.getstr(),cat.msgl_OldG.getstr(),cat.msgl_OldB.getstr(),
                                                            cat.msgl_CopyR.getstr(),NIL],
                                        MUIA_Cycle_Active,3,
                                        MUIA_CycleChain,1,
                                    End,
                                    Child, RectangleObject,End,
                                End,

                                Child, HGroup,
                                    Child,blue_fail:=LampObject,
                                        MUIA_Lamp_Type,MUIV_Lamp_Type_Big,
                                        MUIA_Lamp_ColorType,MUIV_Lamp_ColorType_UserDefined,
                                        MUIA_Lamp_Color,MUIV_Lamp_Color_Ok,
                                    End,
                                    Child, b_lbl:=label('b(#,x,y)='),
                                    Child, blue_str:=StringObject,
                                        StringFrame,
                                        MUIA_CycleChain,1,
                                    End,
                                End,
                                Child, HGroup,
                                    Child, Label(cat.msgl_Output.getstr()),
                                    Child, outb_cyc:=CycleObject,
                                        MUIA_Cycle_Entries,[cat.msgl_Int32.getstr(),cat.msgl_Float32.getstr(),cat.msgl_Float64.getstr(),
                                                            cat.msgl_OldR.getstr(),cat.msgl_OldG.getstr(),cat.msgl_OldB.getstr(),
                                                            cat.msgl_CopyR.getstr(),cat.msgl_CopyG.getstr(),NIL],
                                        MUIA_Cycle_Active,3,
                                        MUIA_CycleChain,1,
                                    End,
                                    Child, RectangleObject,End,
                                End,
                            End,
                        End,
                        Child, HGroup,
                            Child, TextObject,
                                MUIA_Text_PreParse,'\ec',
                                MUIA_Text_Contents,cat.msgl_Width.getstr(),
                            End,
                            Child, TextObject,
                                MUIA_Text_PreParse,'\ec',
                                MUIA_Text_Contents,cat.msgl_Height.getstr(),
                            End,
                        End,
                        Child, HGroup,
                            Child, width_str:=StringObject,
                                StringFrame,
                                MUIA_String_Accept,'0123456789',
                            End,
                            Child, height_str:=StringObject,
                                StringFrame,
                                MUIA_String_Accept,'0123456789',
                            End,
                        End,
                        Child, GroupObject,MUIA_Group_Rows,2,
                            Child, draw_btn:=TextObject,
                                ButtonFrame,
                                MUIA_Background,MUII_ButtonBack,
                                MUIA_Text_PreParse,'\ec',
                                MUIA_Text_Contents,cat.msgl_Render.getstr(),
                                MUIA_InputMode,MUIV_InputMode_RelVerify,
                            End,
                            Child, savepic_btn:=SimpleButton(cat.msgl_SavePic.getstr()),
                            Child, savefunc_btn:=SimpleButton(cat.msgl_SaveFunc.getstr()),
                            Child, abort_btn:=SimpleButton(cat.msgl_Abort.getstr()),
                            Child, showme_cyc:=CycleObject,
                                ButtonFrame,
                                MUIA_Cycle_Entries,showme_cyclelabels,
                                MUIA_Cycle_Active,1,
                            End,
                            Child, close_btn:=SimpleButton(cat.msgl_Close.getstr()),
                            Child, loadfunc_btn:=SimpleButton(cat.msgl_LoadFunc.getstr()),
                            Child, clear_btn:=SimpleButton(cat.msgl_Clear.getstr()),
                        End,
                    End,
                    Child, stat_grp:=HGroup,
                        Child,lamp_lmp:=LampObject,
                            MUIA_Lamp_Type,MUIV_Lamp_Type_Big,
                            MUIA_Lamp_ColorType,MUIV_Lamp_ColorType_UserDefined,
                            MUIA_Lamp_Color,MUIV_Lamp_Color_Off,
                        End,
                        Child,state_txt:=TextObject,
                            MUIA_Text_Contents,cat.msgs_Idle.getstr(),
                            MUIA_Weight,50,
                        End,
                        Child,state_gau:=GaugeObject,
                            TextFrame,
                            MUIA_Gauge_Horiz , MUI_TRUE,
                        End,
                    End,
                End,
            End,
        End,
        SubWindow,prefs_win:=WindowObject,
            MUIA_Window_Title,cat.msgl_Prefs.getstr(),
            MUIA_Window_ID,"PREF",
            WindowContents,VGroup,
                Child, HGroup,
                    Child, VGroup,
                        GroupFrameT(cat.msgp_Priority.getstr()),
                        Child, mainpri_sld:=uSliderObject,
                            GroupFrameT(cat.msgp_MainTask.getstr()),
                            MUIA_Slider_Min,-10,
                            MUIA_Slider_Max,10,
                            MUIA_Slider_Level,0,
                        End,
                        Child, currpri_sld:=uSliderObject,
                            GroupFrameT(cat.msgp_CurrentTask.getstr()),
                            MUIA_Slider_Min,-10,
                            MUIA_Slider_Max,10,
                            MUIA_Slider_Level,-1,
                        End,
                        Child, renderpri_sld:=uSliderObject,
                            GroupFrameT(cat.msgp_RenderTask.getstr()),
                            MUIA_Slider_Min,-10,
                            MUIA_Slider_Max,10,
                            MUIA_Slider_Level,-2,
                        End,
                    End,
                    Child, VGroup,
                        Child, HGroup,
                            Child,recompile_chk:=CheckMark(MUI_TRUE),
                            Child,FreeLabel(cat.msgp_Recompile.getstr()),
                        End,
                        Child, renderupdate_sld:=uSliderObject,
                            MUIA_Disabled,MUI_TRUE,
                            GroupFrameT(cat.msgp_RenderUpd.getstr()),
                            MUIA_uSlider_Stringify,MUI_TRUE,
                            MUIA_Slider_Min,0,
                            MUIA_Slider_Max,40,
                            MUIA_Slider_Level,10,
                        End,
                        Child, HGroup,
                            Child, FreeLabel(cat.msgp_Close.getstr()),
                            Child, close_cyc:=CycleObject,
                                MUIA_Cycle_Entries,[cat.msgl_Iconify.getstr(),cat.msgl_Close.getstr(),NIL],
                                MUIA_Cycle_Active,0,
                            End,
                        End,
                        Child, HGroup,
                            Child, showmess_chk:=CheckMark(MUI_TRUE),
                            Child, FreeLabel(cat.msgp_ShowMw.getstr()),
                        End,
                    End,
                    Child, VGroup,
                        Child, HGroup,
                            Child, FreeLabel(cat.msgp_DithM.getstr()),
                            Child, dither_cyc:=CycleObject,
                                MUIA_Cycle_Entries,[cat.msgp_diNone.getstr(),'EDD','Floyd Steinberg',cat.msgp_diRand.getstr(),NIL],
                                MUIA_Cycle_Active,2,
                            End,
                        End,
                        Child, dither_sld:=SliderObject,
                            GroupFrameT(cat.msgp_DithA.getstr()),
                            MUIA_Slider_Min,0,
                            MUIA_Slider_Max,255,
                            MUIA_Slider_Level,40,
                        End,
                    End,
                End,
                Child, HGroup,
                    Child, RectangleObject,End,
                    Child, savep_btn:=SimpleButton(cat.msgp_SavePrefs.getstr()),
                    Child, mui_btn:=TextObject,
                        ButtonFrame,
                        MUIA_Background,MUII_ButtonBack,
                        MUIA_Text_SetMax,MUI_TRUE,
                        MUIA_Text_PreParse,'\ec',
                        MUIA_Text_Contents,cat.msgp_MuiPrefs.getstr(),
                        MUIA_InputMode,MUIV_InputMode_RelVerify,
                    End,
                    Child, RectangleObject,End,
                End,
            End,
        End,
        SubWindow,messages_win:=WindowObject,
            MUIA_Window_Title,cat.msgm_NoMsg.getstr(),
            MUIA_Window_ID,"MESS",
            MUIA_Window_CloseGadget,FALSE,
            WindowContents,VGroup,
                Child,ListviewObject,
                    -> MUIA_VertDisappear,50, -> This does not work :-(
                    MUIA_Listview_Input,FALSE,
                    ReadListFrame,
                    MUIA_Listview_List,messages_lst:=ListObject,
                        MUIA_List_SourceArray,messages,
                    End,
                End,
                Child, colour_txt:=TextObject,
                    TextFrame,
                    MUIA_Background,MUII_TextBack,
                    MUIA_Text_Contents,'c(p,x,y)=[r,g,b]',
                End,
            End,
        End,
        SubWindow,about_win:=WindowObject,
            MUIA_Window_Title,cat.msgn_AboutPicFX.getstr(),
            MUIA_Window_ID,"ABT",
            WindowContents,VGroup,
                Child, HGroup,
                    Child,RectangleObject,End,
                    Child, ImageObject,
                        TextFrame,
                        MUIA_Image_FreeHoriz,MUI_TRUE,
                        MUIA_FixWidth,300,MUIA_FixHeight,150,
                        MUIA_Image_FreeVert,MUI_TRUE,
                        MUIA_Image_Spec,'5:progdir:picFXlogo',
                    End,
                    Child, RectangleObject,End,
                End,
                Child, FloattextObject,
                    TextFrame,MUIA_Background,MUII_TextBack,
                    MUIA_Floattext_Text,abouttext,
                End,
            End,
        End,
        SubWindow, animate_win:=WindowObject,
            MUIA_Window_Title,cat.msgn_Animation.getstr(),
            MUIA_Window_ID,"ANIM",
            WindowContents,VGroup,
                Child, HGroup,
                    Child, label(cat.msga_Parameter.getstr()),
                    Child, parameter_str:=StringObject,
                        StringFrame,
                        MUIA_String_Contents,'t',
                    End,
                    Child, label(cat.msga_FrameCount.getstr()),
                    Child, frames_str:=StringObject,
                        StringFrame,
                        MUIA_String_Accept,'0123456789',
                        MUIA_String_Contents,'50',
                    End,
                End,
                Child, HGroup,
                    Child, VGroup,
                        Child, TextObject,
                            NoFrame,
                            MUIA_Text_Contents,cat.msga_RenderThese.getstr(),
                        End,
                        Child,render_lsv:=ListviewObject,
                            InputListFrame,
                            MUIA_Listview_DragType,MUIV_Listview_DragType_Immediate,
                            MUIA_Listview_List,render_lst:=ListObject,
                                MUIA_List_DragSortable,MUI_TRUE,
                                MUIA_List_DisplayHook,pl_displayer,
                            End,
                        End,
                        Child, HGroup,
                            Child, insertP_btn:=SimpleButton(cat.msga_Insert.getstr()),
                            Child, delP_btn:=SimpleButton(cat.msgl_Del.getstr()),
                        End,
                    End,
                    Child, VGroup,
                        Child, RectangleObject,End,
                        Child, HGroup,
                            Child, label(cat.msga_SaveThis.getstr()),
                            Child, saveP:=ProjectholdObject,
                                TextFrame,
                                MUIA_Background,MUII_TextBack,
                                MUIA_Text_Contents,cat.msgl_DropHere.getstr(),
                            End,
                        End,
                        Child, HGroup,
                            Child, label(cat.msga_FileBase.getstr()),
                            Child, savename_str:=StringObject,
                                StringFrame,
                                MUIA_String_Contents,'Ram:anim',
                            End,
                        End,    
                        Child, RectangleObject,End,
                    End,
                End,
                Child, HGroup,
                    Child, RectangleObject,End,
                    Child, doAnim_btn:=SimpleButton(cat.msga_StartAnimation.getstr()),
                    Child, RectangleObject,End,
                End,
                Child, animstatus_txt:=TextObject,
                    TextFrame,
                    MUIA_Background,MUII_TextBack,
                    MUIA_Text_Contents,cat.msgs_Idle.getstr(),
                End,
            End,
        End,
    End

    IF app <> NIL
/*Let's do all notifications now*/

        doMethodA(settings_win,[MUIM_Notify,MUIA_Window_CloseRequest,MUI_TRUE,app,2,MUIM_Application_ReturnID,MUIV_Application_ReturnID_Quit])
        doMethodA(messages_win,[MUIM_Notify,MUIA_Window_CloseRequest,MUI_TRUE,showmess_chk,3,MUIM_Set,MUIA_Selected,FALSE])
        doMethodA(about_win,   [MUIM_Notify,MUIA_Window_CloseRequest,MUI_TRUE,about_win,3,MUIM_Set,MUIA_Window_Open,FALSE])
        doMethodA(animate_win, [MUIM_Notify,MUIA_Window_CloseRequest,MUI_TRUE,animate_win,3,MUIM_Set,MUIA_Window_Open,FALSE])

        doMethodA(new_btn,     [MUIM_Notify,MUIA_Pressed,FALSE,opened_lst,1,MUIM_ProjectList_NewItem])
/*Variable List handling*/
        doMethodA(varlist_lst,[MUIM_VariableList_Fields,varname_str,varval_str])
        doMethodA(var_btn,     [MUIM_Notify,MUIA_Selected,MUIV_EveryTime,varlist_win,3,MUIM_Set,MUIA_Window_Open,MUIV_TriggerValue])
        doMethodA(varlist_win, [MUIM_Notify,MUIA_Window_CloseRequest,MUI_TRUE,var_btn,3,MUIM_Set,MUIA_Selected,FALSE])
        doMethodA(varlist_lst, [MUIM_Notify,MUIA_List_Active,MUIV_EveryTime,varlist_lst,3,MUIM_VariableList_UpdateStr,varname_str,varval_str])
        doMethodA(varname_str, [MUIM_Notify,MUIA_String_Acknowledge,MUIV_EveryTime,varlist_lst,3,MUIM_VariableList_SetVar,MUIV_VariableList_SetVar_Name,MUIV_TriggerValue])
        doMethodA(varval_str,  [MUIM_Notify,MUIA_String_Acknowledge,MUIV_EveryTime,varlist_lst,3,MUIM_VariableList_SetVar,MUIV_VariableList_SetVar_Value,MUIV_TriggerValue])
        doMethodA(varname_str, [MUIM_Notify,MUIA_String_Acknowledge,MUIV_EveryTime,varlist_win,3,MUIM_Set,MUIA_Window_ActiveObject,varname_str])
        doMethodA(varval_str,  [MUIM_Notify,MUIA_String_Acknowledge,MUIV_EveryTime,varlist_win,3,MUIM_Set,MUIA_Window_ActiveObject,varval_str])

        doMethodA(varadd_btn,  [MUIM_Notify,MUIA_Pressed,FALSE,varlist_lst,1,MUIM_VariableList_AddVar])
        doMethodA(vardel_btn,  [MUIM_Notify,MUIA_Pressed,FALSE,varlist_lst,2,MUIM_List_Remove,MUIV_List_Remove_Active])
/*preferences*/
        doMethodA(prefs_win,       [MUIM_Notify,MUIA_Window_CloseRequest,MUI_TRUE,prefs_win,3,MUIM_Set,MUIA_Window_Open,FALSE])
        doMethodA(prefs_btn,       [MUIM_Notify,MUIA_Pressed,     FALSE,         prefs_win,3,MUIM_Set,MUIA_Window_Open,MUI_TRUE])

        doMethodA(mui_btn,         [MUIM_Notify,MUIA_Pressed,     FALSE,         app,1,MUIM_Application_OpenConfigWindow])
        doMethodA(savep_btn,       [MUIM_Notify,MUIA_Pressed,     FALSE,         opened_lst,1,MUIM_ProjectList_SavePrefs])
        doMethodA(mainpri_sld,     [MUIM_Notify,MUIA_Slider_Level,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_SetPri,MUIV_ProjectList_SetPri_Main,MUIV_TriggerValue])
        doMethodA(currpri_sld,     [MUIM_Notify,MUIA_Slider_Level,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_SetPri,MUIV_ProjectList_SetPri_Curr,MUIV_TriggerValue])
        doMethodA(renderpri_sld,   [MUIM_Notify,MUIA_Slider_Level,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_SetPri,MUIV_ProjectList_SetPri_Render,MUIV_TriggerValue])
        doMethodA(renderupdate_sld,[MUIM_Notify,MUIA_Slider_Level,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_Prefs,{rupd},MUIV_TriggerValue])
        doMethodA(close_cyc,       [MUIM_Notify,MUIA_Cycle_Active,MUIV_EveryTime,app,3,MUIM_WriteLong,MUIV_TriggerValue,{wclose}])
        doMethodA(showmess_chk,    [MUIM_Notify,MUIA_Selected    ,MUIV_EveryTime,messages_win,3,MUIM_Set,MUIA_Window_Open,MUIV_TriggerValue])
        doMethodA(dither_cyc,      [MUIM_Notify,MUIA_Cycle_Active,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_Prefs,{dithm},MUIV_TriggerValue])
        doMethodA(dither_sld,      [MUIM_Notify,MUIA_Slider_Level,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_Prefs,{ditha},MUIV_TriggerValue])
/*mainpri >= currpri >= renderpri*/
        doMethodA(mainpri_sld,[MUIM_Notify,MUIA_Slider_Level,MUIV_EveryTime,currpri_sld,2,MUIM_uSlider_Smaller,MUIV_TriggerValue])
        doMethodA(mainpri_sld,[MUIM_Notify,MUIA_Slider_Level,MUIV_EveryTime,renderpri_sld,2,MUIM_uSlider_Smaller,MUIV_TriggerValue])
        doMethodA(currpri_sld,[MUIM_Notify,MUIA_Slider_Level,MUIV_EveryTime,renderpri_sld,2,MUIM_uSlider_Smaller,MUIV_TriggerValue])
        doMethodA(currpri_sld,[MUIM_Notify,MUIA_Slider_Level,MUIV_EveryTime,mainpri_sld,2,MUIM_uSlider_Bigger,MUIV_TriggerValue])
        doMethodA(renderpri_sld,[MUIM_Notify,MUIA_Slider_Level,MUIV_EveryTime,mainpri_sld,2,MUIM_uSlider_Bigger,MUIV_TriggerValue])
        doMethodA(renderpri_sld,[MUIM_Notify,MUIA_Slider_Level,MUIV_EveryTime,currpri_sld,2,MUIM_uSlider_Bigger,MUIV_TriggerValue])
/*Every change to a gadget is sent to the ProjectList class*/
        doMethodA(name_str,    [MUIM_Notify,MUIA_String_Contents,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_Notify,MUIV_ProjectList_Notify_Name,MUIV_TriggerValue])
        doMethodA(type_reg,    [MUIM_Notify,MUIA_Group_ActivePage,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_Notify,MUIV_ProjectList_Notify_Type,MUIV_TriggerValue])
        doMethodA(path_str,    [MUIM_Notify,MUIA_String_Contents,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_Notify,MUIV_ProjectList_Notify_ImageFile,MUIV_TriggerValue])
        doMethodA(size_rad,    [MUIM_Notify,MUIA_Radio_Active,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_Notify,MUIV_ProjectList_Notify_LoadM,MUIV_TriggerValue])
        doMethodA(red_str,     [MUIM_Notify,MUIA_String_Acknowledge,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_Notify,MUIV_ProjectList_Notify_Red,MUIV_TriggerValue])
        doMethodA(green_str,   [MUIM_Notify,MUIA_String_Acknowledge,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_Notify,MUIV_ProjectList_Notify_Green,MUIV_TriggerValue])
        doMethodA(blue_str,    [MUIM_Notify,MUIA_String_Acknowledge,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_Notify,MUIV_ProjectList_Notify_Blue,MUIV_TriggerValue])
        doMethodA(outr_cyc,    [MUIM_Notify,MUIA_Cycle_Active,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_Notify,MUIV_ProjectList_Notify_OutputR,MUIV_TriggerValue])
        doMethodA(outg_cyc,    [MUIM_Notify,MUIA_Cycle_Active,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_Notify,MUIV_ProjectList_Notify_OutputG,MUIV_TriggerValue])
        doMethodA(outb_cyc,    [MUIM_Notify,MUIA_Cycle_Active,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_Notify,MUIV_ProjectList_Notify_OutputB,MUIV_TriggerValue])
        doMethodA(width_str,   [MUIM_Notify,MUIA_String_Acknowledge,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_Notify,MUIV_ProjectList_Notify_Width,MUIV_TriggerValue])
        doMethodA(height_str,  [MUIM_Notify,MUIA_String_Acknowledge,MUIV_EveryTime,opened_lst,3,MUIM_ProjectList_Notify,MUIV_ProjectList_Notify_Height,MUIV_TriggerValue])
/*If the active element of the Opened Projects List has changed, the gadgets must be refreshed*/
        doMethodA(opened_lst,  [MUIM_Notify,MUIA_List_Active,MUIV_EveryTime,opened_lst,1,MUIM_ProjectList_Refresh,0])
/*Action buttons:ProjectList class will forward those messages to the selected project*/
        doMethodA(draw_btn,    [MUIM_Notify,MUIA_Pressed,FALSE,opened_lst,3,MUIM_ProjectList_Forward,1,MUIM_PlaneFunc_Render])
        doMethodA(savepic_btn, [MUIM_Notify,MUIA_Pressed,FALSE,opened_lst,4,MUIM_ProjectList_Forward,2,MUIM_PlaneFunc_SavePic,FALSE])
        doMethodA(savefunc_btn,[MUIM_Notify,MUIA_Pressed,FALSE,opened_lst,3,MUIM_ProjectList_Forward,1,MUIM_PlaneFunc_SaveFunc])
        doMethodA(loadfunc_btn,[MUIM_Notify,MUIA_Pressed,FALSE,opened_lst,3,MUIM_ProjectList_Forward,1,MUIM_PlaneFunc_LoadFunc])
        doMethodA(abort_btn,   [MUIM_Notify,MUIA_Pressed,FALSE,opened_lst,4,MUIM_ProjectList_Forward,1,MUIM_PlaneFunc_Abort,MUIV_PlaneFunc_Abort_Abort])
        doMethodA(clear_btn,   [MUIM_Notify,MUIA_Pressed,FALSE,opened_lst,3,MUIM_ProjectList_Forward,1,MUIM_PlaneFunc_Clear])
->Since the address of the current project is variable I can't do direct notification. Instead, there are two methods for ProjectList.
        doMethodA(opened_lsv,  [MUIM_Notify,MUIA_Listview_DoubleClick,MUI_TRUE,showme_cyc,3,MUIM_Set,MUIA_Cycle_Active,1])
        doMethodA(showme_cyc,  [MUIM_Notify,MUIA_Cycle_Active,MUIV_EveryTime,opened_lst,2,MUIM_ProjectList_ShowMe,MUIV_TriggerValue])
        doMethodA(close_btn,   [MUIM_Notify,MUIA_Pressed,FALSE,opened_lst,1,MUIM_ProjectList_DelItem])
->Handy shortcuts
        doMethodA(settings_win,[MUIM_Notify,MUIA_Window_InputEvent,'r',settings_win,3,MUIM_Set,MUIA_Window_ActiveObject,red_str])
        doMethodA(settings_win,[MUIM_Notify,MUIA_Window_InputEvent,'g',settings_win,3,MUIM_Set,MUIA_Window_ActiveObject,green_str])
        doMethodA(settings_win,[MUIM_Notify,MUIA_Window_InputEvent,'b',settings_win,3,MUIM_Set,MUIA_Window_ActiveObject,blue_str])
->Menu handing
        doMethodA(app,         [MUIM_Notify,MUIA_Application_MenuAction,MENU_G_About,about_win,3,MUIM_Set,MUIA_Window_Open,MUI_TRUE])
        doMethodA(app,         [MUIM_Notify,MUIA_Application_MenuAction,MENU_G_Prefs,prefs_win,3,MUIM_Set,MUIA_Window_Open,MUI_TRUE])
        doMethodA(app,         [MUIM_Notify,MUIA_Application_MenuAction,MENU_G_AboutMui,app,1,MUIM_Application_AboutMUI])
        doMethodA(app,         [MUIM_Notify,MUIA_Application_MenuAction,MENU_G_PrefsMui,app,1,MUIM_Application_OpenConfigWindow])
        doMethodA(app,         [MUIM_Notify,MUIA_Application_MenuAction,MENU_G_Hide,app,3,MUIM_Set,MUIA_Application_Iconified,MUI_TRUE])
        doMethodA(app,         [MUIM_Notify,MUIA_Application_MenuAction,MENU_G_Animation,animate_win,3,MUIM_Set,MUIA_Window_Open,MUI_TRUE])
        doMethodA(app,         [MUIM_Notify,MUIA_Application_MenuAction,MENU_G_Varlist,var_btn,3,MUIM_Set,MUIA_Selected,MUI_TRUE])
        doMethodA(app,         [MUIM_Notify,MUIA_Application_MenuAction,MENU_G_New,opened_lst,1,MUIM_ProjectList_NewItem])
        doMethodA(app,         [MUIM_Notify,MUIA_Application_MenuAction,MENU_P_Render,opened_lst,3,MUIM_ProjectList_Forward,1,MUIM_PlaneFunc_Render])
        doMethodA(app,         [MUIM_Notify,MUIA_Application_MenuAction,MENU_P_Abort, opened_lst,4,MUIM_ProjectList_Forward,2,MUIM_PlaneFunc_Abort,MUIV_PlaneFunc_Abort_Abort])
        l:=doMethodA(menus,[MUIM_FindUData,MENU_P_Visible]) -> get the address of the 'visible' menuitem
        doMethodA(l,           [MUIM_Notify,MUIA_Menuitem_Checked,MUIV_EveryTime,showme_cyc,3,MUIM_Set,MUIA_Cycle_Active,MUIV_TriggerValue])
        doMethodA(showme_cyc,  [MUIM_Notify,MUIA_Cycle_Active,MUIV_EveryTime,l,3,MUIM_NoNotifySet,MUIA_Menuitem_Checked,MUIV_TriggerValue]) -> There's a bit of circularity here (useless sometimes :-)
        doMethodA(app,         [MUIM_Notify,MUIA_Application_MenuAction,MENU_P_Close, opened_lst,1,MUIM_ProjectList_DelItem])
        doMethodA(app,         [MUIM_Notify,MUIA_Application_MenuAction,MENU_P_SaveP, opened_lst,3,MUIM_ProjectList_Forward,2,MUIM_PlaneFunc_SavePic,FALSE])
        doMethodA(app,         [MUIM_Notify,MUIA_Application_MenuAction,MENU_P_Clear, opened_lst,3,MUIM_ProjectList_Forward,1,MUIM_PlaneFunc_Clear])
        doMethodA(app,         [MUIM_Notify,MUIA_Application_MenuAction,MENU_P_LoadF, opened_lst,3,MUIM_ProjectList_Forward,1,MUIM_PlaneFunc_LoadFunc])
        doMethodA(app,         [MUIM_Notify,MUIA_Application_MenuAction,MENU_P_SaveF, opened_lst,3,MUIM_ProjectList_Forward,1,MUIM_PlaneFunc_SaveFunc])

/*Animation handling*/
        doMethodA(insertP_btn, [MUIM_Notify,MUIA_Pressed,FALSE,opened_lst,2,MUIM_ProjectList_InsertInList,render_lst])
        doMethodA(delP_btn,    [MUIM_Notify,MUIA_Pressed,FALSE,render_lst,2,MUIM_List_Remove,MUIV_List_Remove_Active])
        doMethodA(doAnim_btn,  [MUIM_Notify,MUIA_Pressed,FALSE,saveP,1,MUIM_Projecthold_Animate])

        loadprefs()

        set(settings_win,MUIA_Window_Open,MUI_TRUE)
        get(showmess_chk,MUIA_Selected,{l}) ;
        IF l THEN
           set(messages_win,MUIA_Window_Open,MUI_TRUE)
        doMethodA(opened_lst,[MUIM_ProjectList_Memory])
        message(-1,cat.msgm_Welcome.getstr())

        WHILE Not(doMethodA(app,[MUIM_Application_NewInput,{sigs}]) = MUIV_Application_ReturnID_Quit)
            IF sigs THEN sigs := Wait(sigs)
        ENDWHILE

    ELSE
        WriteF(cat.msg_NoApp.getstr())
    ENDIF
EXCEPT DO
    useprefs()

    IF opened_lst THEN
        doMethodA(opened_lst,[MUIM_ProjectList_EnableProgram])
    IF app THEN Mui_DisposeObject(app)
    IF cl_planefunc THEN Mui_DeleteCustomClass(cl_planefunc)
    IF cl_projectlist THEN Mui_DeleteCustomClass(cl_projectlist)
    IF cl_uslider THEN Mui_DeleteCustomClass(cl_uslider)
    IF cl_sizetxt THEN Mui_DeleteCustomClass(cl_sizetxt)
    IF cl_myapp THEN Mui_DeleteCustomClass(cl_myapp)
    IF cl_projecthold THEN Mui_DeleteCustomClass(cl_projecthold)
    dcc() -> VarList
    dCleanup()
    IF exception
        IF localebase
            WriteF(cat.msg_Nolib.getstr(),exception)
        ELSE
            WriteF('Failed to open \s.\n',exception)
        ENDIF
    ENDIF
    IF aslbase       THEN CloseLibrary(aslbase)
    IF datatypesbase THEN CloseLibrary(datatypesbase)
    IF guigfxbase    THEN CloseLibrary(guigfxbase)
    IF iffparsebase  THEN CloseLibrary(iffparsebase)
    IF muimasterbase THEN CloseLibrary(muimasterbase)
    IF utilitybase   THEN CloseLibrary(utilitybase)
    cat.close()
    IF localebase THEN CloseLibrary(localebase)
ENDPROC                     

PROC message(id,info:PTR TO CHAR) -> Writes a message in the message-list
DEF t
    FOR t:=0 TO 13
        StrCopy(messages[t],messages[t+1]) -> Warning, the addresses mustn't change!
    ENDFOR
    StringF(messages[14],'[\d] \s',id,info)
    doMethodA(messages_lst,[MUIM_List_Redraw,MUIV_List_Redraw_All])
    IF counter <> 1
        StringF(title,'\d \s',counter++,cat.msgm_Messages.getstr())
    ELSE
        StringF(title,'\d \s',counter++,cat.msgm_Message.getstr())
    ENDIF
    set(messages_win,MUIA_Window_Title,title)
ENDPROC

PROC messagefmt(id,info:PTR TO CHAR,arg1=0,arg2=0,arg3=0)
DEF s[100]:STRING
    StringF(s,info,arg1,arg2,arg3)
    message(id,s)
ENDPROC
