/**
 * Scout - The Amiga System Monitor
 *
 *------------------------------------------------------------------
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * You must not use this source code to gain profit of any kind!
 *
 *------------------------------------------------------------------
 *
 * @author Andreas Gelhausen
 * @author Richard Körber <rkoerber@gmx.de>
 */



#include "system_headers.h"

extern struct GfxBase   *GfxBase;
extern struct ExecBase  *SysBase;
extern APTR AboutText;

UBYTE  updatetimetext[] = "1.0\0\0\0\0\0";

static APTR oldwindowptr;

BOOL GetPriority( UBYTE *nodename,
                  LONG *pri )
{
    BOOL result = FALSE;
    APTR priWin;

    if (priWin = PriorityWindowObject, End) {
        set(priWin, MUIA_PriorityWin_ObjectName, nodename);
        set(priWin, MUIA_PriorityWin_Priority, *pri);

        if (result = (BOOL)DoMethod(priWin, MUIM_PriorityWin_GetPriority)) {
            get(priWin, MUIA_PriorityWin_Priority, pri);
        }


        MUI_DisposeObject(priWin);
    }

    return result;
}

void NoReqOn( void )
{
    struct Process *myprocess;

    myprocess = (struct Process *)FindTask(NULL);
    oldwindowptr = myprocess->pr_WindowPtr;
    myprocess->pr_WindowPtr = (APTR) -1;
}

void NoReqOff( void )
{
    ((struct Process *)FindTask(NULL))->pr_WindowPtr = oldwindowptr;
}

struct Library *MyOpenLibrary( UBYTE *libname,
                               ULONG version )
{
    struct Library *libbase;

    if (!(libbase = OpenLibrary(libname, version))) {
        aprintf("Failed to open %s version %ld!\n", libname, version);
    }

    return libbase;
}

void Flags2BinStr( UBYTE flags,
                   UBYTE *str,
                   ULONG maxlen )
{
    _snprintf(str, maxlen, "%%%ld%ld%ld%ld%ld%ld%ld%ld", (flags & (1 << 7)) ? 1 : 0,
                                                         (flags & (1 << 6)) ? 1 : 0,
                                                         (flags & (1 << 5)) ? 1 : 0,
                                                         (flags & (1 << 4)) ? 1 : 0,
                                                         (flags & (1 << 3)) ? 1 : 0,
                                                         (flags & (1 << 2)) ? 1 : 0,
                                                         (flags & (1 << 1)) ? 1 : 0,
                                                         (flags & (1 << 0)) ? 1 : 0);
}

void MySetContents( APTR textfield,
                    UBYTE *fmt, ...)
{
    UBYTE *buf;

    if (buf = tbAllocVecPooled(globalPool, TMP_STRING_LENGTH)) {
        va_list arg;

        va_start(arg, fmt);
        _vsnprintf(buf, TMP_STRING_LENGTH, fmt, arg);
        va_end(arg);

        nnset(textfield, MUIA_Text_Contents, buf);

        tbFreeVecPooled(globalPool, buf);
    }
}

void MySetContentsHealed( APTR textfield,
                          UBYTE *fmt, ...)
{
    UBYTE *buf;

    if (buf = tbAllocVecPooled(globalPool, TMP_STRING_LENGTH)) {
        va_list arg;

        va_start(arg, fmt);
        _vsnprintf(buf, TMP_STRING_LENGTH, fmt, arg);
        va_end(arg);

        healstring (buf);
        nnset(textfield, MUIA_Text_Contents, buf);

        tbFreeVecPooled(globalPool, buf);
    }
}

void MySetStringContents( APTR textfield,
                          UBYTE *fmt, ... )
{
    UBYTE *buf;

    if (buf = tbAllocVecPooled(globalPool, TMP_STRING_LENGTH)) {
        va_list arg;

        va_start(arg, fmt);
        _vsnprintf(buf, TMP_STRING_LENGTH, fmt, arg);
        va_end(arg);

        nnset(textfield, MUIA_String_Contents, buf);

        tbFreeVecPooled(globalPool, buf);
    }
}

