Aros/Developer/Docs/Libraries/Icon

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


Introduction[edit]

  • OLD - OS1.x (4 col) - OS 2/3 (4 col)
  • NEW - NewIcons (size 36x40 x 32 col) - GlowIcons (size 46x46 x 256 col) - PowerIcons (24bit)
  • AROS - 24bit png dual
  • SVG - Haiku HVIF has a reduced format.


Our "DefaultIcon" implementation was better (and more like the original) - it would be easy to add default Icons that use pattern matching rather than the less efficient datatype matching.

The default icons to specify possible operations for an Icon (thereby filetype) via the default icons tooltypes. The normal Icon behaviour would still be used for its default action but it could also expose specific actions for that file type, e.g.

ICON_ACTION="Preview","SYS:Utiltieis/MultiView"
ICON_ACTION="Edit","Extras:Gfx/LunaPaint"

Workbench (Wanderer) does not read anything directly itself - it relies on icon.library for "file" processing. The loading of PNG Icons does not happen using datatype *system*. It's done through libpng functions. To avoid that both icon.library and png.datatype have libpng statically linked in (double file/mem usage), it's done only for one of them: png.datatype. The png.datatype exposes some extra functions PNG_#?. See workbench/classes/datatypes/png/png.conf and directaccess.c in same directory.


In workbench/libs/icon/diskobjPNGio.c you can see how the PNG_ functions are used to read the PNG icon. In there you'll also see a function MakePlanarImage(). It's one function which will certainly slow down things a bit. What is does (in very unoptimized way) is to create a very simple realtime remapped planar version of the PNG image(s) to be used on low color (LUT) screens.


In libpng 1.5, png_ptr and info_ptr are opaque data types, and can't be dereferenced.

Replace that code with the following (which works in both libpng 1.2 and 1.5)

...
   Write(png_get_io_ptr(png_ptr), data, length);
...

The reason is that in a (near!) future version of libpng, the details of the png_ptr and info_ptr datastructures may change dramatically.

Changes are trivial, just grep for the old field name (ie 'io_ptr') in png.h, and use the appropriate accessor function. 

merge icon35 and png icons

  • png data would live in the IFF stream of the icon35 format (IFF type 'PNG ')
  • 4 color and icon35 image formats would be precomputed by the ilbmtoicon tool
  • AOS should be able to read and understand this format

Do OS4 and MorphOS use the same type of PNG icons as us, or a different variation? They use the same format (which PowerIcons originated), which is a plain PNG file named "*.info" with some extra PNG hunks in it to hold some icon data. (PNG is like IFF in that way).

Just to be clear, I am *not* planning on removing 'plain PNG' support. I am extending the Icon 3.5 to include PNG data, so that (a) our generated ICONs can work on any Amiga system, including OS 1.x to 3.x, and (b) my poor 68000 can choose a planar image from the icon file instead of spending approx 7 seconds per icon converting from ARGB to 4-color planar.

Existing PNG support will *not* be changed - so we should continue to be able to enjoy AmigaOS4 and Morphos icons sets on the faster processors.


Handling of icons by an AppWindow/Menu/Icon. That's how the concept of graphically dealing with files (represented as icon objects) from an already running application was implemented from Kickstart 2.04 on. So selecting icons isn't enough for any application to be aware of it: the user has to do one more action (which is to drag the selected icon(s) towards an AppWindow/Icon or to select an AppMenu).

And you're right, Wanderer only allows to use AppWindows yet.

So you need (well... I mean: so far, all that you can do is) to promote your window as an AppWindow, then listen to the AppMessage and act accordingly.

If you want to act on more than one icon at a time, you will need to adapt the code to browse AppMessage->am_ArgList as many times as AppMessage->am_NumArgs ...



Tooltype[edit]

from http://utilitybase.com/forum/index.php?action=vthread&forum=2&topic=1252

Be very careful with modifying the tooltypeslist in an icon.


  1. Read in an icon (Icon->GetdiskObject())
  2. Build a new tooltypeslist
  3. Copy the address of the current tooltypelist to a safe place
  4. Copy the address of the NEW tooltypeslist to the icon
  5. Rewrite the icon (IIcon->PutDiskObject())
  1. Copy the address of the old tooltypeslist from the safe place BACK into the icon
  2. Free the diskobject (IIcon->FreeDiskObject())

6 and 7 are not actually necessary. icon.library keeps track of all the data associated with a loaded icon through a mechanism attached to the 'struct DiskObject' itself, in the private undocumented part of the structure. It does not follow the pointers or assume that they point somewhere meaningful.

In essence, this means that you can modify every bit of information in the documented portion of the 'struct DiskObject' without ill effects. But you have to be careful that the information is in itself consistent if you want to write it back to disk. While icon.library does perform some sanity checking, especially icon.library V44 and beyond, there is only so much you can do.


#include <workbench/workbench.h>
#include <workbench/icon.h>
 
#include <proto/icon.h>
 
#include <stdio.h>
 
int main(int argc, char **argv)
{
    STRPTR newToolTypes[] =
    {
        "AROS=RULES",
        "Tooltypes are actually free-form text",
        "Ain't that neat?",
        NULL
    };
 
    struct DiskObject *icon = GetIconTags
    (
        "writetooltypes", ICONGETA_FailIfUnavailable, FALSE, TAG_DONE
    );
 
    if (icon != NULL)
    {
        STRPTR *oldToolTypes = icon->do_ToolTypes;
 
        icon->do_ToolTypes = newToolTypes;
        if (!PutIconTags("writetooltypes", icon, TAG_DONE))
        {
            printf("ERROR: Failed to write icon.\n");
        }
        icon->do_ToolTypes = oldToolTypes;
 
        FreeDiskObject(icon);
    }
    else
    {
        printf("ERROR: Failed to open icon for file\n");
        return 20;
    }
 
 
    return 0;
}



LayoutIcon[edit]

Currently, LayoutIcon is unimplemented, and an idea that could save me a lot of RAM for PNG images, when they are rendered on low-color screens. The idea is to use LayoutIcon() to render the icon into the Screen's bitmap layout, and use that as the gadget imagery for the icon, and FreeMem() the PNG or other high-color icon imagery.

This is similar to how AOS does things, but with one trick - I 'remember' what image format (and offset and size in the .icon file) was originally there, and when PutDiskObject() is called I carefully avoid scribbling on the image data, preserving the original image.

But you need update the images, when a user change the screen depth. In AFA layoutIcon is too unimplement, i add some times before a jump in debugger command when function is called to test.if a program use this function.I see that workbench not use this function.also other programs do not use. But a speedup of AROS iconlib and much less memory usage is possible, when you make a cache, so that all default Icons only load once.getdiskobject give then only a pointer to existing default icon.

The problem on AROS is, it load every time the icon in and use new memory. For example if you show 100 jpg files, then 100* the icon def_jpg.info is load.so 100* memory usage and load time usage. As wanderer loads always all icons, even if only icon view is select.



transparent PNG[edit]

graphics.lbrary BltMaskBitMapRastPort is useful

static void Change_State(int Reset)
{
    int x;
 
    for(x=Levels_Struct[CurrentLevel].Beginning; x<IconCounter; x++)
    {
        if(Icons_Struct[x].Icon_OK)
        {
            if(Icons_Struct[x].Icon_Status != 0)
            {
                if(Reset == 0)
                {
                    if((Icons_Struct[x].Icon_Status & ICON_ACTIVE) == 0 && Icons_Struct[x].Icon_Status < 4)
                    {
                        // 11000111b
                        Icons_Struct[x].Icon_Status = (Icons_Struct[x].Icon_Status - 1) & 0xC7;
                    }
                    else
                    {
                        Icons_Struct[x].Icon_Status = (Icons_Struct[x].Icon_Status + 1) & 0xC7;
                    }
                }
                else
                {
                    Icons_Struct[x].Icon_Status = 0;
                }
 
                Insert_Icon((Icons_Struct[x].Icon_Status & SELECTED_ICON) ? IDS_SELECTED : IDS_NORMAL, x);
            }
        }
    }
}
 
static void Insert_Icon(int Tryb, int IconNumber)
{
    BltBitMapRastPort(BMP_Buffer,
        Icons_Struct[IconNumber].Icon_PositionX,
        0,
        &RP_DoubleBuffer, //Window_struct->RPort,
        0, //Icons_Struct[IconNumber].Icon_PositionX,
        0,
        Icons_Struct[IconNumber].Icon_Width + 1,
        WindowHeight,
        0xC0);
 
    DrawIconState(&RP_DoubleBuffer, //Window_struct->RPort,
        Icon[IconNumber],
        NULL,
        0, //Icons_Struct[IconNumber].Icon_PositionX,
        Icons_Struct[IconNumber].Icon_PositionY - MovingTable[(Icons_Struct[IconNumber].Icon_Status & 0x07)],
        Tryb,
        ICONDRAWA_Frameless, TRUE,
        ICONDRAWA_EraseBackground, FALSE,
        TAG_DONE);
 
    BltBitMapRastPort(BMP_DoubleBuffer,
        0,
        0,
        Window_struct->RPort,
        Icons_Struct[IconNumber].Icon_PositionX,
        0,
        Icons_Struct[IconNumber].Icon_Width + 1,
        WindowHeight,
        0xC0);
}
 
static void Blink_Icon(int IconNumber)
{
    int x, Tryb=IDS_SELECTED;
 
    for(x=0; x<8; x++)
    {
        Insert_Icon(Tryb, IconNumber);
        Delay(3);
 
        if(Tryb == IDS_NORMAL) Tryb = IDS_SELECTED;
        else Tryb = IDS_NORMAL;
    }
}




Datatype Detection[edit]

The icon.library uses the datatype descriptors from sys:devs/datatypes to identify the file type. Then a default icon def_NAME.info is used for a file with a type of NAME. The AROS build-system uses the tool createdtdesc to create datatype descriptors from simple text files.



Bugs[edit]

After icons can be moved, it will be good to be able to snapshot their position.

And the position of the window in the drawer icon. But beware that IIRC there is a bug/unimplemented feature somewhere (in icon.library?) that prevents it to store properly DrawerData struct (dd_Flags and dd_NewWindow members) in all cases. So Wanderer suffers of the problem too currently, and you can't both save the "View All Files" state _and_ snapshot the window position of a drawer.


  1. having a database in the dir containing mappings is also inefficient when working with individual files - perhaps more so than matching using deficons/datatypes (which generally scan the first bytes of a file only). What happens in a large directory and the file you are querying is deep within the database? slower performance..
  1. modern filesystems can store additional metadata with files - e.g. in NTFS pretty much everything is an attribute of some form or other, it wouldnt take much to make it possible to include querying/setting such attributed as part of most amigaos code's exall/exnext dir scanning loops.