void SetCountText( APTR counttext,
                   ULONG structcnt )
{
   MySetContents(counttext, MUIX_R "%04ld", structcnt);
}

UBYTE *MyGetWindowTitle( UBYTE *type,
                         UBYTE *title,
                         ULONG maxlen )
{
    BOOL ok;
    ULONG i;

    if (type[0] != 0x00) {
        _snprintf(title, maxlen, "SCOUT: %s", type);
    } else {
        stccpy(title, "SCOUT", maxlen);
    }

    Forbid();
    ok = (FindPort("AMITCP") != NULL);
    Permit();

    if (ok) {
        UBYTE *host;

        host = (opts.Host) ? opts.Host : (UBYTE *)"localhost";

        if (type[0] != 0x00) {
            _snprintf(title, maxlen, "SCOUT: %s   [ %s ]", type, host);
        } else {
            _snprintf(title, maxlen, "SCOUT   [ %s ]", host);
        }
    }

    for (i = 0; i < strlen(title); i++) title[i] = ToUpper(title[i]);

    return title;
}

UBYTE *MyGetChildWindowTitle( UBYTE *part1,
                            UBYTE *part2,
                            UBYTE *title,
                            ULONG maxlen )
{
    UBYTE *esc;

    _snprintf(title, maxlen, "%s: %s", part1, part2);
    if (esc = strchr(title, '\033')) *esc = 0x00;

    return title;
}

void ApplicationSleep( BOOL sleep )
{
    set(AP_Scout, MUIA_Application_Sleep, sleep);
}

void RedrawActiveEntry( APTR list )
{
    DoMethod(list, MUIM_NList_Redraw, MUIV_NList_Redraw_Active);
}

void RemoveActiveEntry( APTR list )
{
    DoMethod(list, MUIM_NList_Remove, MUIV_NList_Remove_Active);
}

void InsertBottomEntry( APTR list,
                        APTR entry )
{
    DoMethod(list, MUIM_NList_InsertSingle, entry, MUIV_List_Insert_Bottom);
}

void InsertSortedEntry( APTR list,
                        APTR entry )
{
    DoMethod(list, MUIM_NList_InsertSingle, entry, MUIV_List_Insert_Sorted);
}

APTR GetActiveEntry( APTR list )
{
    APTR result = NULL;
   
    if (list) DoMethod(list, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &result);

    return result;
}

struct MUI_NListtree_TreeNode *GetActiveTreeNode( APTR tree )
{
    struct MUI_NListtree_TreeNode *result = NULL;

    if (tree) result = (struct MUI_NListtree_TreeNode *)DoMethod(tree, MUIM_NListtree_GetEntry, MUIV_NListtree_GetEntry_ListNode_Active, MUIV_NListtree_GetEntry_Position_Active, 0);

    return result;
}

APTR MyNListviewObject( ULONG id,
                        UBYTE *format,
                        struct Hook *conhook,
                        struct Hook *deshook,
                        struct Hook *dsphook,
                        struct Hook *cmphook,
                        BOOL input )
{
    APTR list;

    if (list = NListviewObject,
        MUIA_CycleChain, TRUE,
        MUIA_NListview_NList, NListObject,
            MUIA_NList_Input, input,
            MUIA_NList_Format, format,
            MUIA_NList_Title, TRUE,
            MUIA_NList_TitleSeparator, TRUE,
            (conhook) ? MUIA_NList_ConstructHook2 : TAG_IGNORE, conhook,
            (deshook) ? MUIA_NList_DestructHook2 : TAG_IGNORE, deshook,
            (dsphook) ? MUIA_NList_DisplayHook2 : TAG_IGNORE, dsphook,
            (cmphook) ? MUIA_NList_CompareHook2 : TAG_IGNORE, cmphook,
            MUIA_NList_Exports, MUIV_NList_Exports_ColWidth | MUIV_NList_Exports_ColOrder,
            MUIA_NList_Imports, MUIV_NList_Imports_ColWidth | MUIV_NList_Imports_ColOrder,
            MUIA_NList_MinColSortable, 0,
            MUIA_NList_Pool, globalPool,
            MUIA_ObjectID, id,
        End,
    End) {
        DoMethod(list, MUIM_Notify, MUIA_NList_SortType,    MUIV_EveryTime, MUIV_Notify_Self, 3, MUIM_Set, MUIA_NList_TitleMark, MUIV_TriggerValue);
        DoMethod(list, MUIM_Notify, MUIA_NList_SortType2,   MUIV_EveryTime, MUIV_Notify_Self, 3, MUIM_Set, MUIA_NList_TitleMark2, MUIV_TriggerValue);
        DoMethod(list, MUIM_Notify, MUIA_NList_TitleClick,  MUIV_EveryTime, MUIV_Notify_Self, 4, MUIM_NList_Sort3, MUIV_TriggerValue, MUIV_NList_SortTypeAdd_2Values, MUIV_NList_Sort3_SortType_Both);
        DoMethod(list, MUIM_Notify, MUIA_NList_TitleClick2, MUIV_EveryTime, MUIV_Notify_Self, 4, MUIM_NList_Sort3, MUIV_TriggerValue, MUIV_NList_SortTypeAdd_2Values, MUIV_NList_Sort3_SortType_2);
    }

    return list;
}