one scenario is some combination of using file system meta data and our default icons.

An application scans a directory of files using the normal AmigaOS routines. we provide a new routine (*) to query a file/locks attributes (which uses packets to speak with the file system handler and query the meta data) which we use to request the data formats attribute. This shouldn't incur any/much IO penalty since the meta data is most likely already cached as part of scanning the dir. if the attribute isn't available we can then use default icons pattern/data matching to identify the file - and optionally then set the attribute if the file system does support meta-data.

(*) perhaps this call could be handled in the deficon code itself anyhow, so that existing amigaos code that uses icon.library, etc, would benefit from it without need for change.



Examples[edit]

/*
    Example for changing icon tooltypes.
*/

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/icon.h>

#include <workbench/startup.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

static STRPTR conwinname   = "CON:30/50/500/400/Tooltype parsing/AUTO/CLOSE/WAIT";
static BPTR conwin;
static BPTR olddir = (BPTR)-1;
static struct DiskObject *dobj;
static STRPTR *oldtoolarray, *newtoolarray;

static void clean_exit(CONST_STRPTR s);


int main(int argc, char **argv)
{
    if (argc)
    {
        clean_exit("Application must be started from Wanderer.");
        /* See dos_readargs.c for start from Shell. */
    }
    else
    {
        struct WBStartup *wbmsg = (struct WBStartup *)argv;
        struct WBArg *wbarg = wbmsg->sm_ArgList;
        STRPTR *toolarray;
        LONG toolcnt = 0;
        LONG idx;
        
        /*
            An application started from Wanderer doesn't have a console window
            for output. We have to open our own con: window or all output will
            go to Nirwana.
        */
        conwin = fopen(conwinname, "w");
        if (!conwin)
        {
            clean_exit("Can't open console window");
        }
        
        if ( (wbarg->wa_Lock) && (*wbarg->wa_Name) )
        {
            fprintf(conwin, "Trying to open %s\n", wbarg->wa_Name);
            
            /* We must enter the directory of the icon */
            olddir = CurrentDir(wbarg->wa_Lock);

            dobj = GetDiskObject(wbarg->wa_Name);
            if (dobj)
            {
                /*
                    Remember the old toolarray, so that we can put it back later.
                */
                oldtoolarray = dobj->do_ToolTypes;
            
                /* Count entries */
                if (oldtoolarray)
                {
                    toolarray = oldtoolarray;
                    while (*toolarray)
                    {
                        toolcnt++;
                        toolarray++;
                    }
                }
                fprintf(conwin, "Old icon has %d tooltype entries\n", toolcnt);
                
                /* Create new toolarray */
                newtoolarray = AllocVec(sizeof(STRPTR) * (toolcnt + 3), MEMF_ANY);
                if (!newtoolarray)
                {
                    clean_exit("Can't allocate memory for new toolarray");
                }
                /*
                    Add two new entries and copy the pointers to the
                    old values. If w'd want to change the strings we'd
                    have to work with copies of the strings.
                */
                newtoolarray[0] = "START";
                for (idx = 0 ; idx < toolcnt; idx++)
                {
                    newtoolarray[idx+1] = oldtoolarray[idx];
                }
                newtoolarray[toolcnt + 1] = "END";
                newtoolarray[toolcnt + 2] = NULL;
                
                /* Change toolarray pointer and save icon. */
                dobj->do_ToolTypes = newtoolarray;
                if (!PutDiskObject(wbarg->wa_Name, dobj))
                {
                    clean_exit("Can't write Diskobject");
                }
            }
            else
            {
                clean_exit("Can't open DiskObjekt");
            }
        }
    }

    clean_exit(NULL);

    return 0;
}


static void clean_exit(CONST_STRPTR s)
{
    if (s)
    {
        if (conwin)
        {
            fprintf(conwin, "%s\n", s);
        }
        else
        {
            printf("%s\n", s);
        }
    }
    
    /* Give back allocated resourses */
    if (conwin) fclose(conwin);

    /*
        Free DiskObject. We have to set back the pointer to the toolarray or
        we'll get memory corruption.
    */
    if (dobj)
    {
        dobj->do_ToolTypes = oldtoolarray;
        FreeDiskObject(dobj);
    }
    
    FreeVec(newtoolarray);
    
    /*
        Switch back to old directory. It's important that the directory which
        was active at program start is set when the application is quit.
    */
    if (olddir != (BPTR)-1)
    {
        CurrentDir(olddir);
        olddir = (BPTR)-1;
    }
    
    exit(0);
}


MultiView.c

/*
    Copyright © 1995-2009, The AROS Development Team. All rights reserved.
    $Id$
*/
 
/*********************************************************************************************/
 
#include "global.h"
 
#include <stdlib.h> /* for exit() */
#include <stdio.h>
#include <string.h>
#include <ctype.h>  /*  toupper() */
 
#include "compilerspecific.h"
#include "debug.h"
#include "arossupport.h"
 
extern struct NewMenu nm[];
extern struct NewMenu nmpict[];
extern struct NewMenu nmtext[];
 
/*********************************************************************************************/
 
/* Many datatype classes seem to rely on OM_NOTIFY calls coming back to the datatype object
   as OM_UPDATE :-\ */
 
#define BACK_CONNECTION 1
 
/*********************************************************************************************/
 
#define ARG_TEMPLATE    "FILE,CLIPBOARD/S,CLIPUNIT/K/N,SCREEN/S,PUBSCREEN/K,REQUESTER/S," \
			"BOOKMARK/S,FONTNAME/K,FONTSIZE/K/N,BACKDROP/S,WINDOW/S," \
			"PORTNAME/K,IMMEDIATE/S,REPEAT/S,PRTUNIT/K/N"
 
#define ARG_FILE        0
#define ARG_CLIPBOARD   1
#define ARG_CLIPUNIT    2
#define ARG_SCREEN      3
#define ARG_PUBSCREEN   4
#define ARG_REQUESTER   5
#define ARG_BOOKMARK    6
#define ARG_FONTNAME    7
#define ARG_FONTSIZE    8
#define ARG_BACKDROP    9
#define ARG_WINDOW      10
#define ARG_PORTNAME    11
#define ARG_IMMEDIATE   12
#define ARG_REPEAT      13
#define ARG_PRTUNIT     14
 
#define NUM_ARGS        15
 
/*********************************************************************************************/
 
static struct libinfo
{
    APTR        var;
    STRPTR      name;
    WORD        version;
} libtable[] =
{
    {&IntuitionBase     , "intuition.library"           , 39    },
    {&GfxBase           , "graphics.library"            , 39    },
    {&GadToolsBase      , "gadtools.library"            , 39    },
    {&LayersBase        , "layers.library"              , 39    },
    {&UtilityBase       , "utility.library"             , 39    },
    {&KeymapBase        , "keymap.library"              , 39    },
    {&DataTypesBase     , "datatypes.library"           , 39    },
    {&DiskfontBase      , "diskfont.library"            , 39    },
    {NULL                                                       }
};
 
static struct TextAttr  textattr;
static struct TextFont  *font;
static struct RDArgs    *myargs;
static IPTR             args[NUM_ARGS];
static UBYTE            fontname[256];
static WORD             winwidth, winheight;
static WORD             sizeimagewidth, sizeimageheight;
static BOOL             model_has_members;
 
/*********************************************************************************************/
 
static void CloseLibs(void);
static void KillFont(void);
static void FreeArguments(void);
static void KillICObjects(void);
static void FreeVisual(void);
static void KillGadgets(void);
static void CloseDTO(void);
static void KillWindow(void);
static void ScrollTo(UWORD dir, UWORD quali);
static void FitToWindow(void);
 
/*********************************************************************************************/
 
void OutputMessage(CONST_STRPTR msg)
{
    struct EasyStruct es;
 
    if (msg)
    {
	if ( IntuitionBase && !((struct Process *)FindTask(NULL))->pr_CLI )
	{
	    es.es_StructSize   = sizeof(es);
	    es.es_Flags        = 0;
	    es.es_Title        = "MultiView";
	    es.es_TextFormat   = msg;
	    es.es_GadgetFormat = MSG(MSG_OK);
 
	    EasyRequestArgs(win, &es, NULL, NULL);  
	}
	else
	{
	    printf("MultiView: %s\n", msg);
	}
    }
}
 
/*********************************************************************************************/
 
void Cleanup(CONST_STRPTR msg)
{
    OutputMessage(msg);
 
    if (appwindow)
        RemoveAppWindow(appwindow);
    if (msgport)
        DeleteMsgPort(msgport);
 
    KillWindow();
    KillMenus();
    KillGadgets();
    FreeVisual();
    CloseDTO();
    KillICObjects();
    KillFont();
    FreeArguments();
 
    if (cd != NULL)
        CurrentDir(cd); /* restore current directory */
 
    CloseLibs();
    CleanupLocale();
 
    exit(prog_exitcode);
}
 
 
/*********************************************************************************************/
 
static void OpenLibs(void)
{
    struct libinfo *li;
 
    for(li = libtable; li->var; li++)
    {
	if (!((*(struct Library **)li->var) = OpenLibrary(li->name, li->version)))
	{
	    sprintf(s, MSG(MSG_CANT_OPEN_LIB), li->name, li->version);
	    Cleanup(s);
	}       
    }
 
}
 
/*********************************************************************************************/
 
static void CloseLibs(void)
{
    struct libinfo *li;
 
    for(li = libtable; li->var; li++)
    {
	if (*(struct Library **)li->var) CloseLibrary((*(struct Library **)li->var));
    }
}
 
/*********************************************************************************************/
 
static void LoadFont(void)
{
    font = OpenDiskFont(&textattr);
    if (!font)
    {
	textattr.ta_Name  = "topaz.font";
	textattr.ta_YSize = 8;
	textattr.ta_Style = 0;
	textattr.ta_Flags = 0;
 
	font = OpenFont(&textattr);
    }
}
 
/*********************************************************************************************/
 
static void KillFont(void)
{
    if (font) CloseFont(font);
}
 
/*********************************************************************************************/
 
static void InitDefaults(void)
{
    struct TextFont *defaultfont = GfxBase->DefaultFont;
 
    /* This might be a bit problematic depending on how default system font
       switching through Font prefs program works and if then the previous
       default system font is closed or not. So this is very likely only safe
       when in such a case the previous font is not closed (means -> the font
       will remain in memory in any case)
 
       ClipView example program on Amiga Dev CD also does it like this. So ... */
 
    textattr.ta_Name  = defaultfont->tf_Message.mn_Node.ln_Name;
    textattr.ta_YSize = defaultfont->tf_YSize;
    textattr.ta_Style = defaultfont->tf_Style;
    textattr.ta_Flags = defaultfont->tf_Flags;
}
 
/*********************************************************************************************/
 