APTR MyNListtreeObject( APTR *tree,
                        UBYTE *format,
                        struct Hook *conhook,
                        struct Hook *deshook,
                        struct Hook *dsphook,
                        struct Hook *cmphook,
                        struct Hook *findhook,
                        ULONG column )
{
    APTR list;

    list = NListviewObject,
        MUIA_CycleChain, TRUE,
        MUIA_NListview_NList, *tree = NListtreeObject,
            InputListFrame,
            MUIA_NListtree_DragDropSort, FALSE,
            MUIA_NListtree_Format, format,
            MUIA_NListtree_Title, TRUE,
            (conhook) ? MUIA_NListtree_ConstructHook : TAG_IGNORE, conhook,
            (deshook) ? MUIA_NListtree_DestructHook : TAG_IGNORE, deshook,
            (dsphook) ? MUIA_NListtree_DisplayHook : TAG_IGNORE, dsphook,
            MUIA_NListtree_CompareHook, (cmphook) ? cmphook : (struct Hook *)MUIV_NListtree_CompareHook_Tail,
            (findhook) ? MUIA_NListtree_FindUserDataHook : TAG_IGNORE, findhook,
            MUIA_NListtree_TreeColumn, column,
            MUIA_NListtree_MultiSelect, MUIV_NListtree_MultiSelect_None,
        End,
    End;

    return list;
}

APTR MyBelowListview( APTR *text,
                      APTR *count )
{
    APTR group;

    group = HGroup,
        Child, *text = MyTextObject5(SPACE40),
        Child, HGroup, MUIA_HorizWeight, 0,
            Child, MyLabel(" Cnt:"),
            Child, *count = MyTextObject4("0000"),
        End,
    End;

    return group;
}

APTR MyTextObject( void)
{
    return (TextObject, MyTextFrame, End);
}

APTR MyTextObject2( void )
{
    return (TextObject, MyTextFrame, MUIA_Text_SetMax, TRUE, End);
}

APTR MyTextObject3( UBYTE *text )
{
    return (TextObject, MyTextFrame, MUIA_Text_Contents, text, End);
}

APTR MyTextObject4( UBYTE *text )
{
    return (TextObject, MyTextFrame, MUIA_Text_SetMax, TRUE, MUIA_Text_Contents, text, End);
}

APTR MyTextObject5( UBYTE *text )
{
    return (TextObject, MyTextFrame, MUIA_Text_SetMin, TRUE, MUIA_Text_Contents, text, End);
}

APTR MyCheckmarkImage( void )
{
    return (ImageObject,
        ImageButtonFrame,
        MUIA_InputMode,      MUIV_InputMode_None,
        MUIA_Image_Spec,     MUII_CheckMark,
        MUIA_Image_Spec,     MUII_CheckMark,
        MUIA_Image_FreeVert, TRUE,
        MUIA_Background,     MUII_ButtonBack,
        MUIA_ShowSelState,   FALSE,
    End);
}

void healstring( UBYTE *s )
{
    ULONG i = 0;

    while (s[i++] != 0x00) {
        if (s[i] > 0xfe && s[i] <= 0x07) {
            s[i] = 0x00;
        } else if (s[i] > 0x07 && s[i] <= 0x11) {
            s[i] = ' ';
        }
    }
}

UBYTE *nonetest( UBYTE *s )
{
    ULONG i = 0;
    UBYTE *t;

    if ((ULONG)s <= 0x200 || (s != NULL && s[0] == 0)) return txtNone;

    t = s;
    while (t[i] != 0x00) {
        if (!((t[i] > 8 && t[i] < 128) || (t[i] > 159 && t[i] != 215 && t[i] != 247 && t[i] != 255))) return txtNone;
        i++;
    }

    return s;
}

BOOL IsReal( UBYTE *text )
{
    ULONG i = 0, punkte = 0;

    while (text[i]) {
        if (text[i] == '.') {
            if (punkte) return FALSE;
            punkte++;
        } else if (!isdigit(text[i])) {
            return FALSE;
        }
        i++;
    }

    return TRUE;
}

BOOL IsHex( UBYTE *text,
            LONG *result )
{
    ULONG i;

    *result = 0;

    if (text) {
        if (text[0] == '$')
            i = 1;
        else if (!strnicmp("0x", text, 2))
            i = 2;
        else
            return FALSE;

        if (strlen(text + i) < 9) {
            *result = strtol(text + i, NULL, 16);
            return TRUE;
        }
    }

    return FALSE;
}

BOOL IsUHex( UBYTE *text,
             ULONG *result)
{
    ULONG i;

    *result = 0;

    if (text) {
        if (text[0] == '$')
            i = 1;
        else if (!strnicmp("0x", text, 2))
            i = 2;
        else
            return FALSE;

        if (strlen(text + i) < 9) {
            *result = strtoul(text + i, NULL, 16);
            return TRUE;
        }
    }

    return FALSE;
}

BOOL IsDec( UBYTE *text,
            LONG *result)
{
    *result = 0;

    if (text != NULL && (strlen(text) < 12)) {
        UBYTE *copy;

        if (copy = tbAllocVecPooled(globalPool, strlen(text) + 1)) {
            UBYTE *p, *q;

            p = text;
            q = copy;
            while (*p != '\0' && isspace(*p)) *p++;
            while (*p != '\0' && (*p == *decimalSeparator || isdigit(*p) || *p == '-')) {
                if (*p != *decimalSeparator) {
                    *q = *p;
                    q++;
                }
                p++;
            }
            *q = 0x00;

            *result = atol(copy);

            tbFreeVecPooled(globalPool, copy);

            return TRUE;
        }
    }

    return FALSE;
}

LONG GetRamPointerCount( struct Library *lib )
{
    LONG max;
    LONG count, offset;

    max = lib->lib_NegSize;

    count = 0;
    for (offset = LIB_VECTSIZE; offset <= max; offset += LIB_VECTSIZE) {
        struct JumpEntry *je;

        je = (struct JumpEntry *)((UBYTE *)lib - offset);

        if (je->je_JMPInstruction == 0x0000) {
            // this cannot (or better: should not) happen
            count = -1;
            break;
        } else if (je->je_JMPInstruction == 0x4ef9) {
            if (points2ram(je->je_JumpAddress) || je->je_JumpAddress == NULL) count++;
        }
    }

    return count;
}

LONG AllocListEntry( APTR pool,
                     APTR source,
                     ULONG size )
{
    void *result;

    if (result = tbAllocVecPooled(pool, size)) {
        CopyMem(source, result, size);
    }

    return (LONG)result;
}

void FreeListEntry( APTR pool,
                    APTR *entry )
{
    if (*entry) {
        tbFreeVecPooled(pool, *entry);
        *entry = NULL;
    }
}

static __asm __saveds void flushdevs_callfunc( void )
{
    FlushDevices();
}

MakeHook(flushdevs_callhook, flushdevs_callfunc);