static void GetArguments(void)
{
 
    if (!(myargs = ReadArgs(ARG_TEMPLATE, args, NULL)))
    {
	Fault(IoErr(), 0, s, 256);
	Cleanup(s);
    }
 
    filename = (STRPTR)args[ARG_FILE];
    if (!filename && !args[ARG_CLIPBOARD])
    {
	filename = GetFileName(MSG_ASL_OPEN_TITLE);
	if (!filename) Cleanup(NULL);
    }
 
    if (args[ARG_FONTNAME])
    {
	strncpy(fontname, (char *)args[ARG_FONTNAME], 255 - 5);
	if (!strstr(fontname, ".font")) strcat(fontname, ".font");
 
	textattr.ta_Name = fontname;
    }
 
    if (args[ARG_FONTSIZE])
    {
	textattr.ta_YSize = *(LONG *)args[ARG_FONTSIZE];
    }
 
}
 
/*********************************************************************************************/
 
static void FreeArguments(void)
{
    if (myargs) FreeArgs(myargs);
}
 
/*********************************************************************************************/
 
static void MakeICObjects(void)
{
    static const struct TagItem dto_to_vert_map[] =
    {
	{DTA_TopVert            , PGA_Top       },
	{DTA_VisibleVert        , PGA_Visible   },
	{DTA_TotalVert          , PGA_Total     },
	{TAG_DONE                               }
    };
    static const struct TagItem dto_to_horiz_map[] =
    {
	{DTA_TopHoriz           , PGA_Top       },
	{DTA_VisibleHoriz       , PGA_Visible   },
	{DTA_TotalHoriz         , PGA_Total     },
	{TAG_DONE                               }
    };
    static const struct TagItem vert_to_dto_map[] =
    {
	{PGA_Top                , DTA_TopVert   },
	{TAG_DONE                               }
    };
    static const struct TagItem horiz_to_dto_map[] =
    {
	{PGA_Top                , DTA_TopHoriz  },
	{TAG_DONE                               }
    };
 
    model_obj           = NewObject(NULL, MODELCLASS, ICA_TARGET, ICTARGET_IDCMP,
						      TAG_DONE);
    dto_to_vert_ic_obj  = NewObject(NULL, ICCLASS, ICA_MAP, (IPTR)dto_to_vert_map,
						   TAG_DONE);
    dto_to_horiz_ic_obj = NewObject(NULL, ICCLASS, ICA_MAP, (IPTR)dto_to_horiz_map,
						   TAG_DONE);
    vert_to_dto_ic_obj  = NewObject(NULL, ICCLASS, ICA_MAP, (IPTR)vert_to_dto_map,
						   TAG_DONE);
    horiz_to_dto_ic_obj = NewObject(NULL, ICCLASS, ICA_MAP, (IPTR)horiz_to_dto_map,
						   TAG_DONE);
#if BACK_CONNECTION
    model_to_dto_ic_obj = NewObject(NULL, ICCLASS, TAG_DONE);
#endif
 
    if (!model_obj  	    	||
    	!dto_to_vert_ic_obj 	||
	!dto_to_horiz_ic_obj 	||
	!vert_to_dto_ic_obj 	||
	!horiz_to_dto_ic_obj 
    #if BACK_CONNECTION
	|| !model_to_dto_ic_obj
    #endif
       )
    {
	Cleanup(MSG(MSG_CANT_CREATE_IC));
    }
 
    DoMethod(model_obj, OM_ADDMEMBER, (IPTR) dto_to_vert_ic_obj);
    DoMethod(model_obj, OM_ADDMEMBER, (IPTR) dto_to_horiz_ic_obj);
#if BACK_CONNECTION
    DoMethod(model_obj, OM_ADDMEMBER, (IPTR) model_to_dto_ic_obj);
#endif
 
    model_has_members = TRUE;
 
}
 
/*********************************************************************************************/
 
static void KillICObjects(void)
{
    if (!model_has_members)
    {
	if (dto_to_vert_ic_obj) DisposeObject(dto_to_vert_ic_obj);
	if (dto_to_horiz_ic_obj) DisposeObject(dto_to_horiz_ic_obj);
    #if BACK_CONNECTION
	if (model_to_dto_ic_obj) DisposeObject(model_to_dto_ic_obj);
    #endif
    }
 
    if (model_obj) DisposeObject(model_obj);
    if (vert_to_dto_ic_obj) DisposeObject(vert_to_dto_ic_obj);
    if (horiz_to_dto_ic_obj) DisposeObject(horiz_to_dto_ic_obj);
}
 
/*********************************************************************************************/
 
static void GetVisual(void)
{
    scr = LockPubScreen(NULL);
    if (!scr) Cleanup(MSG(MSG_CANT_LOCK_SCR));
 
    dri = GetScreenDrawInfo(scr);
    if (!dri) Cleanup(MSG(MSG_CANT_GET_DRI));
 
    vi = GetVisualInfoA(scr, NULL);
    if (!vi) Cleanup(MSG(MSG_CANT_GET_VI));
}
 
/*********************************************************************************************/
 
static void FreeVisual(void)
{
    if (vi)  FreeVisualInfo(vi);
    if (dri) FreeScreenDrawInfo(scr, dri);
    if (scr) UnlockPubScreen(NULL, scr);
}
 
/*********************************************************************************************/
 
static void MakeGadgets(void)
{
    static WORD img2which[] =
    {
	UPIMAGE,
	DOWNIMAGE,
	LEFTIMAGE,
	RIGHTIMAGE,
	SIZEIMAGE
    };
 
    IPTR imagew[NUM_IMAGES], imageh[NUM_IMAGES];
    WORD v_offset, h_offset, btop, i;
 
    for(i = 0; i < NUM_IMAGES; i++)
    {
	img[i] = NewObject(NULL, SYSICLASS, SYSIA_DrawInfo      , (IPTR)( dri ),
					    SYSIA_Which         , (IPTR)( img2which[i] ),
					    TAG_DONE);
 
	if (!img[i]) Cleanup(MSG(MSG_CANT_CREATE_SYSIMAGE));
 
	GetAttr(IA_Width,(Object *)img[i],&imagew[i]);
	GetAttr(IA_Height,(Object *)img[i],&imageh[i]);
    }
 
    sizeimagewidth  = imagew[IMG_SIZE];
    sizeimageheight = imageh[IMG_SIZE];
 
    btop = scr->WBorTop + dri->dri_Font->tf_YSize + 1;
 
    v_offset = imagew[IMG_DOWNARROW] / 4;
    h_offset = imageh[IMG_LEFTARROW] / 4;
 
    gad[GAD_UPARROW] = NewObject(NULL, BUTTONGCLASS,
	    GA_Image            , (IPTR)( img[IMG_UPARROW] ),
	    GA_RelRight         , (IPTR)( -imagew[IMG_UPARROW] + 1 ),
	    GA_RelBottom        , (IPTR)( -imageh[IMG_DOWNARROW] - imageh[IMG_UPARROW] - imageh[IMG_SIZE] + 1 ),
	    GA_ID               , (IPTR)( GAD_UPARROW ),
	    GA_RightBorder      , (IPTR)TRUE,
	    GA_Immediate        , (IPTR)TRUE,
	    GA_RelVerify        , (IPTR)TRUE,
	    TAG_DONE);
 
    gad[GAD_DOWNARROW] = NewObject(NULL, BUTTONGCLASS,
	    GA_Image            , (IPTR)( img[IMG_DOWNARROW] ),
	    GA_RelRight         , (IPTR)( -imagew[IMG_UPARROW] + 1 ),
	    GA_RelBottom        , (IPTR)( -imageh[IMG_UPARROW] - imageh[IMG_SIZE] + 1 ),
	    GA_ID               , (IPTR)( GAD_DOWNARROW ),
	    GA_RightBorder      , (IPTR)TRUE,
	    GA_Previous         , (IPTR)( gad[GAD_UPARROW] ),
	    GA_Immediate        , (IPTR)TRUE,
	    GA_RelVerify        , (IPTR)TRUE,
	    TAG_DONE);
 
    gad[GAD_VERTSCROLL] = NewObject(NULL, PROPGCLASS,
	    GA_Top              , (IPTR)( btop + 1 ),
	    GA_RelRight         , (IPTR)( -imagew[IMG_DOWNARROW] + v_offset + 1 ),
	    GA_Width            , (IPTR)( imagew[IMG_DOWNARROW] - v_offset * 2 ),
	    GA_RelHeight        , (IPTR)( -imageh[IMG_DOWNARROW] - imageh[IMG_UPARROW] - imageh[IMG_SIZE] - btop -2 ),
	    GA_ID               , (IPTR)( GAD_VERTSCROLL ),
	    GA_Previous         , (IPTR)( gad[GAD_DOWNARROW] ),
	    GA_RightBorder      , (IPTR)TRUE,
	    GA_RelVerify        , (IPTR)TRUE,
	    GA_Immediate        , (IPTR)TRUE,
	    PGA_NewLook         , (IPTR)TRUE,
	    PGA_Borderless      , (IPTR)TRUE,
	    PGA_Total           , (IPTR)100,
	    PGA_Visible         , (IPTR)100,
	    PGA_Freedom         , (IPTR)FREEVERT,
	    PGA_NotifyBehaviour , (IPTR)PG_BEHAVIOUR_NICE,
	    TAG_DONE);
 
    gad[GAD_RIGHTARROW] = NewObject(NULL, BUTTONGCLASS,
	    GA_Image            , (IPTR)( img[IMG_RIGHTARROW] ),
	    GA_RelRight         , (IPTR)( -imagew[IMG_SIZE] - imagew[IMG_RIGHTARROW] + 1 ),
	    GA_RelBottom        , (IPTR)( -imageh[IMG_RIGHTARROW] + 1 ),
	    GA_ID               , (IPTR)( GAD_RIGHTARROW ),
	    GA_BottomBorder     , (IPTR)TRUE,
	    GA_Previous         , (IPTR)( gad[GAD_VERTSCROLL] ),
	    GA_Immediate        , (IPTR)TRUE,
	    GA_RelVerify        , (IPTR)TRUE,
	    TAG_DONE);
 
    gad[GAD_LEFTARROW] = NewObject(NULL, BUTTONGCLASS,
	    GA_Image            , (IPTR)( img[IMG_LEFTARROW] ),
	    GA_RelRight         , (IPTR)( -imagew[IMG_SIZE] - imagew[IMG_RIGHTARROW] - imagew[IMG_LEFTARROW] + 1 ),
	    GA_RelBottom        , (IPTR)( -imageh[IMG_RIGHTARROW] + 1 ),
	    GA_ID               , (IPTR)( GAD_LEFTARROW ),
	    GA_BottomBorder     , (IPTR)TRUE,
	    GA_Previous         , (IPTR)( gad[GAD_RIGHTARROW] ),
	    GA_Immediate        , (IPTR)TRUE,
	    GA_RelVerify        , (IPTR)TRUE,
	    TAG_DONE);
 
    gad[GAD_HORIZSCROLL] = NewObject(NULL, PROPGCLASS,
	    GA_Left             , (IPTR)( scr->WBorLeft ),
	    GA_RelBottom        , (IPTR)( -imageh[IMG_LEFTARROW] + h_offset + 1 ),
	    GA_RelWidth         , (IPTR)( -imagew[IMG_LEFTARROW] - imagew[IMG_RIGHTARROW] - imagew[IMG_SIZE] - scr->WBorRight - 2 ),
	    GA_Height           , (IPTR)( imageh[IMG_LEFTARROW] - (h_offset * 2) ),
	    GA_ID               , (IPTR)( GAD_HORIZSCROLL ),
	    GA_Previous         , (IPTR)( gad[GAD_LEFTARROW] ),
	    GA_BottomBorder     , (IPTR)TRUE,
	    GA_RelVerify        , (IPTR)TRUE,
	    GA_Immediate        , (IPTR)TRUE,
	    PGA_NewLook         , (IPTR)TRUE,
	    PGA_Borderless      , (IPTR)TRUE,
	    PGA_Total           , (IPTR)100,
	    PGA_Visible         , (IPTR)100,
	    PGA_Freedom         , (IPTR)FREEHORIZ,
	    PGA_NotifyBehaviour , (IPTR)PG_BEHAVIOUR_NICE,
	    TAG_DONE);
 
    for(i = 0;i < NUM_GADGETS;i++)
    {
	if (!gad[i]) Cleanup(MSG(MSG_CANT_CREATE_GADGET));
    }
 
    SetAttrs(gad[GAD_VERTSCROLL] , ICA_TARGET, (IPTR)vert_to_dto_ic_obj, TAG_DONE);
    SetAttrs(gad[GAD_HORIZSCROLL], ICA_TARGET, (IPTR)horiz_to_dto_ic_obj, TAG_DONE);
    SetAttrs(dto_to_vert_ic_obj  , ICA_TARGET, (IPTR)gad[GAD_VERTSCROLL], TAG_DONE);
    SetAttrs(dto_to_horiz_ic_obj , ICA_TARGET, (IPTR)gad[GAD_HORIZSCROLL], TAG_DONE);
}
 