static __asm __saveds void flushfonts_callfunc( void )
{
    FlushFonts();
}

MakeHook(flushfonts_callhook, flushfonts_callfunc);

static __asm __saveds void flushlibs_callfunc( void )
{
    FlushLibraries();
}

MakeHook(flushlibs_callhook, flushlibs_callfunc);

static __asm __saveds void flushall_callfunc( void )
{
    FlushDevices();
    FlushFonts();
    FlushLibraries();
}

MakeHook(flushall_callhook, flushall_callfunc);

static __asm __saveds void clearvect_callfunc( void )
{
    ClearResetVectors();
}

MakeHook(clearvect_callhook, clearvect_callfunc);

enum menu_ids {
    MENU_PROJECT = 1,
        MENU_ABOUT, MENU_SNAPSHOT, MENU_QUIT,

    MENU_LIST = 10,
       MENU_ALLOCATIONS, MENU_ASSIGNS, MENU_CLASSES, MENU_COMMODITIES, MENU_DEVICES, MENU_EXPANSIONS, MENU_FONTS,
       MENU_INPUTHANDLERS, MENU_INTERRUPTS, MENU_LIBRARIES, MENU_LOCKS, MENU_LOWMEMORY, MENU_MEMORY, MENU_MOUNTS,
       MENU_PORTS, MENU_RESIDENTS, MENU_COMMANDS, MENU_RESOURCES, MENU_SEMAPHORES, MENU_SCREENMODES, MENU_SYSTEM,
       MENU_TASKS, MENU_TIMERS, MENU_VECTORS, MENU_WINDOWS, MENU_PATCHES, MENU_CATALOGS, MENU_AUDIOMODES,

    MENU_OTHER = 50,
        MENU_FLUSHDEVS, MENU_FLUSHFONTS, MENU_FLUSHLIBS, MENU_FLUSHALL, MENU_CLEARVECTORS
};