/*********************************************************************************************/
 
static void KillGadgets(void)
{
    WORD i;
 
    for(i = 0; i < NUM_GADGETS;i++)
    {
	if (win) RemoveGadget(win, (struct Gadget *)gad[i]);
	if (gad[i]) DisposeObject(gad[i]);
	gad[i] = 0;
    }
 
    for(i = 0; i < NUM_IMAGES;i++)
    {
	if (img[i]) DisposeObject(img[i]);
	img[i] = NULL;
    }
}
 
/*********************************************************************************************/
 
void AddDTOToWin(void)
{
    EraseRect(win->RPort, win->BorderLeft,
			  win->BorderTop,
			  win->Width - 1 - win->BorderRight,
			  win->Height - 1 - win->BorderBottom);
 
    SetDTAttrs (dto, NULL, NULL, GA_Left        , win->BorderLeft + 2                           ,
				 GA_Top         , win->BorderTop + 2                            ,
				 GA_RelWidth    , - win->BorderLeft - win->BorderRight - 4      ,
				 GA_RelHeight   , - win->BorderTop - win->BorderBottom - 4      ,
				 TAG_DONE);
 
    AddDTObject(win, NULL, dto, -1);
    // RefreshDTObjects(dto, win, NULL, NULL); needed ?
 
}
 
/*********************************************************************************************/
 
static void OpenDTO(void)
{
    struct DTMethod *triggermethods;
    ULONG           *methods;
    STRPTR          objname = NULL;
    IPTR            val;
    struct DataType *dt;
 
    old_dto = dto;
 
    do
    {
        if (!old_dto && args[ARG_CLIPBOARD])
        {
	    APTR clipunit = 0;
 
	    if (args[ARG_CLIPUNIT]) clipunit = *(APTR *)args[ARG_CLIPUNIT];
 
	    D(bug("MultiView: calling NewDTObject\n"));
 
	    dto = NewDTObject(clipunit, ICA_TARGET    , (IPTR)model_obj,
				    GA_ID         , 1000           ,
				    DTA_SourceType, DTST_CLIPBOARD ,
				    DTA_TextAttr  , (IPTR)&textattr,
				    TAG_DONE);
 
	    D(bug("MultiView: NewDTObject returned %x\n", dto));
        }
        else
        {
	     dto = NewDTObject(filename, ICA_TARGET      , (IPTR)model_obj,
				    GA_ID           , 1000           ,
				    DTA_TextAttr    , (IPTR)&textattr,
				    TAG_DONE);
        }
 
        if (!dto)
        {
	    ULONG errnum = IoErr();
 
	    if (errnum == DTERROR_UNKNOWN_DATATYPE)
	    {
	        BPTR lock = Lock(filename,ACCESS_READ);
	        if (lock)
	        {
		    struct DataType *dtn;
		    if ((dtn = ObtainDataTypeA(DTST_FILE, lock, NULL)))
		    {
			if (!Stricmp(dtn->dtn_Header->dth_Name, "directory"))
			{
			    /* file is a directory and no directory.datatype is installed */
			    strncpy(filenamebuffer, (filename ? filename : (STRPTR)""), 298);
			    filenamebuffer[298]=0;
 
			    if (strlen(filenamebuffer) &&
			        filenamebuffer[strlen(filenamebuffer)-1] != ':' &&
				filenamebuffer[strlen(filenamebuffer)-1] != '/')
			    {
				strcat(filenamebuffer,"/");
			    }
 
			    filename = GetFileName(MSG_ASL_OPEN_TITLE);
			    if (filename) continue;
			}
		        ReleaseDataType(dtn);
		    }
		    UnLock(lock);
	        }
	    }
 
	    if (errnum >= DTERROR_UNKNOWN_DATATYPE)
	        sprintf(s, GetDTString(errnum), filename);
	    else
	        Fault(errnum, 0, s, 256);
 
	    if (!old_dto) Cleanup(s);
	    dto = old_dto;
	    return;
        }
    } while (!dto);
 
    strncpy(filenamebuffer, (filename ? filename : (STRPTR)""), 299);
 
    SetAttrs(vert_to_dto_ic_obj, ICA_TARGET, (IPTR)dto, TAG_DONE);
    SetAttrs(horiz_to_dto_ic_obj, ICA_TARGET, (IPTR)dto, TAG_DONE);
#if BACK_CONNECTION
    SetAttrs(model_to_dto_ic_obj, ICA_TARGET, (IPTR)dto, TAG_DONE);
#endif
 
    val = 0;
    GetDTAttrs(dto, DTA_NominalHoriz, (IPTR)&val, TAG_DONE);
    pdt_origwidth = winwidth = (WORD)val;
    GetDTAttrs(dto, DTA_NominalVert , (IPTR)&val, TAG_DONE);
    pdt_origheight = winheight = (WORD)val;
    pdt_zoom = 1;
    pdt_fit_win = FALSE;
    pdt_keep_aspect = FALSE;
 
    /*
     *  Add 4 Pixels for border around DataType-Object
     *  See AddDTOToWin() for details
     */
    if(winwidth)
    {
     winwidth += 4;
    }
 
    if(winheight)
    {
     winheight += 4;
    }
 
    GetDTAttrs(dto, DTA_ObjName, (IPTR)&objname, TAG_DONE);
    strncpy(objnamebuffer, objname ? objname : filenamebuffer, 299);
 
    dt = NULL;
    dto_subclass_gid = 0;
    if (GetDTAttrs(dto, DTA_DataType, (IPTR)&dt, TAG_DONE))
    {
	if (dt)
	{
	    dto_subclass_gid = dt->dtn_Header->dth_GroupID;
	}
    }
 
    dto_supports_write = FALSE;
    dto_supports_write_iff = FALSE;
    dto_supports_print = FALSE;
    dto_supports_copy = FALSE;
    dto_supports_selectall = FALSE;
    dto_supports_clearselected = FALSE;
 
    if (DoWriteMethod(NULL, DTWM_RAW)) dto_supports_write = TRUE;	/* probe raw saving */
    if ((methods = GetDTMethods(dto)))
    {
	if (FindMethod(methods, DTM_WRITE)) dto_supports_write_iff = TRUE;
	if (FindMethod(methods, DTM_PRINT)) dto_supports_print = TRUE;
	if (FindMethod(methods, DTM_COPY)) dto_supports_copy = TRUE;
	if (FindMethod(methods, DTM_SELECT)) dto_supports_selectall = TRUE;
	if (FindMethod(methods, DTM_CLEARSELECTED)) dto_supports_clearselected = TRUE;
    }
 
    dto_supports_activate_field =  FALSE;
    dto_supports_next_field =  FALSE;
    dto_supports_prev_field =  FALSE;
    dto_supports_retrace =  FALSE;
    dto_supports_browse_next =  FALSE;
    dto_supports_browse_prev =  FALSE;
    dto_supports_search =  FALSE;
    dto_supports_search_next =  FALSE;
    dto_supports_search_prev =  FALSE;
 
    if ((triggermethods = (struct DTMethod *)GetDTTriggerMethods(dto)))
    {
	if (FindTriggerMethod(triggermethods, NULL, STM_ACTIVATE_FIELD)) dto_supports_activate_field = TRUE;
	if (FindTriggerMethod(triggermethods, NULL, STM_NEXT_FIELD))     dto_supports_next_field     = TRUE;
	if (FindTriggerMethod(triggermethods, NULL, STM_PREV_FIELD))     dto_supports_prev_field     = TRUE;
	if (FindTriggerMethod(triggermethods, NULL, STM_RETRACE))        dto_supports_retrace        = TRUE;
	if (FindTriggerMethod(triggermethods, NULL, STM_BROWSE_NEXT))    dto_supports_browse_next    = TRUE;
	if (FindTriggerMethod(triggermethods, NULL, STM_BROWSE_PREV))    dto_supports_browse_prev    = TRUE;
	if (FindTriggerMethod(triggermethods, NULL, STM_SEARCH))         dto_supports_search         = TRUE;
	if (FindTriggerMethod(triggermethods, NULL, STM_SEARCH_NEXT))    dto_supports_search_next    = TRUE;
	if (FindTriggerMethod(triggermethods, NULL, STM_SEARCH_PREV))    dto_supports_search_prev    = TRUE;
    }
 
    D(bug("\nMultiview: Found Methods:%s%s%s%s%s%s\n",
	dto_supports_write ? " DTM_WRITE->RAW" : "",
	dto_supports_write_iff ? " DTM_WRITE->IFF" : "",
	dto_supports_print ? " DTM_PRINT" : "",
	dto_supports_copy ? " DTM_COPY" : "",
	dto_supports_selectall ? " DTM_SELECT" : "",
	dto_supports_clearselected ? " DTM_CLEARSELECTED" : ""));
 
    D(bug("Multiview: Found Triggers:%s%s%s%s%s%s%s\n\n",
	dto_supports_activate_field ? " STM_ACTIVATE_FIELD" : "",
	dto_supports_next_field ? " STM_NEXT_FIELD" : "",
	dto_supports_prev_field ? " STM_PREV_FIELD" : "",
	dto_supports_retrace ? " STM_RETRACE" : "",
	dto_supports_browse_next ? " STM_BROWSE_NEXT" : "",
	dto_supports_browse_prev ? " STM_BROWSE_PREV" : "",
	dto_supports_search ? " STM_SEARCH" : "",
	dto_supports_search_next ? " STM_SEARCH_NEXT" : "",
	dto_supports_search_prev ? " STM_SEARCH_PREV" : ""));
 
    if (old_dto)
    {
	if (win) RemoveDTObject(win, old_dto);
	DisposeDTObject(old_dto);
 
	if (win)
	{
	    AddDTOToWin();
	    SetWindowTitles(win, objnamebuffer, (UBYTE *)~0);
	    SetMenuFlags();
	}
    }
 
}
 
/*********************************************************************************************/
 
static void CloseDTO(void)
{
    if (dto)
    {
	if (win) RemoveDTObject(win, dto);
	DisposeDTObject(dto);
	dto = NULL;
    }
}
 
/*********************************************************************************************/
 
static void MakeWindow(void)
{
    WORD minwidth, minheight;
 
    if (!winwidth) winwidth = scr->Width;
    if (!winheight) winheight = scr->Height - scr->BarHeight - 1 - 
				scr->WBorTop - scr->Font->ta_YSize - 1 - sizeimageheight;
 
    minwidth  = (winwidth  < 50) ? winwidth : 50;
    minheight = (winheight < 50) ? winheight : 50;
 
    win = OpenWindowTags(0, WA_PubScreen        , (IPTR)scr             ,
			    WA_Title            , (IPTR)objnamebuffer   ,
			    WA_CloseGadget      , TRUE                  ,
			    WA_DepthGadget      , TRUE                  ,
			    WA_DragBar          , TRUE                  ,
			    WA_SizeGadget       , TRUE                  ,
			    WA_Activate         , TRUE                  ,
			    WA_SimpleRefresh    , TRUE                  ,
			    WA_NoCareRefresh    , TRUE                  ,
			    WA_NewLookMenus     , TRUE                  ,
			    WA_Left             , 0                     ,
			    WA_Top              , scr->BarHeight + 1    ,
			    WA_InnerWidth       , winwidth              ,
			    WA_InnerHeight      , winheight             ,
			    WA_AutoAdjust       , TRUE                  ,
			    WA_MinWidth         , minwidth              ,
			    WA_MinHeight        , minheight             ,
			    WA_MaxWidth         , 16383                 ,
			    WA_MaxHeight        , 16383                 ,
			    WA_Gadgets          , (IPTR)gad[GAD_UPARROW],
			    WA_IDCMP            , IDCMP_CLOSEWINDOW |
						  IDCMP_GADGETUP    |
						  IDCMP_GADGETDOWN  |
						  IDCMP_MOUSEMOVE   |
						  IDCMP_VANILLAKEY  |                                             
						  IDCMP_RAWKEY      |
						  IDCMP_IDCMPUPDATE |
						  IDCMP_MENUPICK    |
						  IDCMP_NEWSIZE     |
						  IDCMP_INTUITICKS      ,
			    TAG_DONE);
 
    if (!win) Cleanup(MSG(MSG_CANT_CREATE_WIN));                            
 
    AddDTOToWin();
 
    SetMenuStrip(win, menus);
 
    winmask = 1L << win->UserPort->mp_SigBit;
    if (!(msgport = CreateMsgPort()))
    {
        Cleanup(MSG(MSG_CANT_CREATE_MSGPORT));
    }
    if (!(appwindow = AddAppWindow(0,0,win,msgport,NULL)))
    {
        Cleanup(MSG(MSG_CANT_ADD_APPWINDOW));
    }
    msgmask = 1L << msgport->mp_SigBit;
}
 
/*********************************************************************************************/
 
static void KillWindow(void)
{
    if (win)
    {
	if (dto) RemoveDTObject(win, dto);
	if (menus) ClearMenuStrip(win);
	CloseWindow(win);
	win = NULL;
 
	winwidth = winheight = 0;
    }
}
 
/*********************************************************************************************/
 