static const struct NewMenu menu_list[] =
{
    { NM_TITLE, "Project",                NULL, 0, 0, (APTR)MENU_PROJECT        },
    { NM_ITEM,  "?\0About...",            NULL, 0, 0, (APTR)MENU_ABOUT          },
/*
    { NM_ITEM,  NM_BARLABEL,              NULL, 0, 0, 0                         },
    { NM_ITEM,  "Snapshot list columns",  NULL, 0, 0, (APTR)MENU_SNAPSHOT       },
*/
    { NM_ITEM,  NM_BARLABEL,              NULL, 0, 0, 0                         },
    { NM_ITEM,  "Q\0Quit",                NULL, 0, 0, (APTR)MENU_QUIT           },

    { NM_TITLE, "List",                   NULL, 0, 0, (APTR)MENU_LIST           },
    { NM_ITEM,  "A\0Allocations",         NULL, 0, 0, (APTR)MENU_ALLOCATIONS    },
    { NM_ITEM,  "G\0Assigns",             NULL, 0, 0, (APTR)MENU_ASSIGNS        },
    { NM_ITEM,  "B\0Classes",             NULL, 0, 0, (APTR)MENU_CLASSES        },
    { NM_ITEM,  "K\0Commodities",         NULL, 0, 0, (APTR)MENU_COMMODITIES    },
    { NM_ITEM,  "D\0Devices",             NULL, 0, 0, (APTR)MENU_DEVICES        },
    { NM_ITEM,  "X\0Expansions",          NULL, 0, 0, (APTR)MENU_EXPANSIONS     },
    { NM_ITEM,  "F\0Fonts",               NULL, 0, 0, (APTR)MENU_FONTS          },
    { NM_ITEM,  "H\0InputHandlers",       NULL, 0, 0, (APTR)MENU_INPUTHANDLERS  },
    { NM_ITEM,  "I\0Interrupts",          NULL, 0, 0, (APTR)MENU_INTERRUPTS     },
    { NM_ITEM,  "L\0Libraries",           NULL, 0, 0, (APTR)MENU_LIBRARIES      },
    { NM_ITEM,  "O\0Locks",               NULL, 0, 0, (APTR)MENU_LOCKS          },
    { NM_ITEM,  "J\0LowMemory",           NULL, 0, 0, (APTR)MENU_LOWMEMORY      },
    { NM_ITEM,  "M\0Memory",              NULL, 0, 0, (APTR)MENU_MEMORY         },
    { NM_ITEM,  "N\0Mount",               NULL, 0, 0, (APTR)MENU_MOUNTS         },
    { NM_ITEM,  "P\0Ports",               NULL, 0, 0, (APTR)MENU_PORTS          },
    { NM_ITEM,  "R\0Residents",           NULL, 0, 0, (APTR)MENU_RESIDENTS      },
    { NM_ITEM,  "C\0Commands",            NULL, 0, 0, (APTR)MENU_COMMANDS       },
    { NM_ITEM,  "U\0Resources",           NULL, 0, 0, (APTR)MENU_RESOURCES      },
    { NM_ITEM,  "Z\0ScreenModes",         NULL, 0, 0, (APTR)MENU_SCREENMODES    },
    { NM_ITEM,  "S\0Semaphores",          NULL, 0, 0, (APTR)MENU_SEMAPHORES     },
    { NM_ITEM,  "Y\0System",              NULL, 0, 0, (APTR)MENU_SYSTEM         },
    { NM_ITEM,  "T\0Tasks",               NULL, 0, 0, (APTR)MENU_TASKS          },
    { NM_ITEM,  "E\0Timer",               NULL, 0, 0, (APTR)MENU_TIMERS         },
    { NM_ITEM,  "V\0Vectors",             NULL, 0, 0, (APTR)MENU_VECTORS        },
    { NM_ITEM,  "W\0Windows",             NULL, 0, 0, (APTR)MENU_WINDOWS        },
    { NM_ITEM,  NM_BARLABEL,              NULL, 0, 0, 0                         },
    { NM_ITEM,  "1\0Installed patches",   NULL, 0, 0, (APTR)MENU_PATCHES        },
    { NM_ITEM,  "2\0Locale catalogs",     NULL, 0, 0, (APTR)MENU_CATALOGS       },
    { NM_ITEM,  "3\0AHI AudioModes",      NULL, 0, 0, (APTR)MENU_AUDIOMODES     },

    { NM_TITLE, "Other",                  NULL, 0, 0, (APTR)MENU_OTHER          },
    { NM_ITEM,  "Flush devices",          NULL, 0, 0, (APTR)MENU_FLUSHDEVS      },
    { NM_ITEM,  "Flush fonts",            NULL, 0, 0, (APTR)MENU_FLUSHFONTS     },
    { NM_ITEM,  "Flush libraries",        NULL, 0, 0, (APTR)MENU_FLUSHLIBS      },
    { NM_ITEM,  ".\0Flush all",           NULL, 0, 0, (APTR)MENU_FLUSHALL       },
    { NM_ITEM,  NM_BARLABEL,              NULL, 0, 0, 0                         },
    { NM_ITEM,  "!\0Clear reset vectors", NULL, 0, 0, (APTR)MENU_CLEARVECTORS   },

    { NM_END,   NULL,                     0,    0, 0, 0                         },
};