static void ScrollTo(UWORD dir, UWORD quali)
{
    IPTR val;
    LONG oldtop, top, total, visible, delta = 1;
    BOOL horiz;
    BOOL inc;
 
#ifdef __AROS__    
    switch(dir)
    {
    	case RAWKEY_NM_WHEEL_UP:
	    dir = CURSORUP;
	    delta = 3;
	    break;
 
	case RAWKEY_NM_WHEEL_DOWN:
	    dir = CURSORDOWN;
	    delta = 3;
	    break;
 
	case RAWKEY_NM_WHEEL_LEFT:
	    dir = CURSORLEFT;
	    delta = 3;
	    break;
 
	case RAWKEY_NM_WHEEL_RIGHT:
	    dir = CURSORRIGHT;
	    delta = 3;
	    break;
    }
#endif
 
    if ((dir == CURSORUP) || (dir == CURSORDOWN))
    {
	horiz = FALSE;
	if (dir == CURSORUP) inc = FALSE; else inc = TRUE;
 
	GetDTAttrs(dto, DTA_TopVert, (IPTR)&val, TAG_DONE);
	top = (LONG)val;
	GetDTAttrs(dto, DTA_TotalVert, (IPTR)&val, TAG_DONE);
	total = (LONG)val;
	GetDTAttrs(dto, DTA_VisibleVert, (IPTR)&val, TAG_DONE);
	visible = (LONG)val;
    }
    else
    {
	horiz = TRUE;
	if (dir == CURSORLEFT) inc = FALSE; else inc = TRUE;
 
	GetDTAttrs(dto, DTA_TopHoriz, (IPTR)&val, TAG_DONE);
	top = (LONG)val;
	GetDTAttrs(dto, DTA_TotalHoriz, (IPTR)&val, TAG_DONE);
	total = (LONG)val;
	GetDTAttrs(dto, DTA_VisibleHoriz, (IPTR)&val, TAG_DONE);
	visible = (LONG)val;
 
    }
 
    oldtop = top;
    if (quali & (IEQUALIFIER_LALT | IEQUALIFIER_RALT | IEQUALIFIER_CONTROL))
    {
	if (inc) top = total; else top = 0;
    }
    else
    if (quali & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
    {
	if (inc) top += visible - 1; else top -= visible - 1;
    }
    else
    {
	if (inc) top += delta; else top -= delta;
    }
 
    if (top + visible > total) top = total - visible;
    if (top < 0) top = 0;
 
    if (top != oldtop)
    {
	struct Gadget *g;
 
	if (horiz)
	{
	    g = (struct Gadget *)gad[GAD_HORIZSCROLL];
	}
	else
	{
	    g = (struct Gadget *)gad[GAD_VERTSCROLL];
	}
 
	SetGadgetAttrs(g, win, NULL, PGA_Top, top,
				     TAG_DONE);
 
#ifdef __MORPHOS__
	/* Looks like setting PGA_Top on Amiga does not cause OM_NOTIFIEs
	   to be sent (to dto). Or something like that. */
 
	SetDTAttrs(dto, win, NULL, (horiz ? DTA_TopHoriz : DTA_TopVert), top, TAG_DONE);
#endif
 
    } /* if (top != oldtop) */
 
}
 
/*********************************************************************************************/
 
static void FitToWindow(void)
{
    if( pdt_fit_win )
    {
	int x, y;
 
	x = win->Width - (win->BorderLeft + win->BorderRight + 4);
	y = win->Height - (win->BorderTop + win->BorderBottom + 4);
	D(bug("=> width %ld height %ld\n", x, y));
	DoScaleMethod(x, y, pdt_keep_aspect);
//	DoLayout(TRUE); seems to be done by intuition ?
    }
}
 
/*********************************************************************************************/
 
static void HandleAll(void)
{
    struct IntuiMessage *msg;
    const struct TagItem *tstate, *tags;
    struct TagItem      *tag;
    struct MenuItem     *item;
    struct Gadget       *activearrowgad = NULL;
    WORD                arrowticker = 0, activearrowkind = 0;
    IPTR                tidata;
    UWORD               men;
    BOOL                quitme = FALSE;
    const STRPTR	not_supported = "Sorry, not supported yet\n";
    ULONG               sigs;
 
    while (!quitme)
    {
	sigs = Wait(msgmask | winmask );
//        if ( (sigs & winmask) || (sigs & msgmask) )
        LONG                editorvarbuffer[300];
        struct AppMessage  *appmsg;
 
        while ((appmsg = (struct AppMessage *) GetMsg(msgport)))
        {
            if (appmsg->am_Type == AMTYPE_APPWINDOW)
            {
                if (appmsg->am_NumArgs >= 1)
                {
                    NameFromLock(appmsg->am_ArgList->wa_Lock, filenamebuffer, 299);
                    AddPart(filenamebuffer, appmsg->am_ArgList->wa_Name, 299);
                    filename = filenamebuffer;
                    D(bug("[Multiview] appwindow received message: filename = %s\n", filename));
                }
            }
 
            ReplyMsg ((struct Message *) appmsg);
            ActivateWindow(win);
 
            if (filename)
                OpenDTO();
 
        } /* while ((appmsg = (struct AppMessage *) GetMsg(msgport))) */
 
	while((msg = (struct IntuiMessage *)GetMsg(win->UserPort)))
	{
//	    D(if (msg->Class!=IDCMP_INTUITICKS) bug("  Msg Class %08lx\n", (long)msg->Class));
	    switch (msg->Class)
	    {
		case IDCMP_CLOSEWINDOW:
		    quitme = TRUE;
		    break;
 
		case IDCMP_VANILLAKEY:
		    D(bug("[Multiview] Vanillakey %d\n", (int)msg->Code));
		    switch(msg->Code)
		    {
			case 27: /* ESC */
			    quitme = TRUE;
			    break;
 
			case 13: /* RETURN */
			    if (dto_supports_activate_field) DoTrigger(STM_ACTIVATE_FIELD);
			    else if (dto_supports_search) DoTrigger(STM_SEARCH);
			    RefreshDTObjects (dto, win, NULL, (IPTR) NULL);
			    break;
 
			case 9: /* TAB */
			    if (dto_supports_next_field) DoTrigger(STM_NEXT_FIELD);
			    else if (dto_supports_search_next) DoTrigger(STM_SEARCH_NEXT);
			    break;
 
			case 8: /* Backspace */
			    if (dto_supports_retrace) DoTrigger(STM_RETRACE);
			    break;
 
			case '>':
			    if (dto_supports_browse_next) DoTrigger(STM_BROWSE_NEXT);
			    break;
 
			case '<':
			    if (dto_supports_browse_prev) DoTrigger(STM_BROWSE_PREV);
			    break;
 
                    } /* switch(msg->Code) */
                    if (strchr(MSG(MSG_SHORTCUT_EDITOR), toupper(msg->Code)))
		    {
                        if ( (GetVar("editor", (STRPTR) editorvarbuffer, 299, GVF_GLOBAL_ONLY)) != -1L )
                        {
                            sprintf(s, "Run QUIET \"%s\" \"%s\"", editorvarbuffer, filename );
		            D(bug("[Multiview] editor command: \"%s\"\n", s));
                            if (SystemTags(s, TAG_END))
		                DisplayBeep(NULL);
                        }
                    }
		    break;
 
		case IDCMP_RAWKEY:
		    switch(msg->Code)
		    {
		    #ifdef __AROS__
		    	case RAWKEY_NM_WHEEL_UP:
		    	case RAWKEY_NM_WHEEL_DOWN:
		    	case RAWKEY_NM_WHEEL_LEFT:
		    	case RAWKEY_NM_WHEEL_RIGHT:
		    #endif
			case CURSORUP:
			case CURSORDOWN:
			case CURSORRIGHT:
			case CURSORLEFT:
			    ScrollTo(msg->Code, msg->Qualifier);
			    break;
 
		    #ifdef __AROS__
			case RAWKEY_HOME: /* HOME */
			    ScrollTo(CURSORUP, IEQUALIFIER_LALT);
			    break;
 
			case RAWKEY_END: /* END */
			    ScrollTo(CURSORDOWN, IEQUALIFIER_LALT);
			    break;
 
			case RAWKEY_PAGEUP: /* PAGE UP */
			    ScrollTo(CURSORUP, IEQUALIFIER_LSHIFT);
			    break;
 
			case RAWKEY_PAGEDOWN: /* PAGE DOWN */
			    ScrollTo(CURSORDOWN, IEQUALIFIER_LSHIFT);
			    break;
		    #endif
 
			case 0x42: /* SHIFT TAB? */
			    if (msg->Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
			    {
				if (dto_supports_prev_field) DoTrigger(STM_PREV_FIELD);
				else if (dto_supports_search_prev) DoTrigger(STM_SEARCH_PREV);
			    }
			    break;
 
		    } /* switch(msg->Code) */
		    break;
 
		case IDCMP_GADGETDOWN:
		    arrowticker = 3;
		    activearrowgad = (struct Gadget *)msg->IAddress;
		    switch(activearrowgad->GadgetID)
		    {
			case GAD_UPARROW:
			    activearrowkind = CURSORUP;
			    ScrollTo(CURSORUP, 0);
			    break;
 
			case GAD_DOWNARROW:
			    activearrowkind = CURSORDOWN;
			    ScrollTo(CURSORDOWN, 0);
			    break;
 
			case GAD_LEFTARROW:
			    activearrowkind = CURSORLEFT;
			    ScrollTo(CURSORLEFT, 0);
			    break;
 
			case GAD_RIGHTARROW:
			    activearrowkind = CURSORRIGHT;
			    ScrollTo(CURSORRIGHT, 0);
			    break;
 
			default:
			    activearrowkind = 0;
			    break;
 
		    }
		    break;
 
		case IDCMP_INTUITICKS:
		    if (activearrowkind)
		    {
			if (arrowticker)
			{
			    arrowticker--;
			}
			else if (activearrowgad->Flags & GFLG_SELECTED)
			{
			    ScrollTo(activearrowkind, 0);
			}
		    }
		    break;
 
		case IDCMP_GADGETUP:
		    switch(((struct Gadget *)msg->IAddress)->GadgetID)
		    {
			case GAD_UPARROW:
			case GAD_DOWNARROW:
			case GAD_LEFTARROW:
			case GAD_RIGHTARROW:
			    activearrowkind = 0;
			    break;
		    }
		    break;
 
		case IDCMP_MENUPICK:
		    men = msg->Code;            
//		    D(bug(" * MV: men %08lx\n", (long)men));
		    while(men != MENUNULL)
		    {
			if ((item = ItemAddress(menus, men)))
			{
//			    D(bug(" * MV: item %08lx  menus %08lx\n", (long)item, (long)menus));
			    switch((ULONG)GTMENUITEM_USERDATA(item))
			    {
				case MSG_MEN_PROJECT_OPEN:
				    filename = GetFileName(MSG_ASL_OPEN_TITLE);
				    if (filename) OpenDTO();
				    break;
 
				case MSG_MEN_PROJECT_SAVEAS:
				    filename = GetFileName(MSG_ASL_SAVE_TITLE);
				    if (filename) DoWriteMethod(filename, DTWM_RAW);
				    break;
 
				case MSG_MEN_PROJECT_SAVEAS_IFF:
				    filename = GetFileName(MSG_ASL_SAVE_TITLE);
				    if (filename) DoWriteMethod(filename, DTWM_IFF);
				    break;
 
				case MSG_MEN_PROJECT_PRINT:
				    OutputMessage(not_supported);
				    break;
 
				case MSG_MEN_PROJECT_ABOUT:
				    About();
				    break;
 
				case MSG_MEN_PROJECT_QUIT:
				    quitme = TRUE;
				    break;
 
				case MSG_MEN_EDIT_MARK:
				#if defined(__AROS__) && !defined(__MORPHOS__)
				    if (StartDragSelect(dto))
				#else
				    {
					struct DTSpecialInfo *si;
 
					/*
					** ClipView example on AmigaDev CD does just the following.
					** None of the checks AROS datatypes.library/StartDragSelect()
					** does.
					*/
 
					si = (struct DTSpecialInfo *)(((struct Gadget *)dto)->SpecialInfo);
					si->si_Flags |= DTSIF_DRAGSELECT;
				    }
				#endif
				    {
					#warning TODO: change mouse pointer to crosshair
				    }
				    break;
 
				case MSG_MEN_EDIT_COPY:
				    {
					struct dtGeneral dtg;
 
					dtg.MethodID = DTM_COPY;
					dtg.dtg_GInfo = NULL;
 
					DoDTMethodA(dto, win, NULL, (Msg)&dtg);
				    }
				    break;
 
				case MSG_MEN_EDIT_SELECTALL:
				    OutputMessage(not_supported);
				    break;
 
				case MSG_MEN_EDIT_CLEARSELECTED:
				    {
					struct dtGeneral dtg;
 
					dtg.MethodID = DTM_CLEARSELECTED;
					dtg.dtg_GInfo = NULL;
 
					DoDTMethodA(dto, win, NULL, (Msg)&dtg);
				    }
				    break;
 
				case MSG_MEN_WINDOW_SEPSCREEN:
				    OutputMessage(not_supported);
				    break;
 
				case MSG_MEN_WINDOW_MINIMIZE:
				    OutputMessage(not_supported);
				    break;
 
				case MSG_MEN_WINDOW_NORMAL:
				    OutputMessage(not_supported);
				    break;
 
				case MSG_MEN_WINDOW_MAXIMIZE:
				    OutputMessage(not_supported);
				    break;
 
				case MSG_MEN_SETTINGS_SAVEDEF:
				    OutputMessage(not_supported);
				    break;
 
				case MSG_MEN_PICT_ZOOM_IN:
				    pdt_zoom++;
				    if (pdt_zoom == -1 ) pdt_zoom = 1;
				    DoZoom(pdt_zoom);
				    break;
 
				case MSG_MEN_PICT_ZOOM_OUT:
				    pdt_zoom--;
				    if (pdt_zoom == 0 ) pdt_zoom = -2;
				    DoZoom(pdt_zoom);
				    break;
 
				case MSG_MEN_PICT_RESET:
				    pdt_zoom = 1;
				    DoZoom(pdt_zoom);
				    break;
 
				case MSG_MEN_PICT_FIT_WIN:
				    pdt_fit_win = (item->Flags & CHECKED) ? TRUE : FALSE;
				    FitToWindow();
				    DoLayout(TRUE);
				    break;
 
				case MSG_MEN_PICT_KEEP_ASPECT:
				    pdt_keep_aspect = (item->Flags & CHECKED) ? TRUE : FALSE;
				    FitToWindow();
				    DoLayout(TRUE);
				    break;
 
				case MSG_MEN_PICT_FORCE_MAP:
				    SetDTAttrs (dto, NULL, NULL,
						PDTA_DestMode, (item->Flags & CHECKED) ? FALSE : TRUE,
						TAG_DONE);
				    DoLayout(TRUE);
				    break;
 
				case MSG_MEN_PICT_DITHER:
				    SetDTAttrs (dto, NULL, NULL,
						PDTA_DitherQuality, (item->Flags & CHECKED) ? 4 : 0,
						TAG_DONE);
				    DoLayout(TRUE);
				    break;
 
				case MSG_MEN_TEXT_WORDWRAP:
				    if (item->Flags & CHECKED)
					D(bug("wordwrap enabled\n"));
				    else
					D(bug("wordwrap disabled\n"));
				    SetDTAttrs (dto, NULL, NULL,
						TDTA_WordWrap, (item->Flags & CHECKED) ? TRUE : FALSE,
						TAG_DONE);
				    DoLayout(TRUE);
				    break;
 
				case MSG_MEN_TEXT_SEARCH:
				    if (dto_supports_search) DoTrigger(STM_SEARCH);
				    break;
 
				case MSG_MEN_TEXT_SEARCH_PREV:
				    if (dto_supports_search_prev) DoTrigger(STM_SEARCH_PREV);
				    break;
 
				case MSG_MEN_TEXT_SEARCH_NEXT:
				    if (dto_supports_search_next) DoTrigger(STM_SEARCH_NEXT);
				    break;
 
			    } /* switch(GTMENUITEM_USERDATA(item)) */
 
			    men = item->NextSelect;
			}
			else
			{
			    men = MENUNULL;
			}
 
		    } /* while(men != MENUNULL) */
		    break;
 
		case IDCMP_NEWSIZE:
		    D(bug("IDCMP NEWSIZE\n"));
		    FitToWindow();
		    break;
 
		case IDCMP_IDCMPUPDATE:
		    tstate = tags = (const struct TagItem *) msg->IAddress;
		    while ((tag = NextTagItem(&tstate)) != NULL)
		    {
			tidata = tag->ti_Data;
//			D(bug("IDCMP UPDATE %08lx %08lx\n", (long)tag->ti_Tag, (long)tag->ti_Data));
			switch (tag->ti_Tag)
			{
			    /* Change in busy state */
			    case DTA_Busy:
				if (tidata)
				    SetWindowPointer (win, WA_BusyPointer, TRUE, TAG_DONE);
				else
				    SetWindowPointer (win, WA_Pointer, (IPTR) NULL, TAG_DONE);
				break;
 
			    case DTA_Title:
				SetWindowTitles(win, (UBYTE *)tidata, (UBYTE *)~0);
				break;
 
			    /* Error message */
			    case DTA_ErrorLevel:
/*                              if (tidata)
				{
				    errnum = GetTagData (DTA_ErrorNumber, NULL, tags);
				    PrintErrorMsg (errnum, (STRPTR) options[OPT_NAME]);
				}*/
				break;
 
			    /* Time to refresh */
			    case DTA_Sync:
				/* Refresh the DataType object */
				D(bug("Multiview: DTA_SYNC\n"));
				RefreshDTObjects (dto, win, NULL, (IPTR) NULL);
				break;
 
			} /* switch (tag->ti_Tag) */
 
		    } /* while ((tag = NextTagItem ((const struct TagItem **)&tstate))) */
		    break;
 
	    } /* switch (msg->Class) */
 
	    ReplyMsg((struct Message *)msg);
 
	} /* while((msg = (struct IntuiMessage *)GetMsg(win->UserPort))) */
 
    } /* while (!quitme) */
}
 
/*********************************************************************************************/
 
int main(int argc, char **argv)
{
    InitLocale("System/Utilities/MultiView.catalog", 1);
    InitMenus(nm);
    InitMenus(nmpict);
    InitMenus(nmtext);
    OpenLibs();
    InitDefaults();
 
    if (argc == 0)
    {
        struct WBStartup *startup = (struct WBStartup *) argv;
 
        if (startup->sm_NumArgs >= 2)
        {
            /* FIXME: all arguments but the first are ignored */
            cd       = CurrentDir(startup->sm_ArgList[1].wa_Lock);
            filename = startup->sm_ArgList[1].wa_Name;
        }
        else
        {
            filename = GetFileName(MSG_ASL_OPEN_TITLE);
            if (!filename) Cleanup(NULL);
        }
    }
    else
    {
        GetArguments();
    }
 
    LoadFont();
    MakeICObjects();
    OpenDTO();
    GetVisual();
    MakeGadgets();
    menus = MakeMenus(nm);
    pictmenus = MakeMenus(nmpict);
    textmenus = MakeMenus(nmtext);
    SetMenuFlags();
    MakeWindow();
    HandleAll();
    Cleanup(NULL);
 
    return 0;
}
 
/*********************************************************************************************/


/*
    Copyright © 1995-2009, The AROS Development Team. All rights reserved.
    $Id$
*/
 
/*********************************************************************************************/
 
#include "global.h"
#include "version.h"
 
#include <string.h>
 
#include "compilerspecific.h"
#include "debug.h"
 
/*********************************************************************************************/
 
static struct MenuItem * FindMenuItem( struct Menu *menu, ULONG msgid );
static void ChangeItemState( ULONG msgid, BOOL state );
 
/*********************************************************************************************/
 
struct NewMenu nm[] =
{
    {NM_TITLE, (STRPTR)MSG_MEN_PROJECT                                                  },	/* 0 */
     {NM_ITEM, (STRPTR)MSG_MEN_PROJECT_OPEN                                             },
     {NM_ITEM, NM_BARLABEL                                                              },
     {NM_ITEM, (STRPTR)MSG_MEN_PROJECT_SAVEAS                                           },
     {NM_ITEM, (STRPTR)MSG_MEN_PROJECT_SAVEAS_IFF                                       },
     {NM_ITEM, NM_BARLABEL                                                              },
     {NM_ITEM, (STRPTR)MSG_MEN_PROJECT_PRINT                                            },
     {NM_ITEM, (STRPTR)MSG_MEN_PROJECT_ABOUT                                            },
     {NM_ITEM, NM_BARLABEL                                                              },
     {NM_ITEM, (STRPTR)MSG_MEN_PROJECT_QUIT                                             },
    {NM_TITLE, (STRPTR)MSG_MEN_EDIT                                                     },	/* 1 */
     {NM_ITEM, (STRPTR)MSG_MEN_EDIT_MARK                                                },
     {NM_ITEM, (STRPTR)MSG_MEN_EDIT_COPY                                                },
     {NM_ITEM, NM_BARLABEL                                                              },
     {NM_ITEM, (STRPTR)MSG_MEN_EDIT_SELECTALL                                           },
     {NM_ITEM, (STRPTR)MSG_MEN_EDIT_CLEARSELECTED                                       },
    {NM_TITLE, (STRPTR)MSG_MEN_WINDOW                                                   },	/* 2 */
     {NM_ITEM, (STRPTR)MSG_MEN_WINDOW_SEPSCREEN         , 0, CHECKIT | MENUTOGGLE       },
     {NM_ITEM, NM_BARLABEL                                                              },
     {NM_ITEM, (STRPTR)MSG_MEN_WINDOW_MINIMIZE                                          },
     {NM_ITEM, (STRPTR)MSG_MEN_WINDOW_NORMAL                                            },
     {NM_ITEM, (STRPTR)MSG_MEN_WINDOW_MAXIMIZE                                          },
    {NM_TITLE, (STRPTR)MSG_MEN_SETTINGS                                                 },	/* 3 */
     {NM_ITEM, (STRPTR)MSG_MEN_SETTINGS_SAVEDEF                                         },
    {NM_END}
};
 
struct NewMenu nmpict[] =
{
    {NM_TITLE, (STRPTR)MSG_MEN_PICT                                                      },
     {NM_ITEM, (STRPTR)MSG_MEN_PICT_ZOOM_IN                                              },
     {NM_ITEM, (STRPTR)MSG_MEN_PICT_ZOOM_OUT                                             },
     {NM_ITEM, (STRPTR)MSG_MEN_PICT_RESET                                                },
     {NM_ITEM, NM_BARLABEL                                                               },
     {NM_ITEM, (STRPTR)MSG_MEN_PICT_FIT_WIN              , 0, CHECKIT | MENUTOGGLE       },
     {NM_ITEM, (STRPTR)MSG_MEN_PICT_KEEP_ASPECT          , 0, CHECKIT | MENUTOGGLE       },
     {NM_ITEM, NM_BARLABEL                                                               },
     {NM_ITEM, (STRPTR)MSG_MEN_PICT_FORCE_MAP            , 0, CHECKIT | MENUTOGGLE       },
     {NM_ITEM, (STRPTR)MSG_MEN_PICT_DITHER               , 0, CHECKIT | MENUTOGGLE | CHECKED },
    {NM_END}
};
 
struct NewMenu nmtext[] =
{
    {NM_TITLE, (STRPTR)MSG_MEN_TEXT                                                      },
     {NM_ITEM, (STRPTR)MSG_MEN_TEXT_WORDWRAP             , 0, CHECKIT | MENUTOGGLE       },
     {NM_ITEM, (STRPTR)MSG_MEN_TEXT_SEARCH                                               },
     {NM_ITEM, (STRPTR)MSG_MEN_TEXT_SEARCH_PREV                                          },
     {NM_ITEM, (STRPTR)MSG_MEN_TEXT_SEARCH_NEXT                                          },
    {NM_END}
};
 
/*********************************************************************************************/
 
static struct MenuItem * FindMenuItem( struct Menu *menu, ULONG msgid )
{
    struct MenuItem *item;
 
    while( menu )
    {
	if( (ULONG)GTMENU_USERDATA(menu) == msgid )
	    return (struct MenuItem *)menu;
	item = menu->FirstItem;
	while( item )
	{
	    if( (ULONG)GTMENUITEM_USERDATA(item) == msgid )
		return item;
	    item = item->NextItem;
	}
	menu = menu->NextMenu;
    }
    return NULL;
}
 
/*********************************************************************************************/
 
static void ChangeItemState( ULONG msgid, BOOL state )
{
    struct MenuItem *item;
 
    item = FindMenuItem(menus, msgid);
    if (item)
    {
	if (state) item->Flags |= ITEMENABLED; else item->Flags &= ~ITEMENABLED;
    }
}
 
/*********************************************************************************************/
 
void InitMenus(struct NewMenu *newm)
{
    struct NewMenu *actnm;
 
    for(actnm = newm; actnm->nm_Type != NM_END; actnm++)
    {
	if (actnm->nm_Label != NM_BARLABEL)
	{
	    ULONG  id = (ULONG)actnm->nm_Label;
	    CONST_STRPTR str = MSG(id);
 
	    if (actnm->nm_Type == NM_TITLE)
	    {
		actnm->nm_Label = str;
	    } else {
		actnm->nm_Label = str + 2;
		if (str[0] != ' ') actnm->nm_CommKey = str;
	    }
	    actnm->nm_UserData = (APTR)id;
 
	} /* if (actnm->nm_Label != NM_BARLABEL) */
 
    } /* for(actnm = nm; nm->nm_Type != NM_END; nm++) */
 
}
 
/*********************************************************************************************/
 
struct Menu * MakeMenus(struct NewMenu *newm)
{
    struct Menu *menu;
    struct TagItem menu_tags[] =
    {
	{GTMN_NewLookMenus, TRUE},
	{TAG_DONE               }
    };
 
    menu = CreateMenusA(newm, NULL);
    if (!menu) Cleanup(MSG(MSG_CANT_CREATE_MENUS));
 
    if (!LayoutMenusA(menu, vi, menu_tags))
    {
	FreeMenus(menu);
	Cleanup(MSG(MSG_CANT_CREATE_MENUS));
    }
    return menu;
}
 
/*********************************************************************************************/
 
void KillMenus(void)
{
    if (win) ClearMenuStrip(win);
    if (menus) FreeMenus(menus);
    if (pictmenus) FreeMenus(pictmenus);
    if (textmenus) FreeMenus(textmenus);
 
    menus = NULL;
    pictmenus = NULL;
    textmenus = NULL;
}
 
/*********************************************************************************************/
 
void SetMenuFlags(void)
{
    struct Menu *menu;
    struct MenuItem *item;
    IPTR val;
    BOOL ret;
 
    if (win) ClearMenuStrip(win);
 
    ChangeItemState( MSG_MEN_PROJECT_SAVEAS, dto_supports_write );
    ChangeItemState( MSG_MEN_PROJECT_SAVEAS_IFF, dto_supports_write_iff );
    ChangeItemState( MSG_MEN_PROJECT_PRINT, dto_supports_print );
    ChangeItemState( MSG_MEN_EDIT_COPY, dto_supports_copy );
    ChangeItemState( MSG_MEN_EDIT_SELECTALL, dto_supports_selectall );
    ChangeItemState( MSG_MEN_EDIT_CLEARSELECTED, dto_supports_clearselected );
 
    item = FindMenuItem(menus, MSG_MEN_SETTINGS);	/* Search last menu, then append dt group dependent menu */
    menu = (struct Menu *)item;
    if (menu)
    {
	if (dto_subclass_gid == GID_PICTURE)
	{
	    D(bug("Multiview: is picture.datatype\n"));
	    menu->NextMenu = pictmenus;
	}
	else if (dto_subclass_gid == GID_TEXT)
	{
	    D(bug("Multiview: is text.datatype\n"));
	    menu->NextMenu = textmenus;
	    ret = GetDTAttrs(dto, TDTA_WordWrap, (IPTR)&val, TAG_DONE);
	    item = FindMenuItem(menus, MSG_MEN_TEXT_WORDWRAP);
	    if (ret && item)
	    {
		if (val) item->Flags |= CHECKED; else item->Flags &= ~CHECKED;
	    }
	    ChangeItemState( MSG_MEN_TEXT_WORDWRAP, ret );
	    ChangeItemState( MSG_MEN_TEXT_SEARCH, dto_supports_search );
	    ChangeItemState( MSG_MEN_TEXT_SEARCH_PREV, dto_supports_search_prev );
	    ChangeItemState( MSG_MEN_TEXT_SEARCH_NEXT, dto_supports_search_next );
	}
	else
	{
	    D(bug("Multiview: is unknown datatype\n"));
	    menu->NextMenu = NULL;
	}
    }
 
    {
	struct TagItem menu_tags[] =
	{
	    {GTMN_NewLookMenus, TRUE},
	    {TAG_DONE               }
	};
 
	LayoutMenusA(menus, vi, menu_tags);
    }
 
    if (win) SetMenuStrip(win, menus);
}
 
/*********************************************************************************************/
 
STRPTR GetFileName(ULONG msgtextid)
{
    static UBYTE         pathbuffer[300];
    static UBYTE         filebuffer[300];
    struct FileRequester *req;
    STRPTR               filepart, retval = NULL;
 
    AslBase = OpenLibrary("asl.library", 39);
    if (AslBase)
    {
	filebuffer[299] = 0;
	pathbuffer[299] = 0;
 
	strncpy(filebuffer, FilePart(filenamebuffer), 299);
	strncpy(pathbuffer, filenamebuffer, 299);
	filepart = FilePart(pathbuffer);
	*filepart = 0;
 
	req = AllocAslRequestTags(ASL_FileRequest, ASLFR_TitleText    , (IPTR)MSG(msgtextid),
						   ASLFR_DoPatterns   , TRUE                         ,
						   ASLFR_InitialFile  , (IPTR)filebuffer             ,
						   ASLFR_InitialDrawer, (IPTR)pathbuffer             ,
						   ASLFR_Window       , (IPTR)win   	    	     ,
						   TAG_DONE);
	if (req)
	{
	    if (AslRequest(req, NULL))
	    {
		strncpy(filebuffer, req->fr_Drawer, 299);
		AddPart(filebuffer, req->fr_File, 299);
 
		retval = filebuffer;
 
	    } /* if (AslRequest(req, NULL) */
 
	    FreeAslRequest(req);
 
	} /* if (req) */
 
	CloseLibrary(AslBase);
 
    } /* if (AslBase) */
 
    return retval;
}
 
/*********************************************************************************************/
 
void About(void)
{
    struct DataType     *dt = NULL;
    struct EasyStruct   es;
    STRPTR              gid_string = NULL;
    STRPTR              name_string = NULL;
    STRPTR              sp;
    WORD                i;
    UBYTE               dtver_string[100];
 
    if (GetDTAttrs(dto, DTA_DataType, (IPTR)&dt, TAG_DONE))
    {
	if (dt)
	{
	    gid_string = (STRPTR) GetDTString(dt->dtn_Header->dth_GroupID);
	    name_string = dt->dtn_Header->dth_Name;
	}
    }
 
    if (!gid_string) gid_string = "";
    if (!name_string) name_string = "";
 
    for(sp = DataTypesBase->lib_IdString;
	(*sp != 0) && ((*sp < '0') || (*sp > '9'));
	sp++)
    {
    }
 
    i = 0;
    while ((*sp != 0) && (*sp != '\r') && (*sp != '\n') && (i < 99))
    {
	dtver_string[i++] = *sp++;
    }
    dtver_string[i++] = '\0';
 
    es.es_StructSize   = sizeof(es);
    es.es_Flags        = 0;
    es.es_Title        = MSG(MSG_ABOUT_TITLE);
    es.es_TextFormat   = MSG(MSG_ABOUT);
    es.es_GadgetFormat = MSG(MSG_CONTINUE);
 
    EasyRequest(win, &es, NULL, (IPTR)VERSION,
				(IPTR)REVISION,
				(IPTR)DATESTR, 
				(IPTR)dtver_string,
				(IPTR)name_string,
				(IPTR)gid_string);
 
}
 
/*********************************************************************************************/
 
ULONG DoTrigger(ULONG what)
{
    struct dtTrigger msg;
 
    msg.MethodID          = DTM_TRIGGER;
    msg.dtt_GInfo         = NULL;
    msg.dtt_Function      = what;
    msg.dtt_Data          = NULL;
 
    return DoDTMethodA(dto, win, NULL, (Msg)&msg);
}
 
/*********************************************************************************************/
 
ULONG DoWriteMethod(STRPTR name, ULONG mode)
{
    struct dtWrite msg;
    BPTR fh;
    ULONG retval;
 
    fh = NULL;
    if (name)
    {
	fh = Open( name, MODE_NEWFILE );
	if (!fh)
	{
	    D(bug("Multiview: Cannot open %s\n", name));
	    OutputMessage(MSG(MSG_SAVE_FAILED));
	    return FALSE;
	}
    }
 
 
 
    msg.MethodID          = DTM_WRITE;
    msg.dtw_GInfo         = NULL;
    msg.dtw_FileHandle    = fh;
    msg.dtw_Mode          = mode;
    msg.dtw_AttrList      = NULL;
 
    D(bug("Multiview: Saving %s mode %ld\n", name ? name : (STRPTR)"[nothing]", mode));
    retval = DoDTMethodA(dto, win, NULL, (Msg)&msg);
    if (fh)
    {
	Close( fh );
	if( !retval )
	{
	    D(bug("Multiview: Error during write !\n"));
	    OutputMessage(MSG(MSG_SAVE_FAILED));
	}
    }
    return retval;
}
 
/*********************************************************************************************/
 
ULONG DoLayout(ULONG initial)
{
    ULONG res;
    struct gpLayout msg;
 
    D(bug("=> erase\n"));
    EraseRect(win->RPort, win->BorderLeft,
			  win->BorderTop,
			  win->Width - 1 - win->BorderRight,
			  win->Height - 1 - win->BorderBottom);
 
#if 1
    msg.MethodID	= GM_LAYOUT;
    msg.gpl_GInfo	= NULL;
    msg.gpl_Initial	= initial;
 
#if 0
    D(bug("=> doasynclayout libcall\n"));
    res = DoAsyncLayout(dto, &msg);
#else
    D(bug("=> GM_Layout method\n"));
    res = DoDTMethodA(dto, win, 0, (Msg)&msg);
#endif
    D(bug("layout result %ld\n", res));
    return res;
#else
    RemoveDTObject(win, dto);
    AddDTObject(win, NULL, dto, -1);
#endif
}
 
/*********************************************************************************************/
 
ULONG DoScaleMethod(ULONG xsize, ULONG ysize, BOOL aspect)
{
    struct pdtScale msg;
 
    D(bug(" scale width %d height %d\n", xsize, ysize));
    msg.MethodID	= PDTM_SCALE;
    msg.ps_NewWidth	= xsize;
    msg.ps_NewHeight	= ysize;
    msg.ps_Flags	= aspect ? PScale_KeepAspect : 0;
    // D(bug("- method %08lx newwidth %ld newheight %ld flags %08lx\n", msg.MethodID, msg.ps_NewWidth, msg.ps_NewHeight, msg.ps_Flags));
 
    return DoMethodA(dto, (Msg)&msg);
}
 
/*********************************************************************************************/
 
void DoZoom(WORD zoomer)
{
    UWORD curwidth, curheight;
 
    if (zoomer > 0)
    {
	curwidth = pdt_origwidth * zoomer;
	curheight = pdt_origheight * zoomer;
    }
    else
    {
	curwidth = pdt_origwidth / -zoomer;
	curheight = pdt_origheight / -zoomer;
    }
    D(bug(" zoom %d width %d height %d\n", zoomer, curwidth, curheight));
    DoScaleMethod(curwidth, curheight, 0);
    DoLayout(TRUE);
}
 
/*********************************************************************************************/

Reference[edit]

struct DiskObject *NewDiskObject(ULONG type) 
struct DiskObject *GetIconTagList(CONST_STRPTR name, const struct TagItem *tags) 

struct DiskObject *GetDiskObject(CONST_STRPTR name) 
BOOL PutDiskObject(CONST_STRPTR name, struct DiskObject *icon) 
void FreeDiskObject(struct DiskObject *diskobj) 

BOOL AddFreeList(struct FreeList *freelist, APTR mem, unsigned long size) 
void FreeFreeList(struct FreeList *freelist) 

UBYTE *FindToolType(const STRPTR *toolTypeArray, const STRPTR typeName) 
BOOL MatchToolValue(UBYTE *typeString, UBYTE *value) 
UBYTE *BumpRevision(UBYTE *newname, UBYTE *oldname) 

struct DiskObject *GetDefDiskObject(LONG type) 
BOOL PutDefDiskObject(struct DiskObject *icon) 
struct DiskObject *GetDiskObjectNew(CONST_STRPTR name) 
BOOL DeleteDiskObject(UBYTE *name) 

struct DiskObject *DupDiskObjectA(struct DiskObject *icon, struct TagItem *tags) 

ULONG IconControlA(struct DiskObject *icon, struct TagItem *tags) 
void DrawIconStateA(struct RastPort *rp, struct DiskObject *icon, STRPTR label, LONG leftEdge, LONG topEdge, ULONG state, struct TagItem *tags) 

BOOL GetIconRectangleA(struct RastPort *rp, struct DiskObject *icon, STRPTR label, struct Rectangle *rectangle, struct TagItem *tags) 

BOOL PutIconTagList(CONST_STRPTR name, struct DiskObject *icon, struct TagItem *tags) 
BOOL LayoutIconA(struct DiskObject *icon, struct Screen *screen, struct TagItem *tags) 
void ChangeToSelectedIconColor(struct ColorRegister *cr)