APTR GetApplication( void )
{
    APTR app, win;
    APTR menustrip;

    if (AP_Scout = app = ApplicationObject,
        MUIA_Application_Title      , PROGNAME,
        MUIA_Application_Version    , version_date,
        MUIA_Application_Copyright  , COPYRIGHT,
        MUIA_Application_Author     , AUTHOR,
        MUIA_Application_Description, DESCRIPTION,
        MUIA_Application_Base       , PROGNAME,
        MUIA_Application_Commands   , arexx_list,
        MUIA_Application_Menustrip, menustrip = MUI_MakeObject(MUIO_MenustripNM, menu_list, MUIO_MenustripNM_CommandKeyCheck),
        MUIA_Application_DiskObject , GetDiskObject("PROGDIR:Scout"),
        MUIA_Application_HelpFile   , "scout.guide",
        SubWindow, WI_Main = win = MainWindowObject,
            MUIA_Window_MaxChildWindowCount, (opts.SingleWindows) ? 1 : 0,
        End,
    End) {
        struct PatchPort *pp;
        struct SetManPort *sp;
        APTR menu;

        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_ABOUT,         win,                     1, MUIM_MainWin_About);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_SNAPSHOT,      MUIV_Notify_Self,        2, MUIM_Application_Save, MUIV_Application_Save_ENV);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_SNAPSHOT,      MUIV_Notify_Self,        2, MUIM_Application_Save, MUIV_Application_Save_ENVARC);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_QUIT,          MUIV_Notify_Self,        2, MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);

        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_ALLOCATIONS,   win,                     1, MUIM_MainWin_ShowAllocations);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_ASSIGNS,       win,                     1, MUIM_MainWin_ShowAssigns);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_CLASSES,       win,                     1, MUIM_MainWin_ShowClasses);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_COMMODITIES,   win,                     1, MUIM_MainWin_ShowCommodities);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_DEVICES,       win,                     1, MUIM_MainWin_ShowDevices);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_EXPANSIONS,    win,                     1, MUIM_MainWin_ShowExpansions);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_FONTS,         win,                     1, MUIM_MainWin_ShowFonts);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_INPUTHANDLERS, win,                     1, MUIM_MainWin_ShowInputHandlers);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_INTERRUPTS,    win,                     1, MUIM_MainWin_ShowInterrupts);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_LIBRARIES,     win,                     1, MUIM_MainWin_ShowLibraries);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_LOCKS,         win,                     1, MUIM_MainWin_ShowLocks);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_LOWMEMORY,     win,                     1, MUIM_MainWin_ShowLowMemory);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_MEMORY,        win,                     1, MUIM_MainWin_ShowMemory);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_MOUNTS,        win,                     1, MUIM_MainWin_ShowMounts);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_PORTS,         win,                     1, MUIM_MainWin_ShowPorts);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_RESIDENTS,     win,                     1, MUIM_MainWin_ShowResidents);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_COMMANDS,      win,                     1, MUIM_MainWin_ShowCommands);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_RESOURCES,     win,                     1, MUIM_MainWin_ShowResources);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_SCREENMODES,   win,                     1, MUIM_MainWin_ShowScreenModes);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_SEMAPHORES,    win,                     1, MUIM_MainWin_ShowSemaphores);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_SYSTEM,        win,                     1, MUIM_MainWin_ShowSystem);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_TASKS,         win,                     1, MUIM_MainWin_ShowTasks);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_TIMERS,        win,                     1, MUIM_MainWin_ShowTimers);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_VECTORS,       win,                     1, MUIM_MainWin_ShowVectors);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_WINDOWS,       win,                     1, MUIM_MainWin_ShowWindows);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_PATCHES,       win,                     1, MUIM_MainWin_ShowPatches);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_CATALOGS,      win,                     1, MUIM_MainWin_ShowCatalogs);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_AUDIOMODES,    win,                     1, MUIM_MainWin_ShowAudioModes);

        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_FLUSHDEVS,     MUIV_Notify_Application, 2, MUIM_CallHook, &flushdevs_callhook);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_FLUSHFONTS,    MUIV_Notify_Application, 2, MUIM_CallHook, &flushfonts_callhook);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_FLUSHLIBS,     MUIV_Notify_Application, 2, MUIM_CallHook, &flushlibs_callhook);
        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_FLUSHALL,      MUIV_Notify_Application, 2, MUIM_CallHook, &flushall_callhook);

        DoMethod(app, MUIM_Notify, MUIA_Application_MenuAction, MENU_CLEARVECTORS,  MUIV_Notify_Application, 2, MUIM_CallHook, &clearvect_callhook);

        Forbid();
        pp = (struct PatchPort *)FindPort(PATCHPORT_NAME);
        sp = (struct SetManPort *)FindPort(SETMANPORT_NAME);
        Permit();

        menu = (APTR)DoMethod(menustrip, MUIM_FindUData, MENU_PATCHES);
        set(menu, MUIA_Menuitem_Enabled, (pp != NULL || sp != NULL));
    }

    return app;
}